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