| /*	$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $	*/ | 
 |  | 
 | /*- | 
 |  * Copyright (c) 1991, 1993 | 
 |  *	The Regents of the University of California.  All rights reserved. | 
 |  * | 
 |  * This code is derived from software contributed to Berkeley by | 
 |  * Kenneth Almquist. | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions | 
 |  * are met: | 
 |  * 1. Redistributions of source code must retain the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer. | 
 |  * 2. Redistributions in binary form must reproduce the above copyright | 
 |  *    notice, this list of conditions and the following disclaimer in the | 
 |  *    documentation and/or other materials provided with the distribution. | 
 |  * 3. Neither the name of the University nor the names of its contributors | 
 |  *    may be used to endorse or promote products derived from this software | 
 |  *    without specific prior written permission. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | 
 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | 
 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | 
 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | 
 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | 
 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 
 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | 
 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 |  * SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include <sys/cdefs.h> | 
 | #ifndef lint | 
 | #if 0 | 
 | static char sccsid[] = "@(#)input.c	8.3 (Berkeley) 6/9/95"; | 
 | #else | 
 | __RCSID("$NetBSD: input.c,v 1.39 2003/08/07 09:05:32 agc Exp $"); | 
 | #endif | 
 | #endif /* not lint */ | 
 |  | 
 | #include <stdio.h>	/* defines BUFSIZ */ | 
 | #include <fcntl.h> | 
 | #include <errno.h> | 
 | #include <unistd.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | /* | 
 |  * This file implements the input routines used by the parser. | 
 |  */ | 
 |  | 
 | #include "shell.h" | 
 | #include "redir.h" | 
 | #include "syntax.h" | 
 | #include "input.h" | 
 | #include "output.h" | 
 | #include "options.h" | 
 | #include "memalloc.h" | 
 | #include "error.h" | 
 | #include "alias.h" | 
 | #include "parser.h" | 
 | #include "myhistedit.h" | 
 |  | 
 | #ifdef WITH_LINENOISE | 
 | #include "linenoise.h" | 
 | #endif | 
 |  | 
 | #define EOF_NLEFT -99		/* value of parsenleft when EOF pushed back */ | 
 |  | 
 | MKINIT | 
 | struct strpush { | 
 | 	struct strpush *prev;	/* preceding string on stack */ | 
 | 	char *prevstring; | 
 | 	int prevnleft; | 
 | 	int prevlleft; | 
 | 	struct alias *ap;	/* if push was associated with an alias */ | 
 | }; | 
 |  | 
 | /* | 
 |  * The parsefile structure pointed to by the global variable parsefile | 
 |  * contains information about the current file being read. | 
 |  */ | 
 |  | 
 | MKINIT | 
 | struct parsefile { | 
 | 	struct parsefile *prev;	/* preceding file on stack */ | 
 | 	int linno;		/* current line */ | 
 | 	int fd;			/* file descriptor (or -1 if string) */ | 
 | 	int nleft;		/* number of chars left in this line */ | 
 | 	int lleft;		/* number of chars left in this buffer */ | 
 | 	char *nextc;		/* next char in buffer */ | 
 | 	char *buf;		/* input buffer */ | 
 | 	struct strpush *strpush; /* for pushing strings at this level */ | 
 | 	struct strpush basestrpush; /* so pushing one is fast */ | 
 | }; | 
 |  | 
 |  | 
 | int plinno = 1;			/* input line number */ | 
 | int parsenleft;			/* copy of parsefile->nleft */ | 
 | MKINIT int parselleft;		/* copy of parsefile->lleft */ | 
 | char *parsenextc;		/* copy of parsefile->nextc */ | 
 | MKINIT struct parsefile basepf;	/* top level input file */ | 
 | MKINIT char basebuf[BUFSIZ];	/* buffer for top level input file */ | 
 | struct parsefile *parsefile = &basepf;	/* current input file */ | 
 | int init_editline = 0;		/* editline library initialized? */ | 
 | int whichprompt;		/* 1 == PS1, 2 == PS2 */ | 
 |  | 
 | #if WITH_HISTORY | 
 | EditLine *el;			/* cookie for editline package */ | 
 | #endif | 
 |  | 
 | STATIC void pushfile(void); | 
 | static int preadfd(void); | 
 |  | 
 | #ifdef mkinit | 
 | INCLUDE <stdio.h> | 
 | INCLUDE "input.h" | 
 | INCLUDE "error.h" | 
 |  | 
 | INIT { | 
 | 	basepf.nextc = basepf.buf = basebuf; | 
 | } | 
 |  | 
 | RESET { | 
 | 	if (exception != EXSHELLPROC) | 
 | 		parselleft = parsenleft = 0;	/* clear input buffer */ | 
 | 	popallfiles(); | 
 | } | 
 |  | 
 | SHELLPROC { | 
 | 	popallfiles(); | 
 | } | 
 | #endif | 
 |  | 
 |  | 
 | /* | 
 |  * Read a line from the script. | 
 |  */ | 
 |  | 
 | char * | 
 | pfgets(char *line, int len) | 
 | { | 
 | 	char *p = line; | 
 | 	int nleft = len; | 
 | 	int c; | 
 |  | 
 | 	while (--nleft > 0) { | 
 | 		c = pgetc_macro(); | 
 | 		if (c == PEOF) { | 
 | 			if (p == line) | 
 | 				return NULL; | 
 | 			break; | 
 | 		} | 
 | 		*p++ = c; | 
 | 		if (c == '\n') | 
 | 			break; | 
 | 	} | 
 | 	*p = '\0'; | 
 | 	return line; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* | 
 |  * Read a character from the script, returning PEOF on end of file. | 
 |  * Nul characters in the input are silently discarded. | 
 |  */ | 
 |  | 
 | int | 
 | pgetc(void) | 
 | { | 
 | 	return pgetc_macro(); | 
 | } | 
 |  | 
 | int in_interactive_mode() { | 
 |     return parsefile != NULL && parsefile->fd == 0; | 
 | } | 
 |  | 
 | static int | 
 | preadfd(void) | 
 | { | 
 | 	int nr; | 
 | 	char *buf =  parsefile->buf; | 
 | 	parsenextc = buf; | 
 |  | 
 | retry: | 
 | #ifdef WITH_HISTORY | 
 | 	if (parsefile->fd == 0 && el) { | 
 | 		static const char *rl_cp; | 
 | 		static int el_len; | 
 |  | 
 | 		if (rl_cp == NULL) | 
 | 			rl_cp = el_gets(el, &el_len); | 
 | 		if (rl_cp == NULL) | 
 | 			nr = 0; | 
 | 		else { | 
 | 			nr = el_len; | 
 | 			if (nr > BUFSIZ - 8) | 
 | 				nr = BUFSIZ - 8; | 
 | 			memcpy(buf, rl_cp, nr); | 
 | 			if (nr != el_len) { | 
 | 				el_len -= nr; | 
 | 				rl_cp += nr; | 
 | 			} else | 
 | 				rl_cp = 0; | 
 | 		} | 
 |  | 
 | 	} else | 
 | #endif | 
 | #ifdef WITH_LINENOISE | 
 |     if (parsefile->fd == 0) { | 
 |         static char *rl_start; | 
 |         static const char *rl_cp; | 
 |         static int el_len; | 
 |  | 
 |         if (rl_cp == NULL) { | 
 |             rl_cp = rl_start = linenoise(getprompt("")); | 
 |             if (rl_cp != NULL) { | 
 |                 el_len = strlen(rl_start); | 
 |                 if (el_len != 0) { | 
 |                     /* Add non-blank lines to history. */ | 
 |                     linenoiseHistoryAdd(rl_start); | 
 |                 } | 
 |                 out2str("\n"); | 
 |                 /* Client expects a newline at end of input, doesn't expect null */ | 
 |                 rl_start[el_len++] = '\n'; | 
 |             } | 
 |         } | 
 |         if (rl_cp == NULL) | 
 |             nr = 0; | 
 |         else { | 
 |             nr = el_len; | 
 |             if (nr > BUFSIZ - 8) | 
 |                 nr = BUFSIZ - 8; | 
 |             memcpy(buf, rl_cp, nr); | 
 |             if (nr != el_len) { | 
 |                 el_len -= nr; | 
 |                 rl_cp += nr; | 
 |             } else { | 
 |                 rl_cp = 0; | 
 |                 if (rl_start != NULL) { | 
 |                     free(rl_start); | 
 |                     rl_start = NULL; | 
 |                 } | 
 |             } | 
 |         } | 
 |     } else | 
 | #endif | 
 | 		nr = read(parsefile->fd, buf, BUFSIZ - 8); | 
 |  | 
 |  | 
 | 	if (nr <= 0) { | 
 |                 if (nr < 0) { | 
 |                         if (errno == EINTR) | 
 |                                 goto retry; | 
 |                         if (parsefile->fd == 0 && errno == EWOULDBLOCK) { | 
 |                                 int flags = fcntl(0, F_GETFL, 0); | 
 |                                 if (flags >= 0 && flags & O_NONBLOCK) { | 
 |                                         flags &=~ O_NONBLOCK; | 
 |                                         if (fcntl(0, F_SETFL, flags) >= 0) { | 
 | 						out2str("sh: turning off NDELAY mode\n"); | 
 |                                                 goto retry; | 
 |                                         } | 
 |                                 } | 
 |                         } | 
 |                 } | 
 |                 nr = -1; | 
 | 	} | 
 | 	return nr; | 
 | } | 
 |  | 
 | /* | 
 |  * Refill the input buffer and return the next input character: | 
 |  * | 
 |  * 1) If a string was pushed back on the input, pop it; | 
 |  * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading | 
 |  *    from a string so we can't refill the buffer, return EOF. | 
 |  * 3) If the is more stuff in this buffer, use it else call read to fill it. | 
 |  * 4) Process input up to the next newline, deleting nul characters. | 
 |  */ | 
 |  | 
 | int | 
 | preadbuffer(void) | 
 | { | 
 | 	char *p, *q; | 
 | 	int more; | 
 | 	int something; | 
 | 	char savec; | 
 |  | 
 | 	if (parsefile->strpush) { | 
 | 		popstring(); | 
 | 		if (--parsenleft >= 0) | 
 | 			return (*parsenextc++); | 
 | 	} | 
 | 	if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) | 
 | 		return PEOF; | 
 | 	flushout(&output); | 
 | 	flushout(&errout); | 
 |  | 
 | again: | 
 | 	if (parselleft <= 0) { | 
 | 		if ((parselleft = preadfd()) == -1) { | 
 | 			parselleft = parsenleft = EOF_NLEFT; | 
 | 			return PEOF; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	q = p = parsenextc; | 
 |  | 
 | 	/* delete nul characters */ | 
 | 	something = 0; | 
 | 	for (more = 1; more;) { | 
 | 		switch (*p) { | 
 | 		case '\0': | 
 | 			p++;	/* Skip nul */ | 
 | 			goto check; | 
 |  | 
 | 		case '\t': | 
 | 		case ' ': | 
 | 			break; | 
 |  | 
 | 		case '\n': | 
 | 			parsenleft = q - parsenextc; | 
 | 			more = 0; /* Stop processing here */ | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			something = 1; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		*q++ = *p++; | 
 | check: | 
 | 		if (--parselleft <= 0) { | 
 | 			parsenleft = q - parsenextc - 1; | 
 | 			if (parsenleft < 0) | 
 | 				goto again; | 
 | 			*q = '\0'; | 
 | 			more = 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	savec = *q; | 
 | 	*q = '\0'; | 
 |  | 
 | #ifdef WITH_HISTORY | 
 | 	if (parsefile->fd == 0 && hist && something) { | 
 | 		HistEvent he; | 
 | 		INTOFF; | 
 | 		history(hist, &he, whichprompt == 1? H_ENTER : H_APPEND, | 
 | 		    parsenextc); | 
 | 		INTON; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	if (vflag) { | 
 | 		out2str(parsenextc); | 
 | 		flushout(out2); | 
 | 	} | 
 |  | 
 | 	*q = savec; | 
 |  | 
 | 	return *parsenextc++; | 
 | } | 
 |  | 
 | /* | 
 |  * Undo the last call to pgetc.  Only one character may be pushed back. | 
 |  * PEOF may be pushed back. | 
 |  */ | 
 |  | 
 | void | 
 | pungetc(void) | 
 | { | 
 | 	parsenleft++; | 
 | 	parsenextc--; | 
 | } | 
 |  | 
 | /* | 
 |  * Push a string back onto the input at this current parsefile level. | 
 |  * We handle aliases this way. | 
 |  */ | 
 | void | 
 | pushstring(char *s, int len, void *ap) | 
 | { | 
 | 	struct strpush *sp; | 
 |  | 
 | 	INTOFF; | 
 | /*dprintf("*** calling pushstring: %s, %d\n", s, len);*/ | 
 | 	if (parsefile->strpush) { | 
 | 		sp = ckmalloc(sizeof (struct strpush)); | 
 | 		sp->prev = parsefile->strpush; | 
 | 		parsefile->strpush = sp; | 
 | 	} else | 
 | 		sp = parsefile->strpush = &(parsefile->basestrpush); | 
 | 	sp->prevstring = parsenextc; | 
 | 	sp->prevnleft = parsenleft; | 
 | 	sp->prevlleft = parselleft; | 
 | 	sp->ap = (struct alias *)ap; | 
 | 	if (ap) | 
 | 		((struct alias *)ap)->flag |= ALIASINUSE; | 
 | 	parsenextc = s; | 
 | 	parsenleft = len; | 
 | 	INTON; | 
 | } | 
 |  | 
 | void | 
 | popstring(void) | 
 | { | 
 | 	struct strpush *sp = parsefile->strpush; | 
 |  | 
 | 	INTOFF; | 
 | 	parsenextc = sp->prevstring; | 
 | 	parsenleft = sp->prevnleft; | 
 | 	parselleft = sp->prevlleft; | 
 | /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ | 
 | 	if (sp->ap) | 
 | 		sp->ap->flag &= ~ALIASINUSE; | 
 | 	parsefile->strpush = sp->prev; | 
 | 	if (sp != &(parsefile->basestrpush)) | 
 | 		ckfree(sp); | 
 | 	INTON; | 
 | } | 
 |  | 
 | /* | 
 |  * Set the input to take input from a file.  If push is set, push the | 
 |  * old input onto the stack first. | 
 |  */ | 
 |  | 
 | void | 
 | setinputfile(const char *fname, int push) | 
 | { | 
 | 	int fd; | 
 | 	int fd2; | 
 |  | 
 | 	INTOFF; | 
 | 	if ((fd = open(fname, O_RDONLY)) < 0) | 
 | 		error("Can't open %s", fname); | 
 | 	if (fd < 10) { | 
 | 		fd2 = copyfd(fd, 10); | 
 | 		close(fd); | 
 | 		if (fd2 < 0) | 
 | 			error("Out of file descriptors"); | 
 | 		fd = fd2; | 
 | 	} | 
 | 	setinputfd(fd, push); | 
 | 	INTON; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Like setinputfile, but takes an open file descriptor.  Call this with | 
 |  * interrupts off. | 
 |  */ | 
 |  | 
 | void | 
 | setinputfd(int fd, int push) | 
 | { | 
 | 	(void) fcntl(fd, F_SETFD, FD_CLOEXEC); | 
 | 	if (push) { | 
 | 		pushfile(); | 
 | 		parsefile->buf = ckmalloc(BUFSIZ); | 
 | 	} | 
 | 	if (parsefile->fd > 0) | 
 | 		close(parsefile->fd); | 
 | 	parsefile->fd = fd; | 
 | 	if (parsefile->buf == NULL) | 
 | 		parsefile->buf = ckmalloc(BUFSIZ); | 
 | 	parselleft = parsenleft = 0; | 
 | 	plinno = 1; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Like setinputfile, but takes input from a string. | 
 |  */ | 
 |  | 
 | void | 
 | setinputstring(char *string, int push) | 
 | { | 
 | 	INTOFF; | 
 | 	if (push) | 
 | 		pushfile(); | 
 | 	parsenextc = string; | 
 | 	parselleft = parsenleft = strlen(string); | 
 | 	parsefile->buf = NULL; | 
 | 	plinno = 1; | 
 | 	INTON; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* | 
 |  * To handle the "." command, a stack of input files is used.  Pushfile | 
 |  * adds a new entry to the stack and popfile restores the previous level. | 
 |  */ | 
 |  | 
 | STATIC void | 
 | pushfile(void) | 
 | { | 
 | 	struct parsefile *pf; | 
 |  | 
 | 	parsefile->nleft = parsenleft; | 
 | 	parsefile->lleft = parselleft; | 
 | 	parsefile->nextc = parsenextc; | 
 | 	parsefile->linno = plinno; | 
 | 	pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); | 
 | 	pf->prev = parsefile; | 
 | 	pf->fd = -1; | 
 | 	pf->strpush = NULL; | 
 | 	pf->basestrpush.prev = NULL; | 
 | 	parsefile = pf; | 
 | } | 
 |  | 
 |  | 
 | void | 
 | popfile(void) | 
 | { | 
 | 	struct parsefile *pf = parsefile; | 
 |  | 
 | 	INTOFF; | 
 | 	if (pf->fd >= 0) | 
 | 		close(pf->fd); | 
 | 	if (pf->buf) | 
 | 		ckfree(pf->buf); | 
 | 	while (pf->strpush) | 
 | 		popstring(); | 
 | 	parsefile = pf->prev; | 
 | 	ckfree(pf); | 
 | 	parsenleft = parsefile->nleft; | 
 | 	parselleft = parsefile->lleft; | 
 | 	parsenextc = parsefile->nextc; | 
 | 	plinno = parsefile->linno; | 
 | 	INTON; | 
 | } | 
 |  | 
 |  | 
 | /* | 
 |  * Return to top level. | 
 |  */ | 
 |  | 
 | void | 
 | popallfiles(void) | 
 | { | 
 | 	while (parsefile != &basepf) | 
 | 		popfile(); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | /* | 
 |  * Close the file(s) that the shell is reading commands from.  Called | 
 |  * after a fork is done. | 
 |  * | 
 |  * Takes one arg, vfork, which tells it to not modify its global vars | 
 |  * as it is still running in the parent. | 
 |  * | 
 |  * This code is (probably) unnecessary as the 'close on exec' flag is | 
 |  * set and should be enough.  In the vfork case it is definitely wrong | 
 |  * to close the fds as another fork() may be done later to feed data | 
 |  * from a 'here' document into a pipe and we don't want to close the | 
 |  * pipe! | 
 |  */ | 
 |  | 
 | void | 
 | closescript(int vforked) | 
 | { | 
 | 	if (vforked) | 
 | 		return; | 
 | 	popallfiles(); | 
 | 	if (parsefile->fd > 0) { | 
 | 		close(parsefile->fd); | 
 | 		parsefile->fd = 0; | 
 | 	} | 
 | } |