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