| /*- | |
| * Copyright (c) 1991, 1993 | |
| * The Regents of the University of California. All rights reserved. | |
| * Copyright (c) 1997-2005 | |
| * Herbert Xu <herbert@gondor.apana.org.au>. 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 <stdio.h> /* defines BUFSIZ */ | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| /* | |
| * This file implements the input routines used by the parser. | |
| */ | |
| #include "eval.h" | |
| #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 "main.h" | |
| #ifndef SMALL | |
| #include "myhistedit.h" | |
| #endif | |
| #ifdef HETIO | |
| #include "hetio.h" | |
| #endif | |
| #define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ | |
| #define IBUFSIZ (BUFSIZ + 1) | |
| MKINIT | |
| struct strpush { | |
| struct strpush *prev; /* preceding string on stack */ | |
| char *prevstring; | |
| int prevnleft; | |
| struct alias *ap; /* if push was associated with an alias */ | |
| char *string; /* remember the string since it may change */ | |
| }; | |
| /* | |
| * 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[IBUFSIZ]; /* buffer for top level input file */ | |
| struct parsefile *parsefile = &basepf; /* current input file */ | |
| int whichprompt; /* 1 == PS1, 2 == PS2 */ | |
| #ifndef SMALL | |
| EditLine *el; /* cookie for editline package */ | |
| #endif | |
| STATIC void pushfile(void); | |
| static int preadfd(void); | |
| static void setinputfd(int fd, int push); | |
| #ifdef mkinit | |
| INCLUDE <stdio.h> | |
| INCLUDE "input.h" | |
| INCLUDE "error.h" | |
| INIT { | |
| basepf.nextc = basepf.buf = basebuf; | |
| } | |
| RESET { | |
| parselleft = parsenleft = 0; /* clear input buffer */ | |
| popallfiles(); | |
| } | |
| #endif | |
| /* | |
| * 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(); | |
| } | |
| /* | |
| * Same as pgetc(), but ignores PEOA. | |
| */ | |
| int | |
| pgetc2() | |
| { | |
| int c; | |
| do { | |
| c = pgetc_macro(); | |
| } while (c == PEOA); | |
| return c; | |
| } | |
| static int | |
| preadfd(void) | |
| { | |
| int nr; | |
| char *buf = parsefile->buf; | |
| parsenextc = buf; | |
| retry: | |
| #ifndef SMALL | |
| 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 > IBUFSIZ - 1) | |
| nr = IBUFSIZ - 1; | |
| memcpy(buf, rl_cp, nr); | |
| if (nr != el_len) { | |
| el_len -= nr; | |
| rl_cp += nr; | |
| } else | |
| rl_cp = 0; | |
| } | |
| } else | |
| #endif | |
| #ifdef HETIO | |
| nr = hetio_read_input(parsefile->fd); | |
| if (nr == -255) | |
| #endif | |
| nr = read(parsefile->fd, buf, IBUFSIZ - 1); | |
| 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; | |
| } | |
| } | |
| } | |
| } | |
| 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 *q; | |
| int more; | |
| #ifndef SMALL | |
| int something; | |
| #endif | |
| char savec; | |
| while (unlikely(parsefile->strpush)) { | |
| if ( | |
| parsenleft == -1 && parsefile->strpush->ap && | |
| parsenextc[-1] != ' ' && parsenextc[-1] != '\t' | |
| ) { | |
| return PEOA; | |
| } | |
| popstring(); | |
| if (--parsenleft >= 0) | |
| return (signed char)*parsenextc++; | |
| } | |
| if (unlikely(parsenleft == EOF_NLEFT || parsefile->buf == NULL)) | |
| return PEOF; | |
| flushout(&output); | |
| #ifdef FLUSHERR | |
| flushout(&errout); | |
| #endif | |
| more = parselleft; | |
| if (more <= 0) { | |
| again: | |
| if ((more = preadfd()) <= 0) { | |
| parselleft = parsenleft = EOF_NLEFT; | |
| return PEOF; | |
| } | |
| } | |
| q = parsenextc; | |
| /* delete nul characters */ | |
| #ifndef SMALL | |
| something = 0; | |
| #endif | |
| for (;;) { | |
| int c; | |
| more--; | |
| c = *q; | |
| if (!c) | |
| memmove(q, q + 1, more); | |
| else { | |
| q++; | |
| if (c == '\n') { | |
| parsenleft = q - parsenextc - 1; | |
| break; | |
| } | |
| #ifndef SMALL | |
| switch (c) { | |
| default: | |
| something = 1; | |
| /* fall through */ | |
| case '\t': | |
| case ' ': | |
| break; | |
| } | |
| #endif | |
| } | |
| if (more <= 0) { | |
| parsenleft = q - parsenextc - 1; | |
| if (parsenleft < 0) | |
| goto again; | |
| break; | |
| } | |
| } | |
| parselleft = more; | |
| savec = *q; | |
| *q = '\0'; | |
| #ifndef SMALL | |
| 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); | |
| #ifdef FLUSHERR | |
| flushout(out2); | |
| #endif | |
| } | |
| *q = savec; | |
| return (signed char)*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, void *ap) | |
| { | |
| struct strpush *sp; | |
| size_t len; | |
| len = strlen(s); | |
| 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->ap = (struct alias *)ap; | |
| if (ap) { | |
| ((struct alias *)ap)->flag |= ALIASINUSE; | |
| sp->string = s; | |
| } | |
| parsenextc = s; | |
| parsenleft = len; | |
| INTON; | |
| } | |
| void | |
| popstring(void) | |
| { | |
| struct strpush *sp = parsefile->strpush; | |
| INTOFF; | |
| if (sp->ap) { | |
| if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { | |
| checkkwd |= CHKALIAS; | |
| } | |
| if (sp->string != sp->ap->val) { | |
| ckfree(sp->string); | |
| } | |
| sp->ap->flag &= ~ALIASINUSE; | |
| if (sp->ap->flag & ALIASDEAD) { | |
| unalias(sp->ap->name); | |
| } | |
| } | |
| parsenextc = sp->prevstring; | |
| parsenleft = sp->prevnleft; | |
| /*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ | |
| 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. | |
| */ | |
| int | |
| setinputfile(const char *fname, int flags) | |
| { | |
| int fd; | |
| INTOFF; | |
| if ((fd = open(fname, O_RDONLY)) < 0) { | |
| if (flags & INPUT_NOFILE_OK) | |
| goto out; | |
| exitstatus = 127; | |
| exerror(EXERROR, "Can't open %s", fname); | |
| } | |
| if (fd < 10) | |
| fd = savefd(fd, fd); | |
| setinputfd(fd, flags & INPUT_PUSH_FILE); | |
| out: | |
| INTON; | |
| return fd; | |
| } | |
| /* | |
| * Like setinputfile, but takes an open file descriptor. Call this with | |
| * interrupts off. | |
| */ | |
| static void | |
| setinputfd(int fd, int push) | |
| { | |
| if (push) { | |
| pushfile(); | |
| parsefile->buf = 0; | |
| } | |
| parsefile->fd = fd; | |
| if (parsefile->buf == NULL) | |
| parsefile->buf = ckmalloc(IBUFSIZ); | |
| parselleft = parsenleft = 0; | |
| plinno = 1; | |
| } | |
| /* | |
| * Like setinputfile, but takes input from a string. | |
| */ | |
| void | |
| setinputstring(char *string) | |
| { | |
| INTOFF; | |
| pushfile(); | |
| parsenextc = string; | |
| 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. | |
| */ | |
| void | |
| closescript(void) | |
| { | |
| popallfiles(); | |
| if (parsefile->fd > 0) { | |
| close(parsefile->fd); | |
| parsefile->fd = 0; | |
| } | |
| } |