Rev 2714 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
2714 | cejka | 1 | /* $NetBSD: input.c,v 1.34 2000/05/22 10:18:47 elric 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 | #include <sys/cdefs.h> |
||
40 | #ifndef lint |
||
41 | #if 0 |
||
42 | static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; |
||
43 | #else |
||
44 | __RCSID("$NetBSD: input.c,v 1.34 2000/05/22 10:18:47 elric Exp $"); |
||
45 | #endif |
||
46 | #endif /* not lint */ |
||
47 | |||
48 | #include <stdio.h> /* defines BUFSIZ */ |
||
49 | #include <fcntl.h> |
||
50 | #include <errno.h> |
||
51 | #include <unistd.h> |
||
52 | #include <stdlib.h> |
||
53 | #include <string.h> |
||
54 | |||
55 | /* |
||
56 | * This file implements the input routines used by the parser. |
||
57 | */ |
||
58 | |||
59 | #include "shell.h" |
||
60 | #include "redir.h" |
||
61 | #include "syntax.h" |
||
62 | #include "input.h" |
||
63 | #include "output.h" |
||
64 | #include "options.h" |
||
65 | #include "memalloc.h" |
||
66 | #include "error.h" |
||
67 | #include "alias.h" |
||
68 | #include "parser.h" |
||
69 | #ifndef SMALL |
||
70 | #include "myhistedit.h" |
||
71 | #endif |
||
72 | |||
73 | #ifdef HETIO |
||
74 | #include "hetio.h" |
||
75 | #endif |
||
76 | |||
77 | #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ |
||
78 | |||
79 | MKINIT |
||
80 | struct strpush { |
||
81 | struct strpush *prev; /* preceding string on stack */ |
||
82 | char *prevstring; |
||
83 | int prevnleft; |
||
84 | int prevlleft; |
||
85 | struct alias *ap; /* if push was associated with an alias */ |
||
86 | }; |
||
87 | |||
88 | /* |
||
89 | * The parsefile structure pointed to by the global variable parsefile |
||
90 | * contains information about the current file being read. |
||
91 | */ |
||
92 | |||
93 | MKINIT |
||
94 | struct parsefile { |
||
95 | struct parsefile *prev; /* preceding file on stack */ |
||
96 | int linno; /* current line */ |
||
97 | int fd; /* file descriptor (or -1 if string) */ |
||
98 | int nleft; /* number of chars left in this line */ |
||
99 | int lleft; /* number of chars left in this buffer */ |
||
100 | char *nextc; /* next char in buffer */ |
||
101 | char *buf; /* input buffer */ |
||
102 | struct strpush *strpush; /* for pushing strings at this level */ |
||
103 | struct strpush basestrpush; /* so pushing one is fast */ |
||
104 | }; |
||
105 | |||
106 | |||
107 | int plinno = 1; /* input line number */ |
||
108 | MKINIT int parsenleft; /* copy of parsefile->nleft */ |
||
109 | MKINIT int parselleft; /* copy of parsefile->lleft */ |
||
110 | char *parsenextc; /* copy of parsefile->nextc */ |
||
111 | MKINIT struct parsefile basepf; /* top level input file */ |
||
112 | char basebuf[BUFSIZ]; /* buffer for top level input file */ |
||
113 | struct parsefile *parsefile = &basepf; /* current input file */ |
||
114 | int init_editline = 0; /* editline library initialized? */ |
||
115 | int whichprompt; /* 1 == PS1, 2 == PS2 */ |
||
116 | |||
117 | #ifndef SMALL |
||
118 | EditLine *el; /* cookie for editline package */ |
||
119 | #endif |
||
120 | |||
121 | STATIC void pushfile (void); |
||
122 | static int preadfd (void); |
||
123 | |||
124 | #ifdef mkinit |
||
125 | INCLUDE "input.h" |
||
126 | INCLUDE "error.h" |
||
127 | |||
128 | INIT { |
||
129 | extern char basebuf[]; |
||
130 | |||
131 | basepf.nextc = basepf.buf = basebuf; |
||
132 | } |
||
133 | |||
134 | RESET { |
||
135 | if (exception != EXSHELLPROC) |
||
136 | parselleft = parsenleft = 0; /* clear input buffer */ |
||
137 | popallfiles(); |
||
138 | } |
||
139 | |||
140 | SHELLPROC { |
||
141 | popallfiles(); |
||
142 | } |
||
143 | #endif |
||
144 | |||
145 | |||
146 | /* |
||
147 | * Read a line from the script. |
||
148 | */ |
||
149 | |||
150 | char * |
||
151 | pfgets(line, len) |
||
152 | char *line; |
||
153 | int len; |
||
154 | { |
||
155 | char *p = line; |
||
156 | int nleft = len; |
||
157 | int c; |
||
158 | |||
159 | while (--nleft > 0) { |
||
160 | c = pgetc_macro(); |
||
161 | if (c == PEOF) { |
||
162 | if (p == line) |
||
163 | return NULL; |
||
164 | break; |
||
165 | } |
||
166 | *p++ = c; |
||
167 | if (c == '\n') |
||
168 | break; |
||
169 | } |
||
170 | *p = '\0'; |
||
171 | return line; |
||
172 | } |
||
173 | |||
174 | |||
175 | |||
176 | /* |
||
177 | * Read a character from the script, returning PEOF on end of file. |
||
178 | * Nul characters in the input are silently discarded. |
||
179 | */ |
||
180 | |||
181 | int |
||
182 | pgetc() |
||
183 | { |
||
184 | return pgetc_macro(); |
||
185 | } |
||
186 | |||
187 | |||
188 | static int |
||
189 | preadfd() |
||
190 | { |
||
191 | int nr; |
||
192 | char *buf = parsefile->buf; |
||
193 | parsenextc = buf; |
||
194 | |||
195 | retry: |
||
196 | #ifndef SMALL |
||
197 | if (parsefile->fd == 0 && el) { |
||
198 | const char *rl_cp; |
||
199 | |||
200 | rl_cp = el_gets(el, &nr); |
||
201 | if (rl_cp == NULL) |
||
202 | nr = 0; |
||
203 | else { |
||
204 | /* XXX - BUFSIZE should redesign so not necessary */ |
||
205 | (void) strcpy(buf, rl_cp); |
||
206 | } |
||
207 | } else |
||
208 | #endif |
||
209 | |||
210 | #ifdef HETIO |
||
211 | nr = hetio_read_input(parsefile->fd); |
||
212 | if (nr == -255) |
||
213 | #endif |
||
214 | nr = read(parsefile->fd, buf, BUFSIZ - 1); |
||
215 | |||
216 | |||
217 | if (nr <= 0) { |
||
218 | if (nr < 0) { |
||
219 | if (errno == EINTR) |
||
220 | goto retry; |
||
221 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { |
||
222 | int flags = fcntl(0, F_GETFL, 0); |
||
223 | if (flags >= 0 && flags & O_NONBLOCK) { |
||
224 | flags &=~ O_NONBLOCK; |
||
225 | if (fcntl(0, F_SETFL, flags) >= 0) { |
||
226 | out2str("sh: turning off NDELAY mode\n"); |
||
227 | goto retry; |
||
228 | } |
||
229 | } |
||
230 | } |
||
231 | } |
||
232 | nr = -1; |
||
233 | } |
||
234 | return nr; |
||
235 | } |
||
236 | |||
237 | /* |
||
238 | * Refill the input buffer and return the next input character: |
||
239 | * |
||
240 | * 1) If a string was pushed back on the input, pop it; |
||
241 | * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading |
||
242 | * from a string so we can't refill the buffer, return EOF. |
||
243 | * 3) If the is more stuff in this buffer, use it else call read to fill it. |
||
244 | * 4) Process input up to the next newline, deleting nul characters. |
||
245 | */ |
||
246 | |||
247 | int |
||
248 | preadbuffer() |
||
249 | { |
||
250 | char *p, *q; |
||
251 | int more; |
||
252 | int something; |
||
253 | char savec; |
||
254 | |||
255 | if (parsefile->strpush) { |
||
256 | popstring(); |
||
257 | if (--parsenleft >= 0) |
||
258 | return (*parsenextc++); |
||
259 | } |
||
260 | if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) |
||
261 | return PEOF; |
||
262 | flushout(&output); |
||
263 | flushout(&errout); |
||
264 | |||
265 | again: |
||
266 | if (parselleft <= 0) { |
||
267 | if ((parselleft = preadfd()) == -1) { |
||
268 | parselleft = parsenleft = EOF_NLEFT; |
||
269 | return PEOF; |
||
270 | } |
||
271 | } |
||
272 | |||
273 | q = p = parsenextc; |
||
274 | |||
275 | /* delete nul characters */ |
||
276 | something = 0; |
||
277 | for (more = 1; more;) { |
||
278 | switch (*p) { |
||
279 | case '\0': |
||
280 | p++; /* Skip nul */ |
||
281 | goto check; |
||
282 | |||
283 | case '\t': |
||
284 | case ' ': |
||
285 | break; |
||
286 | |||
287 | case '\n': |
||
288 | parsenleft = q - parsenextc; |
||
289 | more = 0; /* Stop processing here */ |
||
290 | break; |
||
291 | |||
292 | default: |
||
293 | something = 1; |
||
294 | break; |
||
295 | } |
||
296 | |||
297 | *q++ = *p++; |
||
298 | check: |
||
299 | if (--parselleft <= 0) { |
||
300 | parsenleft = q - parsenextc - 1; |
||
301 | if (parsenleft < 0) |
||
302 | goto again; |
||
303 | *q = '\0'; |
||
304 | more = 0; |
||
305 | } |
||
306 | } |
||
307 | |||
308 | savec = *q; |
||
309 | *q = '\0'; |
||
310 | |||
311 | #ifndef SMALL |
||
312 | if (parsefile->fd == 0 && hist && something) { |
||
313 | HistEvent he; |
||
314 | INTOFF; |
||
315 | history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, |
||
316 | parsenextc); |
||
317 | INTON; |
||
318 | } |
||
319 | #endif |
||
320 | |||
321 | if (vflag) { |
||
322 | out2str(parsenextc); |
||
323 | flushout(out2); |
||
324 | } |
||
325 | |||
326 | *q = savec; |
||
327 | |||
328 | return *parsenextc++; |
||
329 | } |
||
330 | |||
331 | /* |
||
332 | * Undo the last call to pgetc. Only one character may be pushed back. |
||
333 | * PEOF may be pushed back. |
||
334 | */ |
||
335 | |||
336 | void |
||
337 | pungetc() { |
||
338 | parsenleft++; |
||
339 | parsenextc--; |
||
340 | } |
||
341 | |||
342 | /* |
||
343 | * Push a string back onto the input at this current parsefile level. |
||
344 | * We handle aliases this way. |
||
345 | */ |
||
346 | void |
||
347 | pushstring(s, len, ap) |
||
348 | char *s; |
||
349 | int len; |
||
350 | void *ap; |
||
351 | { |
||
352 | struct strpush *sp; |
||
353 | |||
354 | INTOFF; |
||
355 | /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ |
||
356 | if (parsefile->strpush) { |
||
357 | sp = ckmalloc(sizeof (struct strpush)); |
||
358 | sp->prev = parsefile->strpush; |
||
359 | parsefile->strpush = sp; |
||
360 | } else |
||
361 | sp = parsefile->strpush = &(parsefile->basestrpush); |
||
362 | sp->prevstring = parsenextc; |
||
363 | sp->prevnleft = parsenleft; |
||
364 | sp->prevlleft = parselleft; |
||
365 | sp->ap = (struct alias *)ap; |
||
366 | if (ap) |
||
367 | ((struct alias *)ap)->flag |= ALIASINUSE; |
||
368 | parsenextc = s; |
||
369 | parsenleft = len; |
||
370 | INTON; |
||
371 | } |
||
372 | |||
373 | void |
||
374 | popstring() |
||
375 | { |
||
376 | struct strpush *sp = parsefile->strpush; |
||
377 | |||
378 | INTOFF; |
||
379 | parsenextc = sp->prevstring; |
||
380 | parsenleft = sp->prevnleft; |
||
381 | parselleft = sp->prevlleft; |
||
382 | /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ |
||
383 | if (sp->ap) |
||
384 | sp->ap->flag &= ~ALIASINUSE; |
||
385 | parsefile->strpush = sp->prev; |
||
386 | if (sp != &(parsefile->basestrpush)) |
||
387 | ckfree(sp); |
||
388 | INTON; |
||
389 | } |
||
390 | |||
391 | /* |
||
392 | * Set the input to take input from a file. If push is set, push the |
||
393 | * old input onto the stack first. |
||
394 | */ |
||
395 | |||
396 | void |
||
397 | setinputfile(fname, push) |
||
398 | const char *fname; |
||
399 | int push; |
||
400 | { |
||
401 | int fd; |
||
402 | int fd2; |
||
403 | |||
404 | INTOFF; |
||
405 | if ((fd = open(fname, O_RDONLY)) < 0) |
||
406 | error("Can't open %s", fname); |
||
407 | if (fd < 10) { |
||
408 | fd2 = copyfd(fd, 10); |
||
409 | close(fd); |
||
410 | if (fd2 < 0) |
||
411 | error("Out of file descriptors"); |
||
412 | fd = fd2; |
||
413 | } |
||
414 | setinputfd(fd, push); |
||
415 | INTON; |
||
416 | } |
||
417 | |||
418 | |||
419 | /* |
||
420 | * Like setinputfile, but takes an open file descriptor. Call this with |
||
421 | * interrupts off. |
||
422 | */ |
||
423 | |||
424 | void |
||
425 | setinputfd(fd, push) |
||
426 | int fd, push; |
||
427 | { |
||
428 | (void) fcntl(fd, F_SETFD, FD_CLOEXEC); |
||
429 | if (push) { |
||
430 | pushfile(); |
||
431 | parsefile->buf = ckmalloc(BUFSIZ); |
||
432 | } |
||
433 | if (parsefile->fd > 0) |
||
434 | close(parsefile->fd); |
||
435 | parsefile->fd = fd; |
||
436 | if (parsefile->buf == NULL) |
||
437 | parsefile->buf = ckmalloc(BUFSIZ); |
||
438 | parselleft = parsenleft = 0; |
||
439 | plinno = 1; |
||
440 | } |
||
441 | |||
442 | |||
443 | /* |
||
444 | * Like setinputfile, but takes input from a string. |
||
445 | */ |
||
446 | |||
447 | void |
||
448 | setinputstring(string, push) |
||
449 | char *string; |
||
450 | int push; |
||
451 | { |
||
452 | INTOFF; |
||
453 | if (push) |
||
454 | pushfile(); |
||
455 | parsenextc = string; |
||
456 | parselleft = parsenleft = strlen(string); |
||
457 | parsefile->buf = NULL; |
||
458 | plinno = 1; |
||
459 | INTON; |
||
460 | } |
||
461 | |||
462 | |||
463 | |||
464 | /* |
||
465 | * To handle the "." command, a stack of input files is used. Pushfile |
||
466 | * adds a new entry to the stack and popfile restores the previous level. |
||
467 | */ |
||
468 | |||
469 | STATIC void |
||
470 | pushfile() { |
||
471 | struct parsefile *pf; |
||
472 | |||
473 | parsefile->nleft = parsenleft; |
||
474 | parsefile->lleft = parselleft; |
||
475 | parsefile->nextc = parsenextc; |
||
476 | parsefile->linno = plinno; |
||
477 | pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); |
||
478 | pf->prev = parsefile; |
||
479 | pf->fd = -1; |
||
480 | pf->strpush = NULL; |
||
481 | pf->basestrpush.prev = NULL; |
||
482 | parsefile = pf; |
||
483 | } |
||
484 | |||
485 | |||
486 | void |
||
487 | popfile() { |
||
488 | struct parsefile *pf = parsefile; |
||
489 | |||
490 | INTOFF; |
||
491 | if (pf->fd >= 0) |
||
492 | close(pf->fd); |
||
493 | if (pf->buf) |
||
494 | ckfree(pf->buf); |
||
495 | while (pf->strpush) |
||
496 | popstring(); |
||
497 | parsefile = pf->prev; |
||
498 | ckfree(pf); |
||
499 | parsenleft = parsefile->nleft; |
||
500 | parselleft = parsefile->lleft; |
||
501 | parsenextc = parsefile->nextc; |
||
502 | plinno = parsefile->linno; |
||
503 | INTON; |
||
504 | } |
||
505 | |||
506 | |||
507 | /* |
||
508 | * Return to top level. |
||
509 | */ |
||
510 | |||
511 | void |
||
512 | popallfiles() { |
||
513 | while (parsefile != &basepf) |
||
514 | popfile(); |
||
515 | } |
||
516 | |||
517 | |||
518 | |||
519 | /* |
||
520 | * Close the file(s) that the shell is reading commands from. Called |
||
521 | * after a fork is done. |
||
522 | */ |
||
523 | |||
524 | void |
||
525 | closescript() { |
||
526 | popallfiles(); |
||
527 | if (parsefile->fd > 0) { |
||
528 | close(parsefile->fd); |
||
529 | parsefile->fd = 0; |
||
530 | } |
||
531 | } |