input.c revision 1.20 1 /* $NetBSD: input.c,v 1.20 1996/10/16 15:45:08 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95";
42 #else
43 static char rcsid[] = "$NetBSD: input.c,v 1.20 1996/10/16 15:45:08 christos Exp $";
44 #endif
45 #endif /* not lint */
46
47 #include <stdio.h> /* defines BUFSIZ */
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 /*
55 * This file implements the input routines used by the parser.
56 */
57
58 #include "shell.h"
59 #include "redir.h"
60 #include "syntax.h"
61 #include "input.h"
62 #include "output.h"
63 #include "options.h"
64 #include "memalloc.h"
65 #include "error.h"
66 #include "alias.h"
67 #include "parser.h"
68 #include "myhistedit.h"
69
70 #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */
71
72 MKINIT
73 struct strpush {
74 struct strpush *prev; /* preceding string on stack */
75 char *prevstring;
76 int prevnleft;
77 int prevlleft;
78 struct alias *ap; /* if push was associated with an alias */
79 };
80
81 /*
82 * The parsefile structure pointed to by the global variable parsefile
83 * contains information about the current file being read.
84 */
85
86 MKINIT
87 struct parsefile {
88 struct parsefile *prev; /* preceding file on stack */
89 int linno; /* current line */
90 int fd; /* file descriptor (or -1 if string) */
91 int nleft; /* number of chars left in this line */
92 int lleft; /* number of chars left in this buffer */
93 char *nextc; /* next char in buffer */
94 char *buf; /* input buffer */
95 struct strpush *strpush; /* for pushing strings at this level */
96 struct strpush basestrpush; /* so pushing one is fast */
97 };
98
99
100 int plinno = 1; /* input line number */
101 MKINIT int parsenleft; /* copy of parsefile->nleft */
102 MKINIT int parselleft; /* copy of parsefile->lleft */
103 char *parsenextc; /* copy of parsefile->nextc */
104 MKINIT struct parsefile basepf; /* top level input file */
105 char basebuf[BUFSIZ]; /* buffer for top level input file */
106 struct parsefile *parsefile = &basepf; /* current input file */
107 int init_editline = 0; /* editline library initialized? */
108 int whichprompt; /* 1 == PS1, 2 == PS2 */
109
110 EditLine *el; /* cookie for editline package */
111
112 STATIC void pushfile __P((void));
113 static int pread __P((void));
114
115 #ifdef mkinit
116 INCLUDE "input.h"
117 INCLUDE "error.h"
118
119 INIT {
120 extern char basebuf[];
121
122 basepf.nextc = basepf.buf = basebuf;
123 }
124
125 RESET {
126 if (exception != EXSHELLPROC)
127 parselleft = parsenleft = 0; /* clear input buffer */
128 popallfiles();
129 }
130
131 SHELLPROC {
132 popallfiles();
133 }
134 #endif
135
136
137 /*
138 * Read a line from the script.
139 */
140
141 char *
142 pfgets(line, len)
143 char *line;
144 int len;
145 {
146 register char *p = line;
147 int nleft = len;
148 int c;
149
150 while (--nleft > 0) {
151 c = pgetc_macro();
152 if (c == PEOF) {
153 if (p == line)
154 return NULL;
155 break;
156 }
157 *p++ = c;
158 if (c == '\n')
159 break;
160 }
161 *p = '\0';
162 return line;
163 }
164
165
166
167 /*
168 * Read a character from the script, returning PEOF on end of file.
169 * Nul characters in the input are silently discarded.
170 */
171
172 int
173 pgetc()
174 {
175 return pgetc_macro();
176 }
177
178
179 static int
180 pread()
181 {
182 int nr;
183 parsenextc = parsefile->buf;
184
185 retry:
186 if (parsefile->fd == 0 && el) {
187 const char *rl_cp;
188 int len;
189
190 rl_cp = el_gets(el, &nr);
191 if (rl_cp == NULL)
192 nr = 0;
193 else {
194 /* XXX - BUFSIZE should redesign so not necessary */
195 (void) strcpy(parsenextc, rl_cp);
196 }
197 } else {
198 nr = read(parsefile->fd, parsenextc, BUFSIZ - 1);
199 }
200
201 if (nr <= 0) {
202 if (nr < 0) {
203 if (errno == EINTR)
204 goto retry;
205 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
206 int flags = fcntl(0, F_GETFL, 0);
207 if (flags >= 0 && flags & O_NONBLOCK) {
208 flags &=~ O_NONBLOCK;
209 if (fcntl(0, F_SETFL, flags) >= 0) {
210 out2str("sh: turning off NDELAY mode\n");
211 goto retry;
212 }
213 }
214 }
215 }
216 nr = -1;
217 }
218 return nr;
219 }
220
221 /*
222 * Refill the input buffer and return the next input character:
223 *
224 * 1) If a string was pushed back on the input, pop it;
225 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
226 * from a string so we can't refill the buffer, return EOF.
227 * 3) If the is more stuff in this buffer, use it else call read to fill it.
228 * 4) Process input up to the next newline, deleting nul characters.
229 */
230
231 int
232 preadbuffer()
233 {
234 char *p, *q;
235 int more;
236 int something;
237 extern EditLine *el;
238 char savec;
239
240 if (parsefile->strpush) {
241 popstring();
242 if (--parsenleft >= 0)
243 return (*parsenextc++);
244 }
245 if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
246 return PEOF;
247 flushout(&output);
248 flushout(&errout);
249
250 again:
251 if (parselleft <= 0) {
252 if ((parselleft = pread()) == -1) {
253 parselleft = parsenleft = EOF_NLEFT;
254 return PEOF;
255 }
256 }
257
258 q = p = parsenextc;
259
260 /* delete nul characters */
261 something = 0;
262 for (more = 1; more;) {
263 switch (*p) {
264 case '\0':
265 p++; /* Skip nul */
266 goto check;
267
268 case '\t':
269 case ' ':
270 break;
271
272 case '\n':
273 parsenleft = q - parsenextc;
274 more = 0; /* Stop processing here */
275 break;
276
277 default:
278 something = 1;
279 break;
280 }
281
282 *q++ = *p++;
283 check:
284 if (--parselleft <= 0) {
285 parsenleft = q - parsenextc - 1;
286 if (parsenleft < 0)
287 goto again;
288 *q = '\0';
289 more = 0;
290 }
291 }
292
293 savec = *q;
294 *q = '\0';
295
296 #ifndef NO_HISTORY
297 if (parsefile->fd == 0 && hist && something) {
298 INTOFF;
299 history(hist, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc);
300 INTON;
301 }
302 #endif
303
304 if (vflag) {
305 out2str(parsenextc);
306 flushout(out2);
307 }
308
309 *q = savec;
310
311 return *parsenextc++;
312 }
313
314 /*
315 * Undo the last call to pgetc. Only one character may be pushed back.
316 * PEOF may be pushed back.
317 */
318
319 void
320 pungetc() {
321 parsenleft++;
322 parsenextc--;
323 }
324
325 /*
326 * Push a string back onto the input at this current parsefile level.
327 * We handle aliases this way.
328 */
329 void
330 pushstring(s, len, ap)
331 char *s;
332 int len;
333 void *ap;
334 {
335 struct strpush *sp;
336
337 INTOFF;
338 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
339 if (parsefile->strpush) {
340 sp = ckmalloc(sizeof (struct strpush));
341 sp->prev = parsefile->strpush;
342 parsefile->strpush = sp;
343 } else
344 sp = parsefile->strpush = &(parsefile->basestrpush);
345 sp->prevstring = parsenextc;
346 sp->prevnleft = parsenleft;
347 sp->prevlleft = parselleft;
348 sp->ap = (struct alias *)ap;
349 if (ap)
350 ((struct alias *)ap)->flag |= ALIASINUSE;
351 parsenextc = s;
352 parsenleft = len;
353 INTON;
354 }
355
356 void
357 popstring()
358 {
359 struct strpush *sp = parsefile->strpush;
360
361 INTOFF;
362 parsenextc = sp->prevstring;
363 parsenleft = sp->prevnleft;
364 parselleft = sp->prevlleft;
365 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
366 if (sp->ap)
367 sp->ap->flag &= ~ALIASINUSE;
368 parsefile->strpush = sp->prev;
369 if (sp != &(parsefile->basestrpush))
370 ckfree(sp);
371 INTON;
372 }
373
374 /*
375 * Set the input to take input from a file. If push is set, push the
376 * old input onto the stack first.
377 */
378
379 void
380 setinputfile(fname, push)
381 char *fname;
382 int push;
383 {
384 int fd;
385 int fd2;
386
387 INTOFF;
388 if ((fd = open(fname, O_RDONLY)) < 0)
389 error("Can't open %s", fname);
390 if (fd < 10) {
391 fd2 = copyfd(fd, 10);
392 close(fd);
393 if (fd2 < 0)
394 error("Out of file descriptors");
395 fd = fd2;
396 }
397 setinputfd(fd, push);
398 INTON;
399 }
400
401
402 /*
403 * Like setinputfile, but takes an open file descriptor. Call this with
404 * interrupts off.
405 */
406
407 void
408 setinputfd(fd, push)
409 int fd, push;
410 {
411 if (push) {
412 pushfile();
413 parsefile->buf = ckmalloc(BUFSIZ);
414 }
415 if (parsefile->fd > 0)
416 close(parsefile->fd);
417 parsefile->fd = fd;
418 if (parsefile->buf == NULL)
419 parsefile->buf = ckmalloc(BUFSIZ);
420 parselleft = parsenleft = 0;
421 plinno = 1;
422 }
423
424
425 /*
426 * Like setinputfile, but takes input from a string.
427 */
428
429 void
430 setinputstring(string, push)
431 char *string;
432 int push;
433 {
434 INTOFF;
435 if (push)
436 pushfile();
437 parsenextc = string;
438 parselleft = parsenleft = strlen(string);
439 parsefile->buf = NULL;
440 plinno = 1;
441 INTON;
442 }
443
444
445
446 /*
447 * To handle the "." command, a stack of input files is used. Pushfile
448 * adds a new entry to the stack and popfile restores the previous level.
449 */
450
451 STATIC void
452 pushfile() {
453 struct parsefile *pf;
454
455 parsefile->nleft = parsenleft;
456 parsefile->lleft = parselleft;
457 parsefile->nextc = parsenextc;
458 parsefile->linno = plinno;
459 pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
460 pf->prev = parsefile;
461 pf->fd = -1;
462 pf->strpush = NULL;
463 pf->basestrpush.prev = NULL;
464 parsefile = pf;
465 }
466
467
468 void
469 popfile() {
470 struct parsefile *pf = parsefile;
471
472 INTOFF;
473 if (pf->fd >= 0)
474 close(pf->fd);
475 if (pf->buf)
476 ckfree(pf->buf);
477 while (pf->strpush)
478 popstring();
479 parsefile = pf->prev;
480 ckfree(pf);
481 parsenleft = parsefile->nleft;
482 parselleft = parsefile->lleft;
483 parsenextc = parsefile->nextc;
484 plinno = parsefile->linno;
485 INTON;
486 }
487
488
489 /*
490 * Return to top level.
491 */
492
493 void
494 popallfiles() {
495 while (parsefile != &basepf)
496 popfile();
497 }
498
499
500
501 /*
502 * Close the file(s) that the shell is reading commands from. Called
503 * after a fork is done.
504 */
505
506 void
507 closescript() {
508 popallfiles();
509 if (parsefile->fd > 0) {
510 close(parsefile->fd);
511 parsefile->fd = 0;
512 }
513 }
514