input.c revision 1.2 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 static char rcsid[] = "$Id: input.c,v 1.2 1993/03/22 08:04:00 cgd Exp $";
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 }
383