|  | /*	$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; | 
|  | } | 
|  | } |