]> git.saurik.com Git - apple/shell_cmds.git/blobdiff - sh/input.c
shell_cmds-198.tar.gz
[apple/shell_cmds.git] / sh / input.c
diff --git a/sh/input.c b/sh/input.c
new file mode 100644 (file)
index 0000000..a0fd01d
--- /dev/null
@@ -0,0 +1,525 @@
+/*-
+ * 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.
+ * 4. 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)input.c    8.3 (Berkeley) 6/9/95";
+#endif
+#endif /* not lint */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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"
+#include "trap.h"
+
+#define EOF_NLEFT -99          /* value of parsenleft when EOF pushed back */
+
+struct strpush {
+       struct strpush *prev;   /* preceding string on stack */
+       const 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.
+ */
+
+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 lines left in this buffer */
+       const 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 */
+static int parselleft;         /* copy of parsefile->lleft */
+const char *parsenextc;                /* copy of parsefile->nextc */
+static char basebuf[BUFSIZ + 1];/* buffer for top level input file */
+static struct parsefile basepf = {     /* top level input file */
+       .nextc = basebuf,
+       .buf = basebuf
+};
+static struct parsefile *parsefile = &basepf;  /* current input file */
+int whichprompt;               /* 1 == PS1, 2 == PS2 */
+
+#ifndef __APPLE__
+EditLine *el;                  /* cookie for editline package */
+#endif /* !__APPLE__ */
+
+static void pushfile(void);
+static int preadfd(void);
+static void popstring(void);
+
+void
+resetinput(void)
+{
+       popallfiles();
+       parselleft = parsenleft = 0;    /* clear input buffer */
+}
+
+
+
+/*
+ * 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();
+}
+
+
+static int
+preadfd(void)
+{
+       int nr;
+       parsenextc = parsefile->buf;
+
+retry:
+#ifndef NO_HISTORY
+       if (parsefile->fd == 0 && el) {
+               static const char *rl_cp;
+               static int el_len;
+
+               if (rl_cp == NULL) {
+                       el_resize(el);
+                       rl_cp = el_gets(el, &el_len);
+               }
+               if (rl_cp == NULL)
+                       nr = el_len == 0 ? 0 : -1;
+               else {
+                       nr = el_len;
+                       if (nr > BUFSIZ)
+                               nr = BUFSIZ;
+                       memcpy(parsefile->buf, rl_cp, nr);
+                       if (nr != el_len) {
+                               el_len -= nr;
+                               rl_cp += nr;
+                       } else
+                               rl_cp = NULL;
+               }
+       } else
+#endif
+               nr = read(parsefile->fd, parsefile->buf, BUFSIZ);
+
+       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) {
+                                               out2fmt_flush("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 there is more 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;
+       char savec;
+
+       while (parsefile->strpush) {
+               /*
+                * Add a space to the end of an alias to ensure that the
+                * alias remains in use while parsing its last word.
+                * This avoids alias recursions.
+                */
+               if (parsenleft == -1 && parsefile->strpush->ap != NULL)
+                       return ' ';
+               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 = parsefile->buf + (parsenextc - parsefile->buf);
+
+       /* delete nul characters */
+       for (more = 1; more;) {
+               switch (*p) {
+               case '\0':
+                       p++;    /* Skip nul */
+                       goto check;
+
+               case '\n':
+                       parsenleft = q - parsenextc;
+                       more = 0; /* Stop processing here */
+                       break;
+
+               default:
+                       break;
+               }
+
+               *q++ = *p++;
+check:
+               if (--parselleft <= 0) {
+                       parsenleft = q - parsenextc - 1;
+                       if (parsenleft < 0)
+                               goto again;
+                       *q = '\0';
+                       more = 0;
+               }
+       }
+
+       savec = *q;
+       *q = '\0';
+
+#ifndef NO_HISTORY
+       if (parsefile->fd == 0 && hist &&
+           parsenextc[strspn(parsenextc, " \t\n")] != '\0') {
+               HistEvent he;
+               INTOFF;
+               history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD,
+                   parsenextc);
+               INTON;
+       }
+#endif
+
+       if (vflag) {
+               out2str(parsenextc);
+               flushout(out2);
+       }
+
+       *q = savec;
+
+       return *parsenextc++;
+}
+
+/*
+ * Returns if we are certain we are at EOF. Does not cause any more input
+ * to be read from the outside world.
+ */
+
+int
+preadateof(void)
+{
+       if (parsenleft > 0)
+               return 0;
+       if (parsefile->strpush)
+               return 0;
+       if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+               return 1;
+       return 0;
+}
+
+/*
+ * 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(const char *s, int len, struct alias *ap)
+{
+       struct strpush *sp;
+
+       INTOFF;
+/*out2fmt_flush("*** 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 = ap;
+       if (ap)
+               ap->flag |= ALIASINUSE;
+       parsenextc = s;
+       parsenleft = len;
+       INTON;
+}
+
+static void
+popstring(void)
+{
+       struct strpush *sp = parsefile->strpush;
+
+       INTOFF;
+       if (sp->ap) {
+               if (parsenextc != sp->ap->val &&
+                   (parsenextc[-1] == ' ' || parsenextc[-1] == '\t'))
+                       forcealias();
+               sp->ap->flag &= ~ALIASINUSE;
+       }
+       parsenextc = sp->prevstring;
+       parsenleft = sp->prevnleft;
+       parselleft = sp->prevlleft;
+/*out2fmt_flush("*** 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.
+ */
+
+void
+setinputfile(const char *fname, int push)
+{
+       int fd;
+       int fd2;
+
+       INTOFF;
+       if ((fd = open(fname, O_RDONLY | O_CLOEXEC)) < 0)
+               error("cannot open %s: %s", fname, strerror(errno));
+       if (fd < 10) {
+               fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 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 (which should have
+ * its FD_CLOEXEC flag already set).  Call this with interrupts off.
+ */
+
+void
+setinputfd(int fd, int push)
+{
+       if (push) {
+               pushfile();
+               parsefile->buf = ckmalloc(BUFSIZ + 1);
+       }
+       if (parsefile->fd > 0)
+               close(parsefile->fd);
+       parsefile->fd = fd;
+       if (parsefile->buf == NULL)
+               parsefile->buf = ckmalloc(BUFSIZ + 1);
+       parselleft = parsenleft = 0;
+       plinno = 1;
+}
+
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+
+void
+setinputstring(const 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 current file (to go back to it later using popfilesupto()).
+ */
+
+struct parsefile *
+getcurrentfile(void)
+{
+       return parsefile;
+}
+
+
+/*
+ * Pop files until the given file is on top again. Useful for regular
+ * builtins that read shell commands from files or strings.
+ * If the given file is not an active file, an error is raised.
+ */
+
+void
+popfilesupto(struct parsefile *file)
+{
+       while (parsefile != file && parsefile != &basepf)
+               popfile();
+       if (parsefile != file)
+               error("popfilesupto() misused");
+}
+
+/*
+ * 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;
+       }
+}