xref: /netbsd-src/bin/sh/input.c (revision 81b108b45f75f89f1e3ffad9fb6f074e771c0935)
1 /*	$NetBSD: input.c,v 1.19 1995/10/19 04:14:37 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * This code is derived from software contributed to Berkeley by
8  * Kenneth Almquist.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  */
38 
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
42 #else
43 static char rcsid[] = "$NetBSD: input.c,v 1.19 1995/10/19 04:14:37 christos Exp $";
44 #endif
45 #endif /* not lint */
46 
47 #include <stdio.h>	/* defines BUFSIZ */
48 #include <fcntl.h>
49 #include <errno.h>
50 #include <unistd.h>
51 #include <stdlib.h>
52 #include <string.h>
53 
54 /*
55  * This file implements the input routines used by the parser.
56  */
57 
58 #include "shell.h"
59 #include "redir.h"
60 #include "syntax.h"
61 #include "input.h"
62 #include "output.h"
63 #include "options.h"
64 #include "memalloc.h"
65 #include "error.h"
66 #include "alias.h"
67 #include "parser.h"
68 #include "myhistedit.h"
69 
70 #define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
71 
72 MKINIT
73 struct strpush {
74 	struct strpush *prev;	/* preceding string on stack */
75 	char *prevstring;
76 	int prevnleft;
77 	int prevlleft;
78 	struct alias *ap;	/* if push was associated with an alias */
79 };
80 
81 /*
82  * The parsefile structure pointed to by the global variable parsefile
83  * contains information about the current file being read.
84  */
85 
86 MKINIT
87 struct parsefile {
88 	struct parsefile *prev;	/* preceding file on stack */
89 	int linno;		/* current line */
90 	int fd;			/* file descriptor (or -1 if string) */
91 	int nleft;		/* number of chars left in this line */
92 	int lleft;		/* number of chars left in this buffer */
93 	char *nextc;		/* next char in buffer */
94 	char *buf;		/* input buffer */
95 	struct strpush *strpush; /* for pushing strings at this level */
96 	struct strpush basestrpush; /* so pushing one is fast */
97 };
98 
99 
100 int plinno = 1;			/* input line number */
101 MKINIT int parsenleft;		/* copy of parsefile->nleft */
102 MKINIT int parselleft;		/* copy of parsefile->lleft */
103 char *parsenextc;		/* copy of parsefile->nextc */
104 MKINIT struct parsefile basepf;	/* top level input file */
105 char basebuf[BUFSIZ];		/* buffer for top level input file */
106 struct parsefile *parsefile = &basepf;	/* current input file */
107 int init_editline = 0;		/* editline library initialized? */
108 int whichprompt;		/* 1 == PS1, 2 == PS2 */
109 
110 EditLine *el;			/* cookie for editline package */
111 
112 STATIC void pushfile __P((void));
113 static int pread __P((void));
114 
115 #ifdef mkinit
116 INCLUDE "input.h"
117 INCLUDE "error.h"
118 
119 INIT {
120 	extern char basebuf[];
121 
122 	basepf.nextc = basepf.buf = basebuf;
123 }
124 
125 RESET {
126 	if (exception != EXSHELLPROC)
127 		parselleft = parsenleft = 0;	/* clear input buffer */
128 	popallfiles();
129 }
130 
131 SHELLPROC {
132 	popallfiles();
133 }
134 #endif
135 
136 
137 /*
138  * Read a line from the script.
139  */
140 
141 char *
142 pfgets(line, len)
143 	char *line;
144 	int len;
145 {
146 	register char *p = line;
147 	int nleft = len;
148 	int c;
149 
150 	while (--nleft > 0) {
151 		c = pgetc_macro();
152 		if (c == PEOF) {
153 			if (p == line)
154 				return NULL;
155 			break;
156 		}
157 		*p++ = c;
158 		if (c == '\n')
159 			break;
160 	}
161 	*p = '\0';
162 	return line;
163 }
164 
165 
166 
167 /*
168  * Read a character from the script, returning PEOF on end of file.
169  * Nul characters in the input are silently discarded.
170  */
171 
172 int
173 pgetc()
174 {
175 	return pgetc_macro();
176 }
177 
178 
179 static int
180 pread()
181 {
182 	int nr;
183 	parsenextc = parsefile->buf;
184 
185 retry:
186 	if (parsefile->fd == 0 && el) {
187 		const char *rl_cp;
188 		int len;
189 
190 		rl_cp = el_gets(el, &nr);
191 		if (rl_cp == NULL)
192 			nr = 0;
193 		else {
194 			/* XXX - BUFSIZE should redesign so not necessary */
195 			(void) strcpy(parsenextc, rl_cp);
196 		}
197 	} else {
198 		nr = read(parsefile->fd, parsenextc, BUFSIZ - 1);
199 	}
200 
201 	if (nr <= 0) {
202                 if (nr < 0) {
203                         if (errno == EINTR)
204                                 goto retry;
205                         if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
206                                 int flags = fcntl(0, F_GETFL, 0);
207                                 if (flags >= 0 && flags & O_NONBLOCK) {
208                                         flags &=~ O_NONBLOCK;
209                                         if (fcntl(0, F_SETFL, flags) >= 0) {
210 						out2str("sh: turning off NDELAY mode\n");
211                                                 goto retry;
212                                         }
213                                 }
214                         }
215                 }
216                 nr = -1;
217 	}
218 	return nr;
219 }
220 
221 /*
222  * Refill the input buffer and return the next input character:
223  *
224  * 1) If a string was pushed back on the input, pop it;
225  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
226  *    from a string so we can't refill the buffer, return EOF.
227  * 3) If the is more stuff in this buffer, use it else call read to fill it.
228  * 4) Process input up to the next newline, deleting nul characters.
229  */
230 
231 int
232 preadbuffer()
233 {
234 	char *p, *q;
235 	int more;
236 	int something;
237 	extern EditLine *el;
238 	char savec;
239 
240 	if (parsefile->strpush) {
241 		popstring();
242 		if (--parsenleft >= 0)
243 			return (*parsenextc++);
244 	}
245 	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
246 		return PEOF;
247 	flushout(&output);
248 	flushout(&errout);
249 
250 again:
251 	if (parselleft <= 0) {
252 		if ((parselleft = pread()) == -1) {
253 			parselleft = parsenleft = EOF_NLEFT;
254 			return PEOF;
255 		}
256 	}
257 
258 	q = p = parsenextc;
259 
260 	/* delete nul characters */
261 	something = 0;
262 	for (more = 1; more;) {
263 		switch (*p) {
264 		case '\0':
265 			p++;	/* Skip nul */
266 			goto check;
267 
268 		case '\t':
269 		case ' ':
270 			break;
271 
272 		case '\n':
273 			parsenleft = q - parsenextc;
274 			more = 0; /* Stop processing here */
275 			break;
276 
277 		default:
278 			something = 1;
279 			break;
280 		}
281 
282 		*q++ = *p++;
283 check:
284 		if (--parselleft <= 0) {
285 			parsenleft = q - parsenextc - 1;
286 			if (parsenleft < 0)
287 				goto again;
288 			*q = '\0';
289 			more = 0;
290 		}
291 	}
292 
293 	savec = *q;
294 	*q = '\0';
295 
296 #ifndef NO_HISTORY
297 	if (parsefile->fd == 0 && hist && something) {
298 		INTOFF;
299 		history(hist, whichprompt == 1 ? H_ENTER : H_ADD, parsenextc);
300 		INTON;
301 	}
302 #endif
303 
304 	if (vflag) {
305 		out2str(parsenextc);
306 		flushout(out2);
307 	}
308 
309 	*q = savec;
310 
311 	return *parsenextc++;
312 }
313 
314 /*
315  * Undo the last call to pgetc.  Only one character may be pushed back.
316  * PEOF may be pushed back.
317  */
318 
319 void
320 pungetc() {
321 	parsenleft++;
322 	parsenextc--;
323 }
324 
325 /*
326  * Push a string back onto the input at this current parsefile level.
327  * We handle aliases this way.
328  */
329 void
330 pushstring(s, len, ap)
331 	char *s;
332 	int len;
333 	void *ap;
334 	{
335 	struct strpush *sp;
336 
337 	INTOFF;
338 /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/
339 	if (parsefile->strpush) {
340 		sp = ckmalloc(sizeof (struct strpush));
341 		sp->prev = parsefile->strpush;
342 		parsefile->strpush = sp;
343 	} else
344 		sp = parsefile->strpush = &(parsefile->basestrpush);
345 	sp->prevstring = parsenextc;
346 	sp->prevnleft = parsenleft;
347 	sp->prevlleft = parselleft;
348 	sp->ap = (struct alias *)ap;
349 	if (ap)
350 		((struct alias *)ap)->flag |= ALIASINUSE;
351 	parsenextc = s;
352 	parsenleft = len;
353 	INTON;
354 }
355 
356 void
357 popstring()
358 {
359 	struct strpush *sp = parsefile->strpush;
360 
361 	INTOFF;
362 	parsenextc = sp->prevstring;
363 	parsenleft = sp->prevnleft;
364 	parselleft = sp->prevlleft;
365 /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
366 	if (sp->ap)
367 		sp->ap->flag &= ~ALIASINUSE;
368 	parsefile->strpush = sp->prev;
369 	if (sp != &(parsefile->basestrpush))
370 		ckfree(sp);
371 	INTON;
372 }
373 
374 /*
375  * Set the input to take input from a file.  If push is set, push the
376  * old input onto the stack first.
377  */
378 
379 void
380 setinputfile(fname, push)
381 	char *fname;
382 	int push;
383 {
384 	int fd;
385 	int fd2;
386 
387 	INTOFF;
388 	if ((fd = open(fname, O_RDONLY)) < 0)
389 		error("Can't open %s", fname);
390 	if (fd < 10) {
391 		fd2 = copyfd(fd, 10);
392 		close(fd);
393 		if (fd2 < 0)
394 			error("Out of file descriptors");
395 		fd = fd2;
396 	}
397 	setinputfd(fd, push);
398 	INTON;
399 }
400 
401 
402 /*
403  * Like setinputfile, but takes an open file descriptor.  Call this with
404  * interrupts off.
405  */
406 
407 void
408 setinputfd(fd, push)
409 	int fd, push;
410 {
411 	if (push) {
412 		pushfile();
413 		parsefile->buf = ckmalloc(BUFSIZ);
414 	}
415 	if (parsefile->fd > 0)
416 		close(parsefile->fd);
417 	parsefile->fd = fd;
418 	if (parsefile->buf == NULL)
419 		parsefile->buf = ckmalloc(BUFSIZ);
420 	parselleft = parsenleft = 0;
421 	plinno = 1;
422 }
423 
424 
425 /*
426  * Like setinputfile, but takes input from a string.
427  */
428 
429 void
430 setinputstring(string, push)
431 	char *string;
432 	int push;
433 	{
434 	INTOFF;
435 	if (push)
436 		pushfile();
437 	parsenextc = string;
438 	parselleft = parsenleft = strlen(string);
439 	parsefile->buf = NULL;
440 	plinno = 1;
441 	INTON;
442 }
443 
444 
445 
446 /*
447  * To handle the "." command, a stack of input files is used.  Pushfile
448  * adds a new entry to the stack and popfile restores the previous level.
449  */
450 
451 STATIC void
452 pushfile() {
453 	struct parsefile *pf;
454 
455 	parsefile->nleft = parsenleft;
456 	parsefile->lleft = parselleft;
457 	parsefile->nextc = parsenextc;
458 	parsefile->linno = plinno;
459 	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
460 	pf->prev = parsefile;
461 	pf->fd = -1;
462 	pf->strpush = NULL;
463 	pf->basestrpush.prev = NULL;
464 	parsefile = pf;
465 }
466 
467 
468 void
469 popfile() {
470 	struct parsefile *pf = parsefile;
471 
472 	INTOFF;
473 	if (pf->fd >= 0)
474 		close(pf->fd);
475 	if (pf->buf)
476 		ckfree(pf->buf);
477 	while (pf->strpush)
478 		popstring();
479 	parsefile = pf->prev;
480 	ckfree(pf);
481 	parsenleft = parsefile->nleft;
482 	parselleft = parsefile->lleft;
483 	parsenextc = parsefile->nextc;
484 	plinno = parsefile->linno;
485 	INTON;
486 }
487 
488 
489 /*
490  * Return to top level.
491  */
492 
493 void
494 popallfiles() {
495 	while (parsefile != &basepf)
496 		popfile();
497 }
498 
499 
500 
501 /*
502  * Close the file(s) that the shell is reading commands from.  Called
503  * after a fork is done.
504  */
505 
506 void
507 closescript() {
508 	popallfiles();
509 	if (parsefile->fd > 0) {
510 		close(parsefile->fd);
511 		parsefile->fd = 0;
512 	}
513 }
514