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