xref: /original-bsd/bin/sh/input.c (revision ea3a8ee8)
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.8 (Berkeley) 07/15/92";
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 "options.h"
27 #include "memalloc.h"
28 #include "error.h"
29 #include "alias.h"
30 #include "parser.h"
31 #include "myhistedit.h"
32 
33 #define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
34 
35 MKINIT
36 struct strpush {
37 	struct strpush *prev;	/* preceding string on stack */
38 	char *prevstring;
39 	int prevnleft;
40 	struct alias *ap;	/* if push was associated with an alias */
41 };
42 
43 /*
44  * The parsefile structure pointed to by the global variable parsefile
45  * contains information about the current file being read.
46  */
47 
48 MKINIT
49 struct parsefile {
50 	struct parsefile *prev;	/* preceding file on stack */
51 	int linno;		/* current line */
52 	int fd;			/* file descriptor (or -1 if string) */
53 	int nleft;		/* number of chars left in buffer */
54 	char *nextc;		/* next char in buffer */
55 	char *buf;		/* input buffer */
56 	struct strpush *strpush; /* for pushing strings at this level */
57 	struct strpush basestrpush; /* so pushing one is fast */
58 };
59 
60 
61 int plinno = 1;			/* input line number */
62 MKINIT int parsenleft;		/* copy of parsefile->nleft */
63 char *parsenextc;		/* copy of parsefile->nextc */
64 MKINIT struct parsefile basepf;	/* top level input file */
65 char basebuf[BUFSIZ];		/* buffer for top level input file */
66 struct parsefile *parsefile = &basepf;	/* current input file */
67 char *pushedstring;		/* copy of parsenextc when text pushed back */
68 int pushednleft;		/* copy of parsenleft when text pushed back */
69 int init_editline = 0;		/* editline library initialized? */
70 int whichprompt;		/* 1 == PS1, 2 == PS2 */
71 
72 EditLine *el;			/* cookie for editline package */
73 
74 #ifdef __STDC__
75 STATIC void pushfile(void);
76 #else
77 STATIC void pushfile();
78 #endif
79 
80 
81 
82 #ifdef mkinit
83 INCLUDE "input.h"
84 INCLUDE "error.h"
85 
86 INIT {
87 	extern char basebuf[];
88 
89 	basepf.nextc = basepf.buf = basebuf;
90 }
91 
92 RESET {
93 	if (exception != EXSHELLPROC)
94 		parsenleft = 0;            /* clear input buffer */
95 	popallfiles();
96 }
97 
98 SHELLPROC {
99 	popallfiles();
100 }
101 #endif
102 
103 
104 /*
105  * Read a line from the script.
106  */
107 
108 char *
109 pfgets(line, len)
110 	char *line;
111 	{
112 	register char *p = line;
113 	int nleft = len;
114 	int c;
115 
116 	while (--nleft > 0) {
117 		c = pgetc_macro();
118 		if (c == PEOF) {
119 			if (p == line)
120 				return NULL;
121 			break;
122 		}
123 		*p++ = c;
124 		if (c == '\n')
125 			break;
126 	}
127 	*p = '\0';
128 	return line;
129 }
130 
131 
132 
133 /*
134  * Read a character from the script, returning PEOF on end of file.
135  * Nul characters in the input are silently discarded.
136  */
137 
138 int
139 pgetc() {
140 	return pgetc_macro();
141 }
142 
143 
144 /*
145  * Refill the input buffer and return the next input character:
146  *
147  * 1) If a string was pushed back on the input, pop it;
148  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
149  *    from a string so we can't refill the buffer, return EOF.
150  * 3) Call read to read in the characters.
151  * 4) Delete all nul characters from the buffer.
152  */
153 
154 int
155 preadbuffer() {
156 	register char *p, *q;
157 	register int i;
158 	register int something;
159 	extern EditLine *el;
160 
161 	if (parsefile->strpush) {
162 		popstring();
163 		if (--parsenleft >= 0)
164 			return (*parsenextc++);
165 	}
166 	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
167 		return PEOF;
168 	flushout(&output);
169 	flushout(&errout);
170 retry:
171 	p = parsenextc = parsefile->buf;
172 	if (parsefile->fd == 0 && el) {
173 		const char *rl_cp;
174 		int len;
175 
176 		rl_cp = el_gets(el, &len);
177 		if (rl_cp == NULL) {
178 			i = 0;
179 			goto eof;
180 		}
181 		strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
182 		i = len;
183 
184 	} else {
185 regular_read:
186 		i = read(parsefile->fd, p, BUFSIZ - 1);
187 	}
188 eof:
189 	if (i <= 0) {
190                 if (i < 0) {
191                         if (errno == EINTR)
192                                 goto retry;
193                         if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
194                                 int flags = fcntl(0, F_GETFL, 0);
195                                 if (flags >= 0 && flags & O_NONBLOCK) {
196                                         flags &=~ O_NONBLOCK;
197                                         if (fcntl(0, F_SETFL, flags) >= 0) {
198 						out2str("sh: turning off NDELAY mode\n");
199                                                 goto retry;
200                                         }
201                                 }
202                         }
203                 }
204                 parsenleft = EOF_NLEFT;
205                 return PEOF;
206 	}
207 	parsenleft = i - 1;	/* we're returning one char in this call */
208 
209 	/* delete nul characters */
210 	something = 0;
211 	for (;;) {
212 		if (*p == '\0')
213 			break;
214 		if (*p != ' ' && *p != '\t' && *p != '\n')
215 			something = 1;
216 		p++;
217 		if (--i <= 0) {
218 			*p = '\0';
219 			goto done;		/* no nul characters */
220 		}
221 	}
222 	/*
223 	 * remove nuls
224 	 */
225 	q = p++;
226 	while (--i > 0) {
227 		if (*p != '\0')
228 			*q++ = *p;
229 		p++;
230 	}
231 	*q = '\0';
232 	if (q == parsefile->buf)
233 		goto retry;			/* buffer contained nothing but nuls */
234 	parsenleft = q - parsefile->buf - 1;
235 
236 done:
237 	if (parsefile->fd == 0 && hist && something) {
238 		INTOFF;
239 		history(hist, whichprompt == 1 ? H_ENTER : H_ADD,
240 			   parsefile->buf);
241 		INTON;
242 	}
243 	if (vflag) {
244 		/*
245 		 * This isn't right.  Most shells coordinate it with
246 		 * reading a line at a time.  I honestly don't know if its
247 		 * worth it.
248 		 */
249 		i = parsenleft + 1;
250 		p = parsefile->buf;
251 		for (; i--; p++)
252 			out2c(*p)
253 		flushout(out2);
254 	}
255 	return *parsenextc++;
256 }
257 
258 /*
259  * Undo the last call to pgetc.  Only one character may be pushed back.
260  * PEOF may be pushed back.
261  */
262 
263 void
264 pungetc() {
265 	parsenleft++;
266 	parsenextc--;
267 }
268 
269 /*
270  * Push a string back onto the input at this current parsefile level.
271  * We handle aliases this way.
272  */
273 void
274 pushstring(s, len, ap)
275 	char *s;
276 	int len;
277 	void *ap;
278 	{
279 	struct strpush *sp;
280 
281 	INTOFF;
282 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
283 	if (parsefile->strpush) {
284 		sp = ckmalloc(sizeof (struct strpush));
285 		sp->prev = parsefile->strpush;
286 		parsefile->strpush = sp;
287 	} else
288 		sp = parsefile->strpush = &(parsefile->basestrpush);
289 	sp->prevstring = parsenextc;
290 	sp->prevnleft = parsenleft;
291 	sp->ap = (struct alias *)ap;
292 	if (ap)
293 		((struct alias *)ap)->flag |= ALIASINUSE;
294 	parsenextc = s;
295 	parsenleft = len;
296 	INTON;
297 }
298 
299 popstring()
300 {
301 	struct strpush *sp = parsefile->strpush;
302 
303 	INTOFF;
304 	parsenextc = sp->prevstring;
305 	parsenleft = sp->prevnleft;
306 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
307 	if (sp->ap)
308 		sp->ap->flag &= ~ALIASINUSE;
309 	parsefile->strpush = sp->prev;
310 	if (sp != &(parsefile->basestrpush))
311 		ckfree(sp);
312 	INTON;
313 }
314 
315 /*
316  * Set the input to take input from a file.  If push is set, push the
317  * old input onto the stack first.
318  */
319 
320 void
321 setinputfile(fname, push)
322 	char *fname;
323 	{
324 	int fd;
325 	int fd2;
326 
327 	INTOFF;
328 	if ((fd = open(fname, O_RDONLY)) < 0)
329 		error("Can't open %s", fname);
330 	if (fd < 10) {
331 		fd2 = copyfd(fd, 10);
332 		close(fd);
333 		if (fd2 < 0)
334 			error("Out of file descriptors");
335 		fd = fd2;
336 	}
337 	setinputfd(fd, push);
338 	INTON;
339 }
340 
341 
342 /*
343  * Like setinputfile, but takes an open file descriptor.  Call this with
344  * interrupts off.
345  */
346 
347 void
348 setinputfd(fd, push) {
349 	if (push) {
350 		pushfile();
351 		parsefile->buf = ckmalloc(BUFSIZ);
352 	}
353 	if (parsefile->fd > 0)
354 		close(parsefile->fd);
355 	parsefile->fd = fd;
356 	if (parsefile->buf == NULL)
357 		parsefile->buf = ckmalloc(BUFSIZ);
358 	parsenleft = 0;
359 	plinno = 1;
360 }
361 
362 
363 /*
364  * Like setinputfile, but takes input from a string.
365  */
366 
367 void
368 setinputstring(string, push)
369 	char *string;
370 	{
371 	INTOFF;
372 	if (push)
373 		pushfile();
374 	parsenextc = string;
375 	parsenleft = strlen(string);
376 	parsefile->buf = NULL;
377 	plinno = 1;
378 	INTON;
379 }
380 
381 
382 
383 /*
384  * To handle the "." command, a stack of input files is used.  Pushfile
385  * adds a new entry to the stack and popfile restores the previous level.
386  */
387 
388 STATIC void
389 pushfile() {
390 	struct parsefile *pf;
391 
392 	parsefile->nleft = parsenleft;
393 	parsefile->nextc = parsenextc;
394 	parsefile->linno = plinno;
395 	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
396 	pf->prev = parsefile;
397 	pf->fd = -1;
398 	pf->strpush = NULL;
399 	pf->basestrpush.prev = NULL;
400 	parsefile = pf;
401 }
402 
403 
404 void
405 popfile() {
406 	struct parsefile *pf = parsefile;
407 
408 	INTOFF;
409 	if (pf->fd >= 0)
410 		close(pf->fd);
411 	if (pf->buf)
412 		ckfree(pf->buf);
413 	while (pf->strpush)
414 		popstring();
415 	parsefile = pf->prev;
416 	ckfree(pf);
417 	parsenleft = parsefile->nleft;
418 	parsenextc = parsefile->nextc;
419 	plinno = parsefile->linno;
420 	INTON;
421 }
422 
423 
424 /*
425  * Return to top level.
426  */
427 
428 void
429 popallfiles() {
430 	while (parsefile != &basepf)
431 		popfile();
432 }
433 
434 
435 
436 /*
437  * Close the file(s) that the shell is reading commands from.  Called
438  * after a fork is done.
439  */
440 
441 void
442 closescript() {
443 	popallfiles();
444 	if (parsefile->fd > 0) {
445 		close(parsefile->fd);
446 		parsefile->fd = 0;
447 	}
448 }
449