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