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