Commit | Line | Data |
---|---|---|
15637ed4 RG |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * 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 | |
78ed81a3 | 38 | /*static char sccsid[] = "from: @(#)input.c 5.4 (Berkeley) 7/1/91";*/ |
39 | static char rcsid[] = "input.c,v 1.4 1993/08/01 18:58:15 mycroft Exp"; | |
15637ed4 RG |
40 | #endif /* not lint */ |
41 | ||
42 | /* | |
43 | * This file implements the input routines used by the parser. | |
44 | */ | |
45 | ||
46 | #include <stdio.h> /* defines BUFSIZ */ | |
47 | #include "shell.h" | |
48 | #include <fcntl.h> | |
49 | #include <errno.h> | |
50 | #include "syntax.h" | |
51 | #include "input.h" | |
52 | #include "output.h" | |
53 | #include "memalloc.h" | |
54 | #include "error.h" | |
55 | ||
56 | #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ | |
57 | ||
58 | ||
59 | /* | |
60 | * The parsefile structure pointed to by the global variable parsefile | |
61 | * contains information about the current file being read. | |
62 | */ | |
63 | ||
64 | MKINIT | |
65 | struct parsefile { | |
66 | int linno; /* current line */ | |
67 | int fd; /* file descriptor (or -1 if string) */ | |
68 | int nleft; /* number of chars left in buffer */ | |
69 | char *nextc; /* next char in buffer */ | |
70 | struct parsefile *prev; /* preceding file on stack */ | |
71 | char *buf; /* input buffer */ | |
72 | }; | |
73 | ||
74 | ||
75 | int plinno = 1; /* input line number */ | |
76 | MKINIT int parsenleft; /* copy of parsefile->nleft */ | |
77 | char *parsenextc; /* copy of parsefile->nextc */ | |
78 | MKINIT struct parsefile basepf; /* top level input file */ | |
79 | char basebuf[BUFSIZ]; /* buffer for top level input file */ | |
80 | struct parsefile *parsefile = &basepf; /* current input file */ | |
81 | char *pushedstring; /* copy of parsenextc when text pushed back */ | |
82 | int pushednleft; /* copy of parsenleft when text pushed back */ | |
83 | ||
84 | #ifdef __STDC__ | |
85 | STATIC void pushfile(void); | |
86 | #else | |
87 | STATIC void pushfile(); | |
88 | #endif | |
89 | ||
90 | ||
91 | ||
92 | #ifdef mkinit | |
93 | INCLUDE "input.h" | |
94 | INCLUDE "error.h" | |
95 | ||
96 | INIT { | |
97 | extern char basebuf[]; | |
98 | ||
99 | basepf.nextc = basepf.buf = basebuf; | |
100 | } | |
101 | ||
102 | RESET { | |
103 | if (exception != EXSHELLPROC) | |
104 | parsenleft = 0; /* clear input buffer */ | |
105 | popallfiles(); | |
106 | } | |
107 | ||
108 | SHELLPROC { | |
109 | popallfiles(); | |
110 | } | |
111 | #endif | |
112 | ||
113 | ||
114 | /* | |
115 | * Read a line from the script. | |
116 | */ | |
117 | ||
118 | char * | |
119 | pfgets(line, len) | |
120 | char *line; | |
121 | { | |
122 | register char *p = line; | |
123 | int nleft = len; | |
124 | int c; | |
125 | ||
126 | while (--nleft > 0) { | |
127 | c = pgetc_macro(); | |
128 | if (c == PEOF) { | |
129 | if (p == line) | |
130 | return NULL; | |
131 | break; | |
132 | } | |
133 | *p++ = c; | |
134 | if (c == '\n') | |
135 | break; | |
136 | } | |
137 | *p = '\0'; | |
138 | return line; | |
139 | } | |
140 | ||
141 | ||
142 | ||
143 | /* | |
144 | * Read a character from the script, returning PEOF on end of file. | |
145 | * Nul characters in the input are silently discarded. | |
146 | */ | |
147 | ||
148 | int | |
149 | pgetc() { | |
150 | return pgetc_macro(); | |
151 | } | |
152 | ||
153 | ||
154 | /* | |
155 | * Refill the input buffer and return the next input character: | |
156 | * | |
157 | * 1) If a string was pushed back on the input, switch back to the regular | |
158 | * buffer. | |
159 | * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading | |
160 | * from a string so we can't refill the buffer, return EOF. | |
161 | * 3) Call read to read in the characters. | |
162 | * 4) Delete all nul characters from the buffer. | |
163 | */ | |
164 | ||
165 | int | |
166 | preadbuffer() { | |
167 | register char *p, *q; | |
168 | register int i; | |
169 | ||
170 | if (pushedstring) { | |
171 | parsenextc = pushedstring; | |
172 | pushedstring = NULL; | |
173 | parsenleft = pushednleft; | |
174 | if (--parsenleft >= 0) | |
175 | return *parsenextc++; | |
176 | } | |
177 | if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) | |
178 | return PEOF; | |
179 | flushout(&output); | |
180 | flushout(&errout); | |
181 | retry: | |
182 | p = parsenextc = parsefile->buf; | |
183 | i = read(parsefile->fd, p, BUFSIZ); | |
184 | if (i <= 0) { | |
185 | if (i < 0) { | |
186 | if (errno == EINTR) | |
187 | goto retry; | |
188 | if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | |
189 | int flags = fcntl(0, F_GETFL, 0); | |
190 | if (flags >= 0 && flags & O_NONBLOCK) { | |
191 | flags &=~ O_NONBLOCK; | |
192 | if (fcntl(0, F_SETFL, flags) >= 0) { | |
193 | out2str("sh: turning off NDELAY mode\n"); | |
194 | goto retry; | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | parsenleft = EOF_NLEFT; | |
200 | return PEOF; | |
201 | } | |
202 | parsenleft = i - 1; | |
203 | ||
204 | /* delete nul characters */ | |
205 | for (;;) { | |
206 | if (*p++ == '\0') | |
207 | break; | |
208 | if (--i <= 0) | |
209 | return *parsenextc++; /* no nul characters */ | |
210 | } | |
211 | q = p - 1; | |
212 | while (--i > 0) { | |
213 | if (*p != '\0') | |
214 | *q++ = *p; | |
215 | p++; | |
216 | } | |
217 | if (q == parsefile->buf) | |
218 | goto retry; /* buffer contained nothing but nuls */ | |
219 | parsenleft = q - parsefile->buf - 1; | |
220 | return *parsenextc++; | |
221 | } | |
222 | ||
223 | ||
224 | /* | |
225 | * Undo the last call to pgetc. Only one character may be pushed back. | |
226 | * PEOF may be pushed back. | |
227 | */ | |
228 | ||
229 | void | |
230 | pungetc() { | |
231 | parsenleft++; | |
232 | parsenextc--; | |
233 | } | |
234 | ||
235 | ||
236 | /* | |
237 | * Push a string back onto the input. This code doesn't work if the user | |
238 | * tries to push back more than one string at once. | |
239 | */ | |
240 | ||
241 | void | |
242 | ppushback(string, length) | |
243 | char *string; | |
244 | { | |
245 | pushedstring = parsenextc; | |
246 | pushednleft = parsenleft; | |
247 | parsenextc = string; | |
248 | parsenleft = length; | |
249 | } | |
250 | ||
251 | ||
252 | ||
253 | /* | |
254 | * Set the input to take input from a file. If push is set, push the | |
255 | * old input onto the stack first. | |
256 | */ | |
257 | ||
258 | void | |
259 | setinputfile(fname, push) | |
260 | char *fname; | |
261 | { | |
262 | int fd; | |
263 | int fd2; | |
264 | ||
265 | INTOFF; | |
266 | if ((fd = open(fname, O_RDONLY)) < 0) | |
267 | error("Can't open %s", fname); | |
268 | if (fd < 10) { | |
269 | fd2 = copyfd(fd, 10); | |
270 | close(fd); | |
271 | if (fd2 < 0) | |
272 | error("Out of file descriptors"); | |
273 | fd = fd2; | |
274 | } | |
275 | setinputfd(fd, push); | |
276 | INTON; | |
277 | } | |
278 | ||
279 | ||
280 | /* | |
281 | * Like setinputfile, but takes an open file descriptor. Call this with | |
282 | * interrupts off. | |
283 | */ | |
284 | ||
285 | void | |
286 | setinputfd(fd, push) { | |
287 | if (push) { | |
288 | pushfile(); | |
289 | parsefile->buf = ckmalloc(BUFSIZ); | |
290 | } | |
291 | if (parsefile->fd > 0) | |
292 | close(parsefile->fd); | |
293 | parsefile->fd = fd; | |
294 | if (parsefile->buf == NULL) | |
295 | parsefile->buf = ckmalloc(BUFSIZ); | |
296 | parsenleft = 0; | |
297 | plinno = 1; | |
298 | } | |
299 | ||
300 | ||
301 | /* | |
302 | * Like setinputfile, but takes input from a string. | |
303 | */ | |
304 | ||
305 | void | |
306 | setinputstring(string, push) | |
307 | char *string; | |
308 | { | |
309 | INTOFF; | |
310 | if (push) | |
311 | pushfile(); | |
312 | parsenextc = string; | |
313 | parsenleft = strlen(string); | |
314 | parsefile->buf = NULL; | |
315 | plinno = 1; | |
316 | INTON; | |
317 | } | |
318 | ||
319 | ||
320 | ||
321 | /* | |
322 | * To handle the "." command, a stack of input files is used. Pushfile | |
323 | * adds a new entry to the stack and popfile restores the previous level. | |
324 | */ | |
325 | ||
326 | STATIC void | |
327 | pushfile() { | |
328 | struct parsefile *pf; | |
329 | ||
330 | parsefile->nleft = parsenleft; | |
331 | parsefile->nextc = parsenextc; | |
332 | parsefile->linno = plinno; | |
333 | pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); | |
334 | pf->prev = parsefile; | |
335 | pf->fd = -1; | |
336 | parsefile = pf; | |
337 | } | |
338 | ||
339 | ||
340 | void | |
341 | popfile() { | |
342 | struct parsefile *pf = parsefile; | |
343 | ||
344 | INTOFF; | |
345 | if (pf->fd >= 0) | |
346 | close(pf->fd); | |
347 | if (pf->buf) | |
348 | ckfree(pf->buf); | |
349 | parsefile = pf->prev; | |
350 | ckfree(pf); | |
351 | parsenleft = parsefile->nleft; | |
352 | parsenextc = parsefile->nextc; | |
353 | plinno = parsefile->linno; | |
354 | INTON; | |
355 | } | |
356 | ||
357 | ||
358 | /* | |
359 | * Return to top level. | |
360 | */ | |
361 | ||
362 | void | |
363 | popallfiles() { | |
364 | while (parsefile != &basepf) | |
365 | popfile(); | |
366 | } | |
367 | ||
368 | ||
369 | ||
370 | /* | |
371 | * Close the file(s) that the shell is reading commands from. Called | |
372 | * after a fork is done. | |
373 | */ | |
374 | ||
375 | void | |
376 | closescript() { | |
377 | popallfiles(); | |
378 | if (parsefile->fd > 0) { | |
379 | close(parsefile->fd); | |
380 | parsefile->fd = 0; | |
381 | } | |
382 | } |