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