xref: /original-bsd/bin/sh/input.c (revision 84fd512e)
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.4 (Berkeley) 07/01/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 & O_NONBLOCK) {
164                                         flags &=~ O_NONBLOCK;
165                                         if (fcntl(0, F_SETFL, flags) >= 0) {
166 						out2str("sh: turning off NDELAY mode\n");
167                                                 goto retry;
168                                         }
169                                 }
170                         }
171                 }
172                 parsenleft = EOF_NLEFT;
173                 return PEOF;
174 	}
175 	parsenleft = i - 1;
176 
177 	/* delete nul characters */
178 	for (;;) {
179 		if (*p++ == '\0')
180 			break;
181 		if (--i <= 0)
182 			return *parsenextc++;		/* no nul characters */
183 	}
184 	q = p - 1;
185 	while (--i > 0) {
186 		if (*p != '\0')
187 			*q++ = *p;
188 		p++;
189 	}
190 	if (q == parsefile->buf)
191 		goto retry;			/* buffer contained nothing but nuls */
192 	parsenleft = q - parsefile->buf - 1;
193 	return *parsenextc++;
194 }
195 
196 
197 /*
198  * Undo the last call to pgetc.  Only one character may be pushed back.
199  * PEOF may be pushed back.
200  */
201 
202 void
203 pungetc() {
204 	parsenleft++;
205 	parsenextc--;
206 }
207 
208 
209 /*
210  * Push a string back onto the input.  This code doesn't work if the user
211  * tries to push back more than one string at once.
212  */
213 
214 void
215 ppushback(string, length)
216 	char *string;
217 	{
218 	pushedstring = parsenextc;
219 	pushednleft = parsenleft;
220 	parsenextc = string;
221 	parsenleft = length;
222 }
223 
224 
225 
226 /*
227  * Set the input to take input from a file.  If push is set, push the
228  * old input onto the stack first.
229  */
230 
231 void
232 setinputfile(fname, push)
233 	char *fname;
234 	{
235 	int fd;
236 	int fd2;
237 
238 	INTOFF;
239 	if ((fd = open(fname, O_RDONLY)) < 0)
240 		error("Can't open %s", fname);
241 	if (fd < 10) {
242 		fd2 = copyfd(fd, 10);
243 		close(fd);
244 		if (fd2 < 0)
245 			error("Out of file descriptors");
246 		fd = fd2;
247 	}
248 	setinputfd(fd, push);
249 	INTON;
250 }
251 
252 
253 /*
254  * Like setinputfile, but takes an open file descriptor.  Call this with
255  * interrupts off.
256  */
257 
258 void
259 setinputfd(fd, push) {
260 	if (push) {
261 		pushfile();
262 		parsefile->buf = ckmalloc(BUFSIZ);
263 	}
264 	if (parsefile->fd > 0)
265 		close(parsefile->fd);
266 	parsefile->fd = fd;
267 	if (parsefile->buf == NULL)
268 		parsefile->buf = ckmalloc(BUFSIZ);
269 	parsenleft = 0;
270 	plinno = 1;
271 }
272 
273 
274 /*
275  * Like setinputfile, but takes input from a string.
276  */
277 
278 void
279 setinputstring(string, push)
280 	char *string;
281 	{
282 	INTOFF;
283 	if (push)
284 		pushfile();
285 	parsenextc = string;
286 	parsenleft = strlen(string);
287 	parsefile->buf = NULL;
288 	plinno = 1;
289 	INTON;
290 }
291 
292 
293 
294 /*
295  * To handle the "." command, a stack of input files is used.  Pushfile
296  * adds a new entry to the stack and popfile restores the previous level.
297  */
298 
299 STATIC void
300 pushfile() {
301 	struct parsefile *pf;
302 
303 	parsefile->nleft = parsenleft;
304 	parsefile->nextc = parsenextc;
305 	parsefile->linno = plinno;
306 	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
307 	pf->prev = parsefile;
308 	pf->fd = -1;
309 	parsefile = pf;
310 }
311 
312 
313 void
314 popfile() {
315 	struct parsefile *pf = parsefile;
316 
317 	INTOFF;
318 	if (pf->fd >= 0)
319 		close(pf->fd);
320 	if (pf->buf)
321 		ckfree(pf->buf);
322 	parsefile = pf->prev;
323 	ckfree(pf);
324 	parsenleft = parsefile->nleft;
325 	parsenextc = parsefile->nextc;
326 	plinno = parsefile->linno;
327 	INTON;
328 }
329 
330 
331 /*
332  * Return to top level.
333  */
334 
335 void
336 popallfiles() {
337 	while (parsefile != &basepf)
338 		popfile();
339 }
340 
341 
342 
343 /*
344  * Close the file(s) that the shell is reading commands from.  Called
345  * after a fork is done.
346  */
347 
348 void
349 closescript() {
350 	popallfiles();
351 	if (parsefile->fd > 0) {
352 		close(parsefile->fd);
353 		parsefile->fd = 0;
354 	}
355 }
356