input.c revision 1.58
1/*	$NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre 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
38static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95";
39#else
40__RCSID("$NetBSD: input.c,v 1.58 2017/06/07 05:08:32 kre 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#include <sys/stat.h>
52
53/*
54 * This file implements the input routines used by the parser.
55 */
56
57#include "shell.h"
58#include "redir.h"
59#include "syntax.h"
60#include "input.h"
61#include "output.h"
62#include "options.h"
63#include "memalloc.h"
64#include "error.h"
65#include "alias.h"
66#include "parser.h"
67#include "myhistedit.h"
68#include "show.h"
69
70#define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */
71
72MKINIT
73struct strpush {
74	struct strpush *prev;	/* preceding string on stack */
75	const 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
86MKINIT
87struct 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	const 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
100int plinno = 1;			/* input line number */
101int parsenleft;			/* copy of parsefile->nleft */
102MKINIT int parselleft;		/* copy of parsefile->lleft */
103const char *parsenextc;		/* copy of parsefile->nextc */
104MKINIT struct parsefile basepf;	/* top level input file */
105MKINIT char basebuf[BUFSIZ];	/* buffer for top level input file */
106struct parsefile *parsefile = &basepf;	/* current input file */
107int init_editline = 0;		/* editline library initialized? */
108int whichprompt;		/* 1 == PS1, 2 == PS2 */
109
110STATIC void pushfile(void);
111static int preadfd(void);
112
113#ifdef mkinit
114INCLUDE <stdio.h>
115INCLUDE "input.h"
116INCLUDE "error.h"
117
118INIT {
119	basepf.nextc = basepf.buf = basebuf;
120}
121
122RESET {
123	if (exception != EXSHELLPROC)
124		parselleft = parsenleft = 0;	/* clear input buffer */
125	popallfiles();
126}
127
128SHELLPROC {
129	popallfiles();
130}
131#endif
132
133
134/*
135 * Read a line from the script.
136 */
137
138char *
139pfgets(char *line, int len)
140{
141	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
167int
168pgetc(void)
169{
170	return pgetc_macro();
171}
172
173
174static int
175preadfd(void)
176{
177	int nr;
178	char *buf =  parsefile->buf;
179	parsenextc = buf;
180
181retry:
182#ifndef SMALL
183	if (parsefile->fd == 0 && el) {
184		static const char *rl_cp;
185		static int el_len;
186
187		if (rl_cp == NULL)
188			rl_cp = el_gets(el, &el_len);
189		if (rl_cp == NULL)
190			nr = el_len == 0 ? 0 : -1;
191		else {
192			nr = el_len;
193			if (nr > BUFSIZ - 8)
194				nr = BUFSIZ - 8;
195			memcpy(buf, rl_cp, nr);
196			if (nr != el_len) {
197				el_len -= nr;
198				rl_cp += nr;
199			} else
200				rl_cp = 0;
201		}
202
203	} else
204#endif
205		nr = read(parsefile->fd, buf, BUFSIZ - 8);
206
207
208	if (nr <= 0) {
209                if (nr < 0) {
210                        if (errno == EINTR)
211                                goto retry;
212                        if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
213                                int flags = fcntl(0, F_GETFL, 0);
214                                if (flags >= 0 && flags & O_NONBLOCK) {
215                                        flags &=~ O_NONBLOCK;
216                                        if (fcntl(0, F_SETFL, flags) >= 0) {
217						out2str("sh: turning off NDELAY mode\n");
218                                                goto retry;
219                                        }
220                                }
221                        }
222                }
223                nr = -1;
224	}
225	return nr;
226}
227
228/*
229 * Refill the input buffer and return the next input character:
230 *
231 * 1) If a string was pushed back on the input, pop it;
232 * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading
233 *    from a string so we can't refill the buffer, return EOF.
234 * 3) If the is more stuff in this buffer, use it else call read to fill it.
235 * 4) Process input up to the next newline, deleting nul characters.
236 */
237
238int
239preadbuffer(void)
240{
241	char *p, *q;
242	int more;
243#ifndef SMALL
244	int something;
245#endif
246	char savec;
247
248	while (parsefile->strpush) {
249		popstring();
250		if (--parsenleft >= 0)
251			return (*parsenextc++);
252	}
253	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
254		return PEOF;
255	flushout(&output);
256	flushout(&errout);
257
258again:
259	if (parselleft <= 0) {
260		if ((parselleft = preadfd()) == -1) {
261			parselleft = parsenleft = EOF_NLEFT;
262			return PEOF;
263		}
264	}
265
266		/* p = (not const char *)parsenextc; */
267	p = parsefile->buf + (parsenextc - parsefile->buf);
268	q = p;
269
270	/* delete nul characters */
271#ifndef SMALL
272	something = 0;
273#endif
274	for (more = 1; more;) {
275		switch (*p) {
276		case '\0':
277			p++;	/* Skip nul */
278			goto check;
279
280		case '\t':
281		case ' ':
282			break;
283
284		case '\n':
285			parsenleft = q - parsenextc;
286			more = 0; /* Stop processing here */
287			break;
288
289		default:
290#ifndef SMALL
291			something = 1;
292#endif
293			break;
294		}
295
296		*q++ = *p++;
297check:
298		if (--parselleft <= 0) {
299			parsenleft = q - parsenextc - 1;
300			if (parsenleft < 0)
301				goto again;
302			*q = '\0';
303			more = 0;
304		}
305	}
306
307	savec = *q;
308	*q = '\0';
309
310#ifndef SMALL
311	if (parsefile->fd == 0 && hist && something) {
312		HistEvent he;
313		INTOFF;
314		history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND,
315		    parsenextc);
316		INTON;
317	}
318#endif
319
320	if (vflag) {
321		out2str(parsenextc);
322		flushout(out2);
323	}
324
325	*q = savec;
326
327	return *parsenextc++;
328}
329
330/*
331 * Undo the last call to pgetc.  Only one character may be pushed back.
332 * PEOF may be pushed back.
333 */
334
335void
336pungetc(void)
337{
338	parsenleft++;
339	parsenextc--;
340}
341
342/*
343 * Push a string back onto the input at this current parsefile level.
344 * We handle aliases this way.
345 */
346void
347pushstring(const char *s, int len, struct alias *ap)
348{
349	struct strpush *sp;
350
351	INTOFF;
352/*debugprintf("*** calling pushstring: %s, %d\n", s, len);*/
353	if (parsefile->strpush) {
354		sp = ckmalloc(sizeof (struct strpush));
355		sp->prev = parsefile->strpush;
356		parsefile->strpush = sp;
357	} else
358		sp = parsefile->strpush = &(parsefile->basestrpush);
359	sp->prevstring = parsenextc;
360	sp->prevnleft = parsenleft;
361	sp->prevlleft = parselleft;
362	sp->ap = ap;
363	if (ap)
364		ap->flag |= ALIASINUSE;
365	parsenextc = s;
366	parsenleft = len;
367	INTON;
368}
369
370void
371popstring(void)
372{
373	struct strpush *sp = parsefile->strpush;
374
375	INTOFF;
376	parsenextc = sp->prevstring;
377	parsenleft = sp->prevnleft;
378	parselleft = sp->prevlleft;
379/*debugprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/
380	if (sp->ap)
381		sp->ap->flag &= ~ALIASINUSE;
382	parsefile->strpush = sp->prev;
383	if (sp != &(parsefile->basestrpush))
384		ckfree(sp);
385	INTON;
386}
387
388/*
389 * Set the input to take input from a file.  If push is set, push the
390 * old input onto the stack first.
391 */
392
393void
394setinputfile(const char *fname, int push)
395{
396	unsigned char magic[4];
397	int fd;
398	int fd2;
399	struct stat sb;
400
401	INTOFF;
402	if ((fd = open(fname, O_RDONLY)) < 0)
403		error("Can't open %s", fname);
404
405	/* Since the message "Syntax error: "(" unexpected" is not very
406	 * helpful, we check if the file starts with the ELF magic to
407	 * avoid that message. The first lseek tries to make sure that
408	 * we can later rewind the file.
409	 */
410	if (fstat(fd, &sb) == 0 && S_ISREG(sb.st_mode) &&
411	    lseek(fd, 0, SEEK_SET) == 0) {
412		if (read(fd, magic, 4) == 4) {
413			if (memcmp(magic, "\177ELF", 4) == 0) {
414				(void)close(fd);
415				error("Cannot execute ELF binary %s", fname);
416			}
417		}
418		if (lseek(fd, 0, SEEK_SET) != 0) {
419			(void)close(fd);
420			error("Cannot rewind the file %s", fname);
421		}
422	}
423
424	fd2 = to_upper_fd(fd);	/* closes fd, returns higher equiv */
425	if (fd2 == fd) {
426		(void) close(fd);
427		error("Out of file descriptors");
428	}
429
430	setinputfd(fd2, push);
431	INTON;
432}
433
434/*
435 * When a shell fd needs to be altered (when the user wants to use
436 * the same fd - rare, but happens - we need to locate the ref to
437 * the fd, and update it.  This happens via a callback.
438 * This is the callback func for fd's used for shell input
439 */
440static void
441input_fd_swap(int from, int to)
442{
443	struct parsefile *pf;
444
445	pf = parsefile;
446	while (pf != NULL) {		/* don't need to stop at basepf */
447		if (pf->fd == from)
448			pf->fd = to;
449		pf = pf->prev;
450	}
451}
452
453/*
454 * Like setinputfile, but takes an open file descriptor.  Call this with
455 * interrupts off.
456 */
457
458void
459setinputfd(int fd, int push)
460{
461	register_sh_fd(fd, input_fd_swap);
462	(void) fcntl(fd, F_SETFD, FD_CLOEXEC);
463	if (push) {
464		pushfile();
465		parsefile->buf = ckmalloc(BUFSIZ);
466	}
467	if (parsefile->fd > 0)
468		sh_close(parsefile->fd);
469	parsefile->fd = fd;
470	if (parsefile->buf == NULL)
471		parsefile->buf = ckmalloc(BUFSIZ);
472	parselleft = parsenleft = 0;
473	plinno = 1;
474}
475
476
477/*
478 * Like setinputfile, but takes input from a string.
479 */
480
481void
482setinputstring(char *string, int push, int line1)
483{
484
485	INTOFF;
486	if (push)		/* XXX: always, as it happens */
487		pushfile();
488	parsenextc = string;
489	parselleft = parsenleft = strlen(string);
490	parsefile->buf = NULL;
491	plinno = line1;
492	TRACE(("setinputstring(\"%.20s%s\" (%d), %d, %d)\n", string,
493	    (parsenleft > 20 ? "..." : ""), parsenleft, push, line1));
494	INTON;
495}
496
497
498
499/*
500 * To handle the "." command, a stack of input files is used.  Pushfile
501 * adds a new entry to the stack and popfile restores the previous level.
502 */
503
504STATIC void
505pushfile(void)
506{
507	struct parsefile *pf;
508
509	parsefile->nleft = parsenleft;
510	parsefile->lleft = parselleft;
511	parsefile->nextc = parsenextc;
512	parsefile->linno = plinno;
513	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile));
514	pf->prev = parsefile;
515	pf->fd = -1;
516	pf->strpush = NULL;
517	pf->basestrpush.prev = NULL;
518	parsefile = pf;
519}
520
521
522void
523popfile(void)
524{
525	struct parsefile *pf = parsefile;
526
527	INTOFF;
528	if (pf->fd >= 0)
529		sh_close(pf->fd);
530	if (pf->buf)
531		ckfree(pf->buf);
532	while (pf->strpush)
533		popstring();
534	parsefile = pf->prev;
535	ckfree(pf);
536	parsenleft = parsefile->nleft;
537	parselleft = parsefile->lleft;
538	parsenextc = parsefile->nextc;
539	plinno = parsefile->linno;
540	INTON;
541}
542
543
544/*
545 * Return to top level.
546 */
547
548void
549popallfiles(void)
550{
551	while (parsefile != &basepf)
552		popfile();
553}
554
555
556
557/*
558 * Close the file(s) that the shell is reading commands from.  Called
559 * after a fork is done.
560 *
561 * Takes one arg, vfork, which tells it to not modify its global vars
562 * as it is still running in the parent.
563 *
564 * This code is (probably) unnecessary as the 'close on exec' flag is
565 * set and should be enough.  In the vfork case it is definitely wrong
566 * to close the fds as another fork() may be done later to feed data
567 * from a 'here' document into a pipe and we don't want to close the
568 * pipe!
569 */
570
571void
572closescript(int vforked)
573{
574	if (vforked)
575		return;
576	popallfiles();
577	if (parsefile->fd > 0) {
578		sh_close(parsefile->fd);
579		parsefile->fd = 0;
580	}
581}
582