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