Add sh from 4.4BSD-Lite Release 2
authorJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:55:02 +0000 (17:55 -0700)
committerJason Self <j@jxself.org>
Mon, 10 Jun 2019 00:55:02 +0000 (17:55 -0700)
Signed-off-by: Jason Self <j@jxself.org>
67 files changed:
usr/src/bin/sh/Makefile [new file with mode: 0644]
usr/src/bin/sh/alias.c [new file with mode: 0644]
usr/src/bin/sh/alias.h [new file with mode: 0644]
usr/src/bin/sh/arith.h [new file with mode: 0644]
usr/src/bin/sh/arith.y [new file with mode: 0644]
usr/src/bin/sh/arith_lex.l [new file with mode: 0644]
usr/src/bin/sh/bltin/bltin.h [new file with mode: 0644]
usr/src/bin/sh/bltin/echo.1 [new file with mode: 0644]
usr/src/bin/sh/bltin/echo.c [new file with mode: 0644]
usr/src/bin/sh/builtins.def [new file with mode: 0644]
usr/src/bin/sh/cd.c [new file with mode: 0644]
usr/src/bin/sh/error.c [new file with mode: 0644]
usr/src/bin/sh/error.h [new file with mode: 0644]
usr/src/bin/sh/eval.c [new file with mode: 0644]
usr/src/bin/sh/eval.h [new file with mode: 0644]
usr/src/bin/sh/exec.c [new file with mode: 0644]
usr/src/bin/sh/exec.h [new file with mode: 0644]
usr/src/bin/sh/expand.c [new file with mode: 0644]
usr/src/bin/sh/expand.h [new file with mode: 0644]
usr/src/bin/sh/funcs/cmv [new file with mode: 0644]
usr/src/bin/sh/funcs/dirs [new file with mode: 0644]
usr/src/bin/sh/funcs/kill [new file with mode: 0644]
usr/src/bin/sh/funcs/login [new file with mode: 0644]
usr/src/bin/sh/funcs/newgrp [new file with mode: 0644]
usr/src/bin/sh/funcs/popd [new file with mode: 0644]
usr/src/bin/sh/funcs/pushd [new file with mode: 0644]
usr/src/bin/sh/funcs/suspend [new file with mode: 0644]
usr/src/bin/sh/histedit.c [new file with mode: 0644]
usr/src/bin/sh/init.h [new file with mode: 0644]
usr/src/bin/sh/input.c [new file with mode: 0644]
usr/src/bin/sh/input.h [new file with mode: 0644]
usr/src/bin/sh/jobs.c [new file with mode: 0644]
usr/src/bin/sh/jobs.h [new file with mode: 0644]
usr/src/bin/sh/machdep.h [new file with mode: 0644]
usr/src/bin/sh/mail.c [new file with mode: 0644]
usr/src/bin/sh/mail.h [new file with mode: 0644]
usr/src/bin/sh/main.c [new file with mode: 0644]
usr/src/bin/sh/main.h [new file with mode: 0644]
usr/src/bin/sh/memalloc.c [new file with mode: 0644]
usr/src/bin/sh/memalloc.h [new file with mode: 0644]
usr/src/bin/sh/miscbltin.c [new file with mode: 0644]
usr/src/bin/sh/mkbuiltins [new file with mode: 0644]
usr/src/bin/sh/mkinit.c [new file with mode: 0644]
usr/src/bin/sh/mknodes.c [new file with mode: 0644]
usr/src/bin/sh/mksyntax.c [new file with mode: 0644]
usr/src/bin/sh/mktokens [new file with mode: 0644]
usr/src/bin/sh/myhistedit.h [new file with mode: 0644]
usr/src/bin/sh/mystring.c [new file with mode: 0644]
usr/src/bin/sh/mystring.h [new file with mode: 0644]
usr/src/bin/sh/nodes.c.pat [new file with mode: 0644]
usr/src/bin/sh/nodetypes [new file with mode: 0644]
usr/src/bin/sh/options.c [new file with mode: 0644]
usr/src/bin/sh/options.h [new file with mode: 0644]
usr/src/bin/sh/output.c [new file with mode: 0644]
usr/src/bin/sh/output.h [new file with mode: 0644]
usr/src/bin/sh/parser.c [new file with mode: 0644]
usr/src/bin/sh/parser.h [new file with mode: 0644]
usr/src/bin/sh/redir.c [new file with mode: 0644]
usr/src/bin/sh/redir.h [new file with mode: 0644]
usr/src/bin/sh/sh.1 [new file with mode: 0644]
usr/src/bin/sh/shell.h [new file with mode: 0644]
usr/src/bin/sh/show.c [new file with mode: 0644]
usr/src/bin/sh/show.h [new file with mode: 0644]
usr/src/bin/sh/trap.c [new file with mode: 0644]
usr/src/bin/sh/trap.h [new file with mode: 0644]
usr/src/bin/sh/var.c [new file with mode: 0644]
usr/src/bin/sh/var.h [new file with mode: 0644]

diff --git a/usr/src/bin/sh/Makefile b/usr/src/bin/sh/Makefile
new file mode 100644 (file)
index 0000000..efa2fe7
--- /dev/null
@@ -0,0 +1,42 @@
+#      @(#)Makefile    8.4 (Berkeley) 5/5/95
+
+PROG=  sh
+SRCS=  alias.c builtins.c cd.c echo.c error.c eval.c exec.c expand.c \
+       histedit.c input.c jobs.c mail.c main.c memalloc.c miscbltin.c \
+       mystring.c nodes.c options.c parser.c redir.c show.c syntax.c \
+       trap.c output.c var.c
+OBJS+= init.o arith.o arith_lex.o
+LDADD+=        -ll -ledit -ltermcap
+LFLAGS= -8     # 8-bit lex scanner for arithmetic
+CFLAGS+=-DSHELL -I. -I${.CURDIR}
+.PATH: ${.CURDIR}/bltin ${.CURDIR}/../../usr.bin/printf
+CLEANFILES+=\
+       builtins.c builtins.h init.c mkinit mknodes mksyntax \
+       nodes.c nodes.h printf.o syntax.c syntax.h token.def y.tab.h
+
+.depend parser.o: token.def
+token.def: mktokens
+       sh ${.CURDIR}/mktokens
+
+builtins.h builtins.c: ${.CURDIR}/mkbuiltins ${.CURDIR}/builtins.def
+       cd ${.CURDIR}; sh mkbuiltins ${.OBJDIR}
+
+init.c: mkinit ${SRCS}
+       ./mkinit '${CC} -c ${CFLAGS} init.c' ${.ALLSRC:S/^mkinit$//}
+
+mkinit: ${.CURDIR}/mkinit.c
+       ${CC} ${CFLAGS} ${.CURDIR}/mkinit.c -o $@
+
+nodes.c nodes.h: mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat
+       ./mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat
+
+mknodes: ${.CURDIR}/mknodes.c
+       ${CC} ${CFLAGS} ${.CURDIR}/mknodes.c -o $@
+
+syntax.c syntax.h: mksyntax
+       ./mksyntax
+
+mksyntax: ${.CURDIR}/mksyntax.c ${.CURDIR}/parser.h
+       ${CC} ${CFLAGS} ${.CURDIR}/mksyntax.c -o $@
+
+.include <bsd.prog.mk>
diff --git a/usr/src/bin/sh/alias.c b/usr/src/bin/sh/alias.c
new file mode 100644 (file)
index 0000000..281444b
--- /dev/null
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)alias.c    8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include "shell.h"
+#include "input.h"
+#include "output.h"
+#include "error.h"
+#include "memalloc.h"
+#include "mystring.h"
+#include "alias.h"
+#include "options.h"   /* XXX for argptr (should remove?) */
+
+#define ATABSIZE 39
+
+struct alias *atab[ATABSIZE];
+
+STATIC void setalias __P((char *, char *));
+STATIC int unalias __P((char *));
+STATIC struct alias **hashalias __P((char *));
+
+STATIC
+void
+setalias(name, val)
+       char *name, *val;
+{
+       struct alias *ap, **app;
+
+       app = hashalias(name);
+       for (ap = *app; ap; ap = ap->next) {
+               if (equal(name, ap->name)) {
+                       INTOFF;
+                       ckfree(ap->val);
+                       ap->val = savestr(val);
+                       INTON;
+                       return;
+               }
+       }
+       /* not found */
+       INTOFF;
+       ap = ckmalloc(sizeof (struct alias));
+       ap->name = savestr(name);
+       /*
+        * XXX - HACK: in order that the parser will not finish reading the
+        * alias value off the input before processing the next alias, we
+        * dummy up an extra space at the end of the alias.  This is a crock
+        * and should be re-thought.  The idea (if you feel inclined to help)
+        * is to avoid alias recursions.  The mechanism used is: when
+        * expanding an alias, the value of the alias is pushed back on the
+        * input as a string and a pointer to the alias is stored with the
+        * string.  The alias is marked as being in use.  When the input
+        * routine finishes reading the string, it markes the alias not
+        * in use.  The problem is synchronization with the parser.  Since
+        * it reads ahead, the alias is marked not in use before the
+        * resulting token(s) is next checked for further alias sub.  The
+        * H A C K is that we add a little fluff after the alias value
+        * so that the string will not be exhausted.  This is a good
+        * idea ------- ***NOT***
+        */
+#ifdef notyet
+       ap->val = savestr(val);
+#else /* hack */
+       {
+       int len = strlen(val);
+       ap->val = ckmalloc(len + 2);
+       memcpy(ap->val, val, len);
+       ap->val[len] = ' ';     /* fluff */
+       ap->val[len+1] = '\0';
+       }
+#endif
+       ap->next = *app;
+       *app = ap;
+       INTON;
+}
+
+STATIC int
+unalias(name)
+       char *name;
+       {
+       struct alias *ap, **app;
+
+       app = hashalias(name);
+
+       for (ap = *app; ap; app = &(ap->next), ap = ap->next) {
+               if (equal(name, ap->name)) {
+                       /*
+                        * if the alias is currently in use (i.e. its
+                        * buffer is being used by the input routine) we
+                        * just null out the name instead of freeing it.
+                        * We could clear it out later, but this situation
+                        * is so rare that it hardly seems worth it.
+                        */
+                       if (ap->flag & ALIASINUSE)
+                               *ap->name = '\0';
+                       else {
+                               INTOFF;
+                               *app = ap->next;
+                               ckfree(ap->name);
+                               ckfree(ap->val);
+                               ckfree(ap);
+                               INTON;
+                       }
+                       return (0);
+               }
+       }
+
+       return (1);
+}
+
+#ifdef mkinit
+MKINIT void rmaliases();
+
+SHELLPROC {
+       rmaliases();
+}
+#endif
+
+void
+rmaliases() {
+       struct alias *ap, *tmp;
+       int i;
+
+       INTOFF;
+       for (i = 0; i < ATABSIZE; i++) {
+               ap = atab[i];
+               atab[i] = NULL;
+               while (ap) {
+                       ckfree(ap->name);
+                       ckfree(ap->val);
+                       tmp = ap;
+                       ap = ap->next;
+                       ckfree(tmp);
+               }
+       }
+       INTON;
+}
+
+struct alias *
+lookupalias(name, check)
+       char *name;
+       int check;
+{
+       struct alias *ap = *hashalias(name);
+
+       for (; ap; ap = ap->next) {
+               if (equal(name, ap->name)) {
+                       if (check && (ap->flag & ALIASINUSE))
+                               return (NULL);
+                       return (ap);
+               }
+       }
+
+       return (NULL);
+}
+
+/*
+ * TODO - sort output
+ */
+int
+aliascmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       char *n, *v;
+       int ret = 0;
+       struct alias *ap;
+
+       if (argc == 1) {
+               int i;
+
+               for (i = 0; i < ATABSIZE; i++)
+                       for (ap = atab[i]; ap; ap = ap->next) {
+                               if (*ap->name != '\0')
+                                   out1fmt("alias %s=%s\n", ap->name, ap->val);
+                       }
+               return (0);
+       }
+       while ((n = *++argv) != NULL) {
+               if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */
+                       if ((ap = lookupalias(n, 0)) == NULL) {
+                               outfmt(out2, "alias: %s not found\n", n);
+                               ret = 1;
+                       } else
+                               out1fmt("alias %s=%s\n", n, ap->val);
+               else {
+                       *v++ = '\0';
+                       setalias(n, v);
+               }
+       }
+
+       return (ret);
+}
+
+int
+unaliascmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       int i;
+       
+       while ((i = nextopt("a")) != '\0') {
+               if (i == 'a') {
+                       rmaliases();
+                       return (0);
+               }
+       }
+       for (i = 0; *argptr; argptr++)
+               i = unalias(*argptr);
+
+       return (i);
+}
+
+STATIC struct alias **
+hashalias(p)
+       register char *p;
+       {
+       unsigned int hashval;
+
+       hashval = *p << 4;
+       while (*p)
+               hashval+= *p++;
+       return &atab[hashval % ATABSIZE];
+}
diff --git a/usr/src/bin/sh/alias.h b/usr/src/bin/sh/alias.h
new file mode 100644 (file)
index 0000000..04b5cda
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 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.
+ *
+ *     @(#)alias.h     8.2 (Berkeley) 5/4/95
+ */
+
+#define ALIASINUSE     1
+
+struct alias {
+       struct alias *next;
+       char *name;
+       char *val;
+       int flag;
+};
+
+struct alias *lookupalias __P((char *, int));
+int aliascmd __P((int, char **));
+int unaliascmd __P((int, char **));
+void rmaliases __P((void));
diff --git a/usr/src/bin/sh/arith.h b/usr/src/bin/sh/arith.h
new file mode 100644 (file)
index 0000000..d1d8376
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 1995
+ *      The Regents of the University of California.  All rights reserved.
+ *
+ * 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.
+ *
+ *     @(#)arith.h     1.1 (Berkeley) 5/4/95
+ */
+
+int arith __P((char *));
+int expcmd __P((int , char **));
diff --git a/usr/src/bin/sh/arith.y b/usr/src/bin/sh/arith.y
new file mode 100644 (file)
index 0000000..9cd2c1f
--- /dev/null
@@ -0,0 +1,180 @@
+%token ARITH_NUM ARITH_LPAREN ARITH_RPAREN 
+
+%left ARITH_OR
+%left ARITH_AND
+%left ARITH_BOR
+%left ARITH_BXOR
+%left ARITH_BAND
+%left ARITH_EQ ARITH_NE
+%left ARITH_LT ARITH_GT ARITH_GE ARITH_LE
+%left ARITH_LSHIFT ARITH_RSHIFT
+%left ARITH_ADD ARITH_SUB
+%left ARITH_MUL ARITH_DIV ARITH_REM
+%left ARITH_UNARYMINUS ARITH_UNARYPLUS ARITH_NOT ARITH_BNOT
+%%
+
+exp:   expr = {
+                       return ($1);
+               }
+       ;
+
+
+expr:  ARITH_LPAREN expr ARITH_RPAREN = { $$ = $2; }
+       | expr ARITH_OR expr    = { $$ = $1 ? $1 : $3 ? $3 : 0; }
+       | expr ARITH_AND expr   = { $$ = $1 ? ( $3 ? $3 : 0 ) : 0; }
+       | expr ARITH_BOR expr   = { $$ = $1 | $3; }
+       | expr ARITH_BXOR expr  = { $$ = $1 ^ $3; }
+       | expr ARITH_BAND expr  = { $$ = $1 & $3; }
+       | expr ARITH_EQ expr    = { $$ = $1 == $3; }
+       | expr ARITH_GT expr    = { $$ = $1 > $3; }
+       | expr ARITH_GE expr    = { $$ = $1 >= $3; }
+       | expr ARITH_LT expr    = { $$ = $1 < $3; }
+       | expr ARITH_LE expr    = { $$ = $1 <= $3; }
+       | expr ARITH_NE expr    = { $$ = $1 != $3; }
+       | expr ARITH_LSHIFT expr = { $$ = $1 << $3; }
+       | expr ARITH_RSHIFT expr = { $$ = $1 >> $3; }
+       | expr ARITH_ADD expr   = { $$ = $1 + $3; }
+       | expr ARITH_SUB expr   = { $$ = $1 - $3; }
+       | expr ARITH_MUL expr   = { $$ = $1 * $3; }
+       | expr ARITH_DIV expr   = {
+                       if ($3 == 0)
+                               yyerror("division by zero");
+                       $$ = $1 / $3; 
+                       }
+       | expr ARITH_REM expr   = {
+                       if ($3 == 0)
+                               yyerror("division by zero");
+                       $$ = $1 % $3; 
+                       }
+       | ARITH_NOT expr        = { $$ = !($2); }
+       | ARITH_BNOT expr       = { $$ = ~($2); }
+       | ARITH_SUB expr %prec ARITH_UNARYMINUS = { $$ = -($2); }
+       | ARITH_ADD expr %prec ARITH_UNARYPLUS = { $$ = $2; }
+       | ARITH_NUM
+       ;
+%%
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arith.y    8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include "shell.h"
+#include "error.h"
+#include "output.h"
+#include "memalloc.h"
+
+char *arith_buf, *arith_startbuf;
+
+int
+arith(s)
+       char *s; 
+{
+       long result;
+
+       arith_buf = arith_startbuf = s;
+
+       INTOFF;
+       result = yyparse();
+       arith_lex_reset();      /* reprime lex */
+       INTON;
+
+       return (result);
+}
+
+void
+yyerror(s)
+       char *s;
+{
+
+       yyerrok;
+       yyclearin;
+       arith_lex_reset();      /* reprime lex */
+       error("arithmetic expression: %s: \"%s\"", s, arith_startbuf);
+}
+
+/*
+ *  The exp(1) builtin.
+ */
+int
+expcmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       char *p;
+       char *concat;
+       char **ap;
+       long i;
+
+       if (argc > 1) {
+               p = argv[1];
+               if (argc > 2) {
+                       /*
+                        * concatenate arguments
+                        */
+                       STARTSTACKSTR(concat);
+                       ap = argv + 2;
+                       for (;;) {
+                               while (*p)
+                                       STPUTC(*p++, concat);
+                               if ((p = *ap++) == NULL)
+                                       break;
+                               STPUTC(' ', concat);
+                       }
+                       STPUTC('\0', concat);
+                       p = grabstackstr(concat);
+               }
+       } else
+               p = "";
+
+       i = arith(p);
+
+       out1fmt("%d\n", i);
+       return (! i);
+}
+
+/*************************/
+#ifdef TEST_ARITH
+#include <stdio.h>
+main(argc, argv)
+       char *argv[];
+{
+       printf("%d\n", exp(argv[1]));
+}
+error(s)
+       char *s;
+{
+       fprintf(stderr, "exp: %s\n", s);
+       exit(1);
+}
+#endif
diff --git a/usr/src/bin/sh/arith_lex.l b/usr/src/bin/sh/arith_lex.l
new file mode 100644 (file)
index 0000000..cddb215
--- /dev/null
@@ -0,0 +1,80 @@
+%{
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arith_lex.l        8.3 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <unistd.h>
+#include "y.tab.h"
+#include "error.h"
+
+extern yylval;
+extern char *arith_buf, *arith_startbuf;
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max) \
+       result = (*buf = *arith_buf++) ? 1 : YY_NULL;
+%}
+
+%%
+[ \t\n]        { ; }
+[0-9]+ { yylval = atol(yytext); return(ARITH_NUM); }
+"("    { return(ARITH_LPAREN); }
+")"    { return(ARITH_RPAREN); }
+"||"   { return(ARITH_OR); }
+"&&"   { return(ARITH_AND); }
+"|"    { return(ARITH_BOR); }
+"^"    { return(ARITH_BXOR); }
+"&"    { return(ARITH_BAND); }
+"=="   { return(ARITH_EQ); }
+"!="   { return(ARITH_NE); }
+">"    { return(ARITH_GT); }
+">="   { return(ARITH_GE); }
+"<"    { return(ARITH_LT); }
+"<="   { return(ARITH_LE); }
+"<<"   { return(ARITH_LSHIFT); }
+">>"   { return(ARITH_RSHIFT); }
+"*"    { return(ARITH_MUL); }
+"/"    { return(ARITH_DIV); }
+"%"    { return(ARITH_REM); }
+"+"    { return(ARITH_ADD); } 
+"-"    { return(ARITH_SUB); }
+"~"    { return(ARITH_BNOT); }
+"!"    { return(ARITH_NOT); }
+.      { error("arith: syntax error: \"%s\"\n", arith_startbuf); }
+%%
+
+void
+arith_lex_reset() {
+       YY_NEW_FILE;
+}
diff --git a/usr/src/bin/sh/bltin/bltin.h b/usr/src/bin/sh/bltin/bltin.h
new file mode 100644 (file)
index 0000000..1a90aab
--- /dev/null
@@ -0,0 +1,69 @@
+/*-
+ * 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
+ * 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.
+ *
+ *     @(#)bltin.h     8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * This file is included by programs which are optionally built into the
+ * shell.  If SHELL is defined, we try to map the standard BSD library
+ * routines to ash routines using defines.
+ */
+
+#include "../shell.h"
+#include "../mystring.h"
+#ifdef SHELL
+#include "../output.h"
+#define stdout out1
+#define stderr out2
+#define printf out1fmt
+#define putc(c, file)  outc(c, file)
+#define putchar(c)     out1c(c)
+#define fprintf outfmt
+#define fputs outstr
+#define fflush flushout
+#define INITARGS(argv)
+#else
+#undef NULL
+#include <stdio.h>
+#undef main
+#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else
+#endif
+
+#ifdef __STDC__
+pointer stalloc(int);
+void error(char *, ...);
+#else
+pointer stalloc();
+void error();
+#endif
+
+
+extern char *commandname;
diff --git a/usr/src/bin/sh/bltin/echo.1 b/usr/src/bin/sh/bltin/echo.1
new file mode 100644 (file)
index 0000000..7a0526c
--- /dev/null
@@ -0,0 +1,107 @@
+.\" 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.
+.\" Copyright 1989 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.
+.\"
+.\"    @(#)echo.1      8.2 (Berkeley) 5/4/95
+.\"
+.Dd May 4, 1995
+.Dt ECHO 1
+.Os BSD 4.4
+.Sh NAME
+.Nm echo
+.Nd produce message in a shell script
+.Sh SYNOPSIS
+.Nm echo
+.Op Fl n | Fl e
+.Ar args... 
+.Sh DESCRIPTION
+.Nm Echo
+prints its arguments on the standard output, separated by spaces.
+Unless the
+.Fl n
+option is present, a newline is output following the arguments.
+The
+.Fl e
+option causes
+.Nm echo
+to treat the escape sequences specially, as described in the following
+paragraph.
+The
+.Fl e
+option is the default, and is provided solely for compatibility with
+other systems.
+Only one of the options
+.Fl n
+and
+.Fl e
+may be given.
+.Pp
+If any of the following sequences of characters is encountered during
+output, the sequence is not output.  Instead, the specified action is
+performed:
+.Bl -tag -width indent
+.It Li \eb
+A backspace character is output.
+.It Li \ec
+Subsequent output is suppressed.  This is normally used at the end of the
+last argument to suppress the trailing newline that
+.Nm echo
+would otherwise output.
+.It Li \ef
+Output a form feed.
+.It Li \en
+Output a newline character.
+.It Li \er
+Output a carriage return.
+.It Li \et
+Output a (horizontal) tab character.
+.It Li \ev
+Output a vertical tab.
+.It Li \e0 Ns Ar digits
+Output the character whose value is given by zero to three digits.
+If there are zero digits, a nul character is output.
+.It Li \e\e
+Output a backslash.
+.El
+.Sh HINTS
+Remember that backslash is special to the shell and needs to be escaped.
+To output a message to standard error, say
+.Pp
+.D1  echo message >&2
+.Sh BUGS
+The octal character escape mechanism
+.Pq Li \e0 Ns Ar digits
+differs from the
+C language mechanism.
+.Pp
+There is no way to force
+.Nm echo
+to treat its arguments literally, rather than interpreting them as
+options and escape sequences.
diff --git a/usr/src/bin/sh/bltin/echo.c b/usr/src/bin/sh/bltin/echo.c
new file mode 100644 (file)
index 0000000..df9e927
--- /dev/null
@@ -0,0 +1,104 @@
+/*-
+ * 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.
+ *
+ *     @(#)echo.c      8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * Echo command.
+ */
+
+#define main echocmd
+
+#include "bltin.h"
+
+/* #define eflag 1 */
+
+int
+main(argc, argv)
+       int argc;
+       char **argv;
+{
+       register char **ap;
+       register char *p;
+       register char c;
+       int count;
+       int nflag = 0;
+#ifndef eflag
+       int eflag = 0;
+#endif
+
+       ap = argv;
+       if (argc)
+               ap++;
+       if ((p = *ap) != NULL) {
+               if (equal(p, "-n")) {
+                       nflag++;
+                       ap++;
+               } else if (equal(p, "-e")) {
+#ifndef eflag
+                       eflag++;
+#endif
+                       ap++;
+               }
+       }
+       while ((p = *ap++) != NULL) {
+               while ((c = *p++) != '\0') {
+                       if (c == '\\' && eflag) {
+                               switch (*p++) {
+                               case 'b':  c = '\b';  break;
+                               case 'c':  return 0;            /* exit */
+                               case 'f':  c = '\f';  break;
+                               case 'n':  c = '\n';  break;
+                               case 'r':  c = '\r';  break;
+                               case 't':  c = '\t';  break;
+                               case 'v':  c = '\v';  break;
+                               case '\\':  break;              /* c = '\\' */
+                               case '0':
+                                       c = 0;
+                                       count = 3;
+                                       while (--count >= 0 && (unsigned)(*p - '0') < 8)
+                                               c = (c << 3) + (*p++ - '0');
+                                       break;
+                               default:
+                                       p--;
+                                       break;
+                               }
+                       }
+                       putchar(c);
+               }
+               if (*ap)
+                       putchar(' ');
+       }
+       if (! nflag)
+               putchar('\n');
+       return 0;
+}
diff --git a/usr/src/bin/sh/builtins.def b/usr/src/bin/sh/builtins.def
new file mode 100644 (file)
index 0000000..c13dc88
--- /dev/null
@@ -0,0 +1,86 @@
+#!/bin/sh -
+#
+# 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.
+#
+#      @(#)builtins.def        8.4 (Berkeley) 5/4/95
+
+#
+# This file lists all the builtin commands.  The first column is the name
+# of a C routine.  The -j flag, if present, specifies that this command
+# is to be excluded from systems without job control, and the -h flag,
+# if present specifies that this command is to be excluded from systems
+# based on the NO_HISTORY compile-time symbol.  The rest of the line
+# specifies the command name or names used to run the command.  The entry
+# for bltincmd, which is run when the user does not specify a command, must
+# come first.
+#
+# NOTE: bltincmd must come first!
+
+bltincmd       command
+#alloccmd      alloc
+bgcmd -j       bg
+breakcmd       break continue
+#catfcmd       catf
+cdcmd          cd chdir
+dotcmd         .
+echocmd                echo
+evalcmd                eval
+execcmd                exec
+exitcmd                exit
+expcmd         exp let
+exportcmd      export readonly
+#exprcmd       expr test [
+falsecmd       false
+histcmd -h     fc
+fgcmd -j       fg
+getoptscmd     getopts
+hashcmd                hash
+jobidcmd       jobid
+jobscmd                jobs
+#linecmd               line
+localcmd       local
+#nlechocmd     nlecho
+#printfcmd     printf
+pwdcmd         pwd
+readcmd                read
+returncmd      return
+setcmd         set
+setvarcmd      setvar
+shiftcmd       shift
+trapcmd                trap
+truecmd                : true
+umaskcmd       umask
+unaliascmd     unalias
+unsetcmd       unset
+waitcmd                wait
+#foocmd                foo
+aliascmd       alias
+ulimitcmd      ulimit
diff --git a/usr/src/bin/sh/cd.c b/usr/src/bin/sh/cd.c
new file mode 100644 (file)
index 0000000..07f76e1
--- /dev/null
@@ -0,0 +1,372 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cd.c       8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+/*
+ * The cd and pwd commands.
+ */
+
+#include "shell.h"
+#include "var.h"
+#include "nodes.h"     /* for jobs.h */
+#include "jobs.h"
+#include "options.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "redir.h"
+#include "mystring.h"
+#include "show.h"
+
+STATIC int docd __P((char *, int));
+STATIC char *getcomponent __P((void));
+STATIC void updatepwd __P((char *));
+STATIC void getpwd __P((void));
+
+char *curdir;                  /* current working directory */
+char *prevdir;                 /* previous working directory */
+STATIC char *cdcomppath;
+
+int
+cdcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       char *dest;
+       char *path;
+       char *p;
+       struct stat statb;
+       char *padvance();
+       int print = 0;
+
+       nextopt(nullstr);
+       if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
+               error("HOME not set");
+       if (dest[0] == '-' && dest[1] == '\0') {
+               dest = prevdir ? prevdir : curdir;
+               print = 1;
+       }
+       if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL)
+               path = nullstr;
+       while ((p = padvance(&path, dest)) != NULL) {
+               if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+                       if (!print) {
+                               /*
+                                * XXX - rethink
+                                */
+                               if (p[0] == '.' && p[1] == '/')
+                                       p += 2;
+                               print = strcmp(p, dest);
+                       }
+                       if (docd(p, print) >= 0)
+                               return 0;
+
+               }
+       }
+       error("can't cd to %s", dest);
+       /*NOTREACHED*/
+       return 0;
+}
+
+
+/*
+ * Actually do the chdir.  If the name refers to symbolic links, we
+ * compute the actual directory name before doing the cd.  In an
+ * interactive shell, print the directory name if "print" is nonzero
+ * or if the name refers to a symbolic link.  We also print the name
+ * if "/u/logname" was expanded in it, since this is similar to a
+ * symbolic link.  (The check for this breaks if the user gives the
+ * cd command some additional, unused arguments.)
+ */
+
+#if SYMLINKS == 0
+STATIC int
+docd(dest, print)
+       char *dest;
+       {
+       INTOFF;
+       if (chdir(dest) < 0) {
+               INTON;
+               return -1;
+       }
+       updatepwd(dest);
+       INTON;
+       if (print && iflag)
+               out1fmt("%s\n", stackblock());
+       return 0;
+}
+
+#else
+
+
+
+STATIC int
+docd(dest, print)
+       char *dest;
+       int print;
+{
+       register char *p;
+       register char *q;
+       char *symlink;
+       char *component;
+       struct stat statb;
+       int first;
+       int i;
+
+       TRACE(("docd(\"%s\", %d) called\n", dest, print));
+
+top:
+       cdcomppath = dest;
+       STARTSTACKSTR(p);
+       if (*dest == '/') {
+               STPUTC('/', p);
+               cdcomppath++;
+       }
+       first = 1;
+       while ((q = getcomponent()) != NULL) {
+               if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
+                       continue;
+               if (! first)
+                       STPUTC('/', p);
+               first = 0;
+               component = q;
+               while (*q)
+                       STPUTC(*q++, p);
+               if (equal(component, ".."))
+                       continue;
+               STACKSTRNUL(p);
+               if (lstat(stackblock(), &statb) < 0)
+                       error("lstat %s failed", stackblock());
+               if (!S_ISLNK(statb.st_mode))
+                       continue;
+
+               /* Hit a symbolic link.  We have to start all over again. */
+               print = 1;
+               STPUTC('\0', p);
+               symlink = grabstackstr(p);
+               i = (int)statb.st_size + 2;             /* 2 for '/' and '\0' */
+               if (cdcomppath != NULL)
+                       i += strlen(cdcomppath);
+               p = stalloc(i);
+               if (readlink(symlink, p, (int)statb.st_size) < 0) {
+                       error("readlink %s failed", stackblock());
+               }
+               if (cdcomppath != NULL) {
+                       p[(int)statb.st_size] = '/';
+                       scopy(cdcomppath, p + (int)statb.st_size + 1);
+               } else {
+                       p[(int)statb.st_size] = '\0';
+               }
+               if (p[0] != '/') {      /* relative path name */
+                       char *r;
+                       q = r = symlink;
+                       while (*q) {
+                               if (*q++ == '/')
+                                       r = q;
+                       }
+                       *r = '\0';
+                       dest = stalloc(strlen(symlink) + strlen(p) + 1);
+                       scopy(symlink, dest);
+                       strcat(dest, p);
+               } else {
+                       dest = p;
+               }
+               goto top;
+       }
+       STPUTC('\0', p);
+       p = grabstackstr(p);
+       INTOFF;
+       if (chdir(p) < 0) {
+               INTON;
+               return -1;
+       }
+       updatepwd(p);
+       INTON;
+       if (print && iflag)
+               out1fmt("%s\n", p);
+       return 0;
+}
+#endif /* SYMLINKS */
+
+
+
+/*
+ * Get the next component of the path name pointed to by cdcomppath.
+ * This routine overwrites the string pointed to by cdcomppath.
+ */
+
+STATIC char *
+getcomponent() {
+       register char *p;
+       char *start;
+
+       if ((p = cdcomppath) == NULL)
+               return NULL;
+       start = cdcomppath;
+       while (*p != '/' && *p != '\0')
+               p++;
+       if (*p == '\0') {
+               cdcomppath = NULL;
+       } else {
+               *p++ = '\0';
+               cdcomppath = p;
+       }
+       return start;
+}
+
+
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.  We also call hashcd to let the routines in exec.c know
+ * that the current directory has changed.
+ */
+
+void hashcd();
+
+STATIC void
+updatepwd(dir)
+       char *dir;
+       {
+       char *new;
+       char *p;
+
+       hashcd();                               /* update command hash table */
+       cdcomppath = stalloc(strlen(dir) + 1);
+       scopy(dir, cdcomppath);
+       STARTSTACKSTR(new);
+       if (*dir != '/') {
+               if (curdir == NULL)
+                       return;
+               p = curdir;
+               while (*p)
+                       STPUTC(*p++, new);
+               if (p[-1] == '/')
+                       STUNPUTC(new);
+       }
+       while ((p = getcomponent()) != NULL) {
+               if (equal(p, "..")) {
+                       while (new > stackblock() && (STUNPUTC(new), *new) != '/');
+               } else if (*p != '\0' && ! equal(p, ".")) {
+                       STPUTC('/', new);
+                       while (*p)
+                               STPUTC(*p++, new);
+               }
+       }
+       if (new == stackblock())
+               STPUTC('/', new);
+       STACKSTRNUL(new);
+       INTOFF;
+       if (prevdir)
+               ckfree(prevdir);
+       prevdir = curdir;
+       curdir = savestr(stackblock());
+       INTON;
+}
+
+
+
+int
+pwdcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       getpwd();
+       out1str(curdir);
+       out1c('\n');
+       return 0;
+}
+
+
+
+/*
+ * Run /bin/pwd to find out what the current directory is.  We suppress
+ * interrupts throughout most of this, but the user can still break out
+ * of it by killing the pwd program.  If we already know the current
+ * directory, this routine returns immediately.
+ */
+
+#define MAXPWD 256
+
+STATIC void
+getpwd() {
+       char buf[MAXPWD];
+       char *p;
+       int i;
+       int status;
+       struct job *jp;
+       int pip[2];
+
+       if (curdir)
+               return;
+       INTOFF;
+       if (pipe(pip) < 0)
+               error("Pipe call failed");
+       jp = makejob((union node *)NULL, 1);
+       if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) {
+               close(pip[0]);
+               if (pip[1] != 1) {
+                       close(1);
+                       copyfd(pip[1], 1);
+                       close(pip[1]);
+               }
+               execl("/bin/pwd", "pwd", (char *)0);
+               error("Cannot exec /bin/pwd");
+       }
+       close(pip[1]);
+       pip[1] = -1;
+       p = buf;
+       while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0
+            || (i == -1 && errno == EINTR)) {
+               if (i > 0)
+                       p += i;
+       }
+       close(pip[0]);
+       pip[0] = -1;
+       status = waitforjob(jp);
+       if (status != 0)
+               error((char *)0);
+       if (i < 0 || p == buf || p[-1] != '\n')
+               error("pwd command failed");
+       p[-1] = '\0';
+       curdir = savestr(buf);
+       INTON;
+}
diff --git a/usr/src/bin/sh/error.c b/usr/src/bin/sh/error.c
new file mode 100644 (file)
index 0000000..028b4c8
--- /dev/null
@@ -0,0 +1,252 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)error.c    8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * Errors and exceptions.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "options.h"
+#include "output.h"
+#include "error.h"
+#include "show.h"
+#include <signal.h>
+#include <unistd.h>
+#include <errno.h>
+
+
+/*
+ * Code to handle exceptions in C.
+ */
+
+struct jmploc *handler;
+int exception;
+volatile int suppressint;
+volatile int intpending;
+char *commandname;
+
+
+/*
+ * Called to raise an exception.  Since C doesn't include exceptions, we
+ * just do a longjmp to the exception handler.  The type of exception is
+ * stored in the global variable "exception".
+ */
+
+void
+exraise(e) 
+       int e;
+{
+       if (handler == NULL)
+               abort();
+       exception = e;
+       longjmp(handler->loc, 1);
+}
+
+
+/*
+ * Called from trap.c when a SIGINT is received.  (If the user specifies
+ * that SIGINT is to be trapped or ignored using the trap builtin, then
+ * this routine is not called.)  Suppressint is nonzero when interrupts
+ * are held using the INTOFF macro.  The call to _exit is necessary because
+ * there is a short period after a fork before the signal handlers are
+ * set to the appropriate value for the child.  (The test for iflag is
+ * just defensive programming.)
+ */
+
+void
+onint() {
+       sigset_t sigset;
+
+       if (suppressint) {
+               intpending++;
+               return;
+       }
+       intpending = 0;
+       sigemptyset(&sigset);
+       sigprocmask(SIG_SETMASK, &sigset, NULL);
+       if (rootshell && iflag)
+               exraise(EXINT);
+       else
+               _exit(128 + SIGINT);
+}
+
+
+
+void
+error2(a, b)
+       char *a, *b;
+       {
+       error("%s: %s", a, b);
+}
+
+
+/*
+ * Error is called to raise the error exception.  If the first argument
+ * is not NULL then error prints an error message using printf style
+ * formatting.  It then raises the error exception.
+ */
+
+#if __STDC__
+void
+error(char *msg, ...)
+#else
+void
+error(va_alist)
+       va_dcl
+#endif
+{
+#if !__STDC__
+       char *msg;
+#endif
+       va_list ap;
+       CLEAR_PENDING_INT;
+       INTOFF;
+
+#if __STDC__
+       va_start(ap, msg);
+#else
+       va_start(ap);
+       msg = va_arg(ap, char *);
+#endif
+#ifdef DEBUG
+       if (msg)
+               TRACE(("error(\"%s\") pid=%d\n", msg, getpid()));
+       else
+               TRACE(("error(NULL) pid=%d\n", getpid()));
+#endif
+       if (msg) {
+               if (commandname)
+                       outfmt(&errout, "%s: ", commandname);
+               doformat(&errout, msg, ap);
+               out2c('\n');
+       }
+       va_end(ap);
+       flushall();
+       exraise(EXERROR);
+}
+
+
+
+/*
+ * Table of error messages.
+ */
+
+struct errname {
+       short errcode;          /* error number */
+       short action;           /* operation which encountered the error */
+       char *msg;              /* text describing the error */
+};
+
+
+#define ALL (E_OPEN|E_CREAT|E_EXEC)
+
+STATIC const struct errname errormsg[] = {
+       { EINTR,        ALL,    "interrupted" },
+       { EACCES,       ALL,    "permission denied" },
+       { EIO,          ALL,    "I/O error" },
+       { ENOENT,       E_OPEN, "no such file" },
+       { ENOENT,       E_CREAT,"directory nonexistent" },
+       { ENOENT,       E_EXEC, "not found" },
+       { ENOTDIR,      E_OPEN, "no such file" },
+       { ENOTDIR,      E_CREAT,"directory nonexistent" },
+       { ENOTDIR,      E_EXEC, "not found" },
+       { EISDIR,       ALL,    "is a directory" },
+#ifdef notdef
+       { EMFILE,       ALL,    "too many open files" },
+#endif
+       { ENFILE,       ALL,    "file table overflow" },
+       { ENOSPC,       ALL,    "file system full" },
+#ifdef EDQUOT
+       { EDQUOT,       ALL,    "disk quota exceeded" },
+#endif
+#ifdef ENOSR
+       { ENOSR,        ALL,    "no streams resources" },
+#endif
+       { ENXIO,        ALL,    "no such device or address" },
+       { EROFS,        ALL,    "read-only file system" },
+       { ETXTBSY,      ALL,    "text busy" },
+#ifdef SYSV
+       { EAGAIN,       E_EXEC, "not enough memory" },
+#endif
+       { ENOMEM,       ALL,    "not enough memory" },
+#ifdef ENOLINK
+       { ENOLINK,      ALL,    "remote access failed" },
+#endif
+#ifdef EMULTIHOP
+       { EMULTIHOP,    ALL,    "remote access failed" },
+#endif
+#ifdef ECOMM
+       { ECOMM,        ALL,    "remote access failed" },
+#endif
+#ifdef ESTALE
+       { ESTALE,       ALL,    "remote access failed" },
+#endif
+#ifdef ETIMEDOUT
+       { ETIMEDOUT,    ALL,    "remote access failed" },
+#endif
+#ifdef ELOOP
+       { ELOOP,        ALL,    "symbolic link loop" },
+#endif
+       { E2BIG,        E_EXEC, "argument list too long" },
+#ifdef ELIBACC
+       { ELIBACC,      E_EXEC, "shared library missing" },
+#endif
+       { 0,            0,      NULL },
+};
+
+
+/*
+ * Return a string describing an error.  The returned string may be a
+ * pointer to a static buffer that will be overwritten on the next call.
+ * Action describes the operation that got the error.
+ */
+
+char *
+errmsg(e, action) 
+       int e;
+       int action;
+{
+       struct errname const *ep;
+       static char buf[12];
+
+       for (ep = errormsg ; ep->errcode ; ep++) {
+               if (ep->errcode == e && (ep->action & action) != 0)
+                       return ep->msg;
+       }
+       fmtstr(buf, sizeof buf, "error %d", e);
+       return buf;
+}
diff --git a/usr/src/bin/sh/error.h b/usr/src/bin/sh/error.h
new file mode 100644 (file)
index 0000000..22ad709
--- /dev/null
@@ -0,0 +1,101 @@
+/*-
+ * 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.
+ *
+ *     @(#)error.h     8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * Types of operations (passed to the errmsg routine).
+ */
+
+#define E_OPEN 01      /* opening a file */
+#define E_CREAT 02     /* creating a file */
+#define E_EXEC 04      /* executing a program */
+
+
+/*
+ * We enclose jmp_buf in a structure so that we can declare pointers to
+ * jump locations.  The global variable handler contains the location to
+ * jump to when an exception occurs, and the global variable exception
+ * contains a code identifying the exeception.  To implement nested
+ * exception handlers, the user should save the value of handler on entry
+ * to an inner scope, set handler to point to a jmploc structure for the
+ * inner scope, and restore handler on exit from the scope.
+ */
+
+#include <setjmp.h>
+
+struct jmploc {
+       jmp_buf loc;
+};
+
+extern struct jmploc *handler;
+extern int exception;
+
+/* exceptions */
+#define EXINT 0                /* SIGINT received */
+#define EXERROR 1      /* a generic error */
+#define EXSHELLPROC 2  /* execute a shell procedure */
+
+
+/*
+ * These macros allow the user to suspend the handling of interrupt signals
+ * over a period of time.  This is similar to SIGHOLD to or sigblock, but
+ * much more efficient and portable.  (But hacking the kernel is so much
+ * more fun than worrying about efficiency and portability. :-))
+ */
+
+extern volatile int suppressint;
+extern volatile int intpending;
+extern char *commandname;      /* name of command--printed on error */
+
+#define INTOFF suppressint++
+#define INTON { if (--suppressint == 0 && intpending) onint(); }
+#define FORCEINTON {suppressint = 0; if (intpending) onint();}
+#define CLEAR_PENDING_INT intpending = 0
+#define int_pending() intpending
+
+void exraise __P((int));
+void onint __P((void));
+void error2 __P((char *, char *));
+void error __P((char *, ...));
+char *errmsg __P((int, int));
+
+
+/*
+ * BSD setjmp saves the signal mask, which violates ANSI C and takes time,
+ * so we use _setjmp instead.
+ */
+
+#ifdef BSD
+#define setjmp(jmploc) _setjmp(jmploc)
+#define longjmp(jmploc, val)   _longjmp(jmploc, val)
+#endif
diff --git a/usr/src/bin/sh/eval.c b/usr/src/bin/sh/eval.c
new file mode 100644 (file)
index 0000000..a888f36
--- /dev/null
@@ -0,0 +1,998 @@
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)eval.c     8.9 (Berkeley) 6/8/95";
+#endif /* not lint */
+
+#include <signal.h>
+#include <unistd.h>
+
+/*
+ * Evaluate a command.
+ */
+
+#include "shell.h"
+#include "nodes.h"
+#include "syntax.h"
+#include "expand.h"
+#include "parser.h"
+#include "jobs.h"
+#include "eval.h"
+#include "builtins.h"
+#include "options.h"
+#include "exec.h"
+#include "redir.h"
+#include "input.h"
+#include "output.h"
+#include "trap.h"
+#include "var.h"
+#include "memalloc.h"
+#include "error.h"
+#include "show.h"
+#include "mystring.h"
+#ifndef NO_HISTORY
+#include "myhistedit.h"
+#endif
+
+
+/* flags in argument to evaltree */
+#define EV_EXIT 01             /* exit after evaluating tree */
+#define EV_TESTED 02           /* exit status is checked; ignore -e flag */
+#define EV_BACKCMD 04          /* command executing within back quotes */
+
+
+/* reasons for skipping commands (see comment on breakcmd routine) */
+#define SKIPBREAK 1
+#define SKIPCONT 2
+#define SKIPFUNC 3
+
+MKINIT int evalskip;           /* set if we are skipping commands */
+STATIC int skipcount;          /* number of levels to skip */
+MKINIT int loopnest;           /* current loop nesting level */
+int funcnest;                  /* depth of function calls */
+
+
+char *commandname;
+struct strlist *cmdenviron;
+int exitstatus;                        /* exit status of last command */
+int oexitstatus;               /* saved exit status */
+
+
+STATIC void evalloop __P((union node *));
+STATIC void evalfor __P((union node *));
+STATIC void evalcase __P((union node *, int));
+STATIC void evalsubshell __P((union node *, int));
+STATIC void expredir __P((union node *));
+STATIC void evalpipe __P((union node *));
+STATIC void evalcommand __P((union node *, int, struct backcmd *));
+STATIC void prehash __P((union node *));
+
+
+/*
+ * Called to reset things after an exception.
+ */
+
+#ifdef mkinit
+INCLUDE "eval.h"
+
+RESET {
+       evalskip = 0;
+       loopnest = 0;
+       funcnest = 0;
+}
+
+SHELLPROC {
+       exitstatus = 0;
+}
+#endif
+
+
+
+/*
+ * The eval commmand.
+ */
+
+int
+evalcmd(argc, argv)  
+       int argc;
+       char **argv; 
+{
+        char *p;
+        char *concat;
+        char **ap;
+
+        if (argc > 1) {
+                p = argv[1];
+                if (argc > 2) {
+                        STARTSTACKSTR(concat);
+                        ap = argv + 2;
+                        for (;;) {
+                                while (*p)
+                                        STPUTC(*p++, concat);
+                                if ((p = *ap++) == NULL)
+                                        break;
+                                STPUTC(' ', concat);
+                        }
+                        STPUTC('\0', concat);
+                        p = grabstackstr(concat);
+                }
+                evalstring(p);
+        }
+        return exitstatus;
+}
+
+
+/*
+ * Execute a command or commands contained in a string.
+ */
+
+void
+evalstring(s)
+       char *s;
+       {
+       union node *n;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       setinputstring(s, 1);
+       while ((n = parsecmd(0)) != NEOF) {
+               evaltree(n, 0);
+               popstackmark(&smark);
+       }
+       popfile();
+       popstackmark(&smark);
+}
+
+
+
+/*
+ * Evaluate a parse tree.  The value is left in the global variable
+ * exitstatus.
+ */
+
+void
+evaltree(n, flags)
+       union node *n;
+       int flags;
+{
+       if (n == NULL) {
+               TRACE(("evaltree(NULL) called\n"));
+               exitstatus = 0;
+               goto out;
+       }
+#ifndef NO_HISTORY
+       displayhist = 1;        /* show history substitutions done with fc */
+#endif
+       TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
+       switch (n->type) {
+       case NSEMI:
+               evaltree(n->nbinary.ch1, 0);
+               if (evalskip)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NAND:
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip || exitstatus != 0)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NOR:
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip || exitstatus == 0)
+                       goto out;
+               evaltree(n->nbinary.ch2, flags);
+               break;
+       case NREDIR:
+               expredir(n->nredir.redirect);
+               redirect(n->nredir.redirect, REDIR_PUSH);
+               evaltree(n->nredir.n, flags);
+               popredir();
+               break;
+       case NSUBSHELL:
+               evalsubshell(n, flags);
+               break;
+       case NBACKGND:
+               evalsubshell(n, flags);
+               break;
+       case NIF: {
+               int status; 
+
+               evaltree(n->nif.test, EV_TESTED);
+               status = exitstatus;
+               exitstatus = 0;
+               if (evalskip)
+                       goto out;
+               if (status == 0)
+                       evaltree(n->nif.ifpart, flags);
+               else if (n->nif.elsepart)
+                       evaltree(n->nif.elsepart, flags);
+               break;
+       }
+       case NWHILE:
+       case NUNTIL:
+               evalloop(n);
+               break;
+       case NFOR:
+               evalfor(n);
+               break;
+       case NCASE:
+               evalcase(n, flags);
+               break;
+       case NDEFUN:
+               defun(n->narg.text, n->narg.next);
+               exitstatus = 0;
+               break;
+       case NNOT:
+               evaltree(n->nnot.com, EV_TESTED);
+               exitstatus = !exitstatus;
+               break;
+
+       case NPIPE:
+               evalpipe(n);
+               break;
+       case NCMD:
+               evalcommand(n, flags, (struct backcmd *)NULL);
+               break;
+       default:
+               out1fmt("Node type = %d\n", n->type);
+               flushout(&output);
+               break;
+       }
+out:
+       if (pendingsigs)
+               dotrap();
+       if ((flags & EV_EXIT) || (eflag && exitstatus && !(flags & EV_TESTED)))
+               exitshell(exitstatus);
+}
+
+
+STATIC void
+evalloop(n)
+       union node *n;
+{
+       int status;
+
+       loopnest++;
+       status = 0;
+       for (;;) {
+               evaltree(n->nbinary.ch1, EV_TESTED);
+               if (evalskip) {
+skipping:        if (evalskip == SKIPCONT && --skipcount <= 0) {
+                               evalskip = 0;
+                               continue;
+                       }
+                       if (evalskip == SKIPBREAK && --skipcount <= 0)
+                               evalskip = 0;
+                       break;
+               }
+               if (n->type == NWHILE) {
+                       if (exitstatus != 0)
+                               break;
+               } else {
+                       if (exitstatus == 0)
+                               break;
+               }
+               evaltree(n->nbinary.ch2, 0);
+               status = exitstatus;
+               if (evalskip)
+                       goto skipping;
+       }
+       loopnest--;
+       exitstatus = status;
+}
+
+
+
+STATIC void
+evalfor(n)
+    union node *n;
+{
+       struct arglist arglist;
+       union node *argp;
+       struct strlist *sp;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       arglist.lastp = &arglist.list;
+       for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
+               oexitstatus = exitstatus;
+               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+               if (evalskip)
+                       goto out;
+       }
+       *arglist.lastp = NULL;
+
+       exitstatus = 0;
+       loopnest++;
+       for (sp = arglist.list ; sp ; sp = sp->next) {
+               setvar(n->nfor.var, sp->text, 0);
+               evaltree(n->nfor.body, 0);
+               if (evalskip) {
+                       if (evalskip == SKIPCONT && --skipcount <= 0) {
+                               evalskip = 0;
+                               continue;
+                       }
+                       if (evalskip == SKIPBREAK && --skipcount <= 0)
+                               evalskip = 0;
+                       break;
+               }
+       }
+       loopnest--;
+out:
+       popstackmark(&smark);
+}
+
+
+
+STATIC void
+evalcase(n, flags)
+       union node *n;
+       int flags;
+{
+       union node *cp;
+       union node *patp;
+       struct arglist arglist;
+       struct stackmark smark;
+
+       setstackmark(&smark);
+       arglist.lastp = &arglist.list;
+       oexitstatus = exitstatus;
+       expandarg(n->ncase.expr, &arglist, EXP_TILDE);
+       for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
+               for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
+                       if (casematch(patp, arglist.list->text)) {
+                               if (evalskip == 0) {
+                                       evaltree(cp->nclist.body, flags);
+                               }
+                               goto out;
+                       }
+               }
+       }
+out:
+       popstackmark(&smark);
+}
+
+
+
+/*
+ * Kick off a subshell to evaluate a tree.
+ */
+
+STATIC void
+evalsubshell(n, flags)
+       union node *n;
+       int flags;
+{
+       struct job *jp;
+       int backgnd = (n->type == NBACKGND);
+
+       expredir(n->nredir.redirect);
+       jp = makejob(n, 1);
+       if (forkshell(jp, n, backgnd) == 0) {
+               if (backgnd)
+                       flags &=~ EV_TESTED;
+               redirect(n->nredir.redirect, 0);
+               evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */
+       }
+       if (! backgnd) {
+               INTOFF;
+               exitstatus = waitforjob(jp);
+               INTON;
+       }
+}
+
+
+
+/*
+ * Compute the names of the files in a redirection list.
+ */
+
+STATIC void
+expredir(n)
+       union node *n;
+{
+       register union node *redir;
+
+       for (redir = n ; redir ; redir = redir->nfile.next) {
+               struct arglist fn;
+               fn.lastp = &fn.list;
+               oexitstatus = exitstatus;
+               switch (redir->type) {
+               case NFROM:
+               case NTO:
+               case NAPPEND:
+                       expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
+                       redir->nfile.expfname = fn.list->text;
+                       break;
+               case NFROMFD:
+               case NTOFD:
+                       if (redir->ndup.vname) {
+                               expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
+                               fixredir(redir, fn.list->text, 1);
+                       }
+                       break;
+               }
+       }
+}
+
+
+
+/*
+ * Evaluate a pipeline.  All the processes in the pipeline are children
+ * of the process creating the pipeline.  (This differs from some versions
+ * of the shell, which make the last process in a pipeline the parent
+ * of all the rest.)
+ */
+
+STATIC void
+evalpipe(n)
+       union node *n;
+{
+       struct job *jp;
+       struct nodelist *lp;
+       int pipelen;
+       int prevfd;
+       int pip[2];
+
+       TRACE(("evalpipe(0x%lx) called\n", (long)n));
+       pipelen = 0;
+       for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
+               pipelen++;
+       INTOFF;
+       jp = makejob(n, pipelen);
+       prevfd = -1;
+       for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+               prehash(lp->n);
+               pip[1] = -1;
+               if (lp->next) {
+                       if (pipe(pip) < 0) {
+                               close(prevfd);
+                               error("Pipe call failed");
+                       }
+               }
+               if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
+                       INTON;
+                       if (prevfd > 0) {
+                               close(0);
+                               copyfd(prevfd, 0);
+                               close(prevfd);
+                       }
+                       if (pip[1] >= 0) {
+                               close(pip[0]);
+                               if (pip[1] != 1) {
+                                       close(1);
+                                       copyfd(pip[1], 1);
+                                       close(pip[1]);
+                               }
+                       }
+                       evaltree(lp->n, EV_EXIT);
+               }
+               if (prevfd >= 0)
+                       close(prevfd);
+               prevfd = pip[0];
+               close(pip[1]);
+       }
+       INTON;
+       if (n->npipe.backgnd == 0) {
+               INTOFF;
+               exitstatus = waitforjob(jp);
+               TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
+               INTON;
+       }
+}
+
+
+
+/*
+ * Execute a command inside back quotes.  If it's a builtin command, we
+ * want to save its output in a block obtained from malloc.  Otherwise
+ * we fork off a subprocess and get the output of the command via a pipe.
+ * Should be called with interrupts off.
+ */
+
+void
+evalbackcmd(n, result)
+       union node *n;
+       struct backcmd *result;
+{
+       int pip[2];
+       struct job *jp;
+       struct stackmark smark;         /* unnecessary */
+
+       setstackmark(&smark);
+       result->fd = -1;
+       result->buf = NULL;
+       result->nleft = 0;
+       result->jp = NULL;
+       if (n == NULL) {
+               exitstatus = 0;
+               goto out;
+       }
+       if (n->type == NCMD) {
+               exitstatus = oexitstatus;
+               evalcommand(n, EV_BACKCMD, result);
+       } else {
+               exitstatus = 0;
+               if (pipe(pip) < 0)
+                       error("Pipe call failed");
+               jp = makejob(n, 1);
+               if (forkshell(jp, n, FORK_NOJOB) == 0) {
+                       FORCEINTON;
+                       close(pip[0]);
+                       if (pip[1] != 1) {
+                               close(1);
+                               copyfd(pip[1], 1);
+                               close(pip[1]);
+                       }
+                       evaltree(n, EV_EXIT);
+               }
+               close(pip[1]);
+               result->fd = pip[0];
+               result->jp = jp;
+       }
+out:
+       popstackmark(&smark);
+       TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
+               result->fd, result->buf, result->nleft, result->jp));
+}
+
+
+
+/*
+ * Execute a simple command.
+ */
+
+STATIC void
+evalcommand(cmd, flags, backcmd)
+       union node *cmd;
+       int flags;
+       struct backcmd *backcmd;
+{
+       struct stackmark smark;
+       union node *argp;
+       struct arglist arglist;
+       struct arglist varlist;
+       char **argv;
+       int argc;
+       char **envp;
+       int varflag;
+       struct strlist *sp;
+       int mode;
+       int pip[2];
+       struct cmdentry cmdentry;
+       struct job *jp;
+       struct jmploc jmploc;
+       struct jmploc *volatile savehandler;
+       char *volatile savecmdname;
+       volatile struct shparam saveparam;
+       struct localvar *volatile savelocalvars;
+       volatile int e;
+       char *lastarg;
+#if __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &argv;
+       (void) &argc;
+       (void) &lastarg;
+       (void) &flags;
+#endif
+
+       /* First expand the arguments. */
+       TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
+       setstackmark(&smark);
+       arglist.lastp = &arglist.list;
+       varlist.lastp = &varlist.list;
+       varflag = 1;
+       oexitstatus = exitstatus;
+       exitstatus = 0;
+       for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
+               char *p = argp->narg.text;
+               if (varflag && is_name(*p)) {
+                       do {
+                               p++;
+                       } while (is_in_name(*p));
+                       if (*p == '=') {
+                               expandarg(argp, &varlist, EXP_VARTILDE);
+                               continue;
+                       }
+               }
+               expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
+               varflag = 0;
+       }
+       *arglist.lastp = NULL;
+       *varlist.lastp = NULL;
+       expredir(cmd->ncmd.redirect);
+       argc = 0;
+       for (sp = arglist.list ; sp ; sp = sp->next)
+               argc++;
+       argv = stalloc(sizeof (char *) * (argc + 1));
+
+       for (sp = arglist.list ; sp ; sp = sp->next) {
+               TRACE(("evalcommand arg: %s\n", sp->text));
+               *argv++ = sp->text;
+       }
+       *argv = NULL;
+       lastarg = NULL;
+       if (iflag && funcnest == 0 && argc > 0)
+               lastarg = argv[-1];
+       argv -= argc;
+
+       /* Print the command if xflag is set. */
+       if (xflag) {
+               outc('+', &errout);
+               for (sp = varlist.list ; sp ; sp = sp->next) {
+                       outc(' ', &errout);
+                       out2str(sp->text);
+               }
+               for (sp = arglist.list ; sp ; sp = sp->next) {
+                       outc(' ', &errout);
+                       out2str(sp->text);
+               }
+               outc('\n', &errout);
+               flushout(&errout);
+       }
+
+       /* Now locate the command. */
+       if (argc == 0) {
+               cmdentry.cmdtype = CMDBUILTIN;
+               cmdentry.u.index = BLTINCMD;
+       } else {
+               static const char PATH[] = "PATH=";
+               char *path = pathval();
+
+               /*
+                * Modify the command lookup path, if a PATH= assignment
+                * is present
+                */
+               for (sp = varlist.list ; sp ; sp = sp->next)
+                       if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
+                               path = sp->text + sizeof(PATH) - 1;
+
+               find_command(argv[0], &cmdentry, 1, path);
+               if (cmdentry.cmdtype == CMDUNKNOWN) {   /* command not found */
+                       exitstatus = 1;
+                       flushout(&errout);
+                       return;
+               }
+               /* implement the bltin builtin here */
+               if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) {
+                       for (;;) {
+                               argv++;
+                               if (--argc == 0)
+                                       break;
+                               if ((cmdentry.u.index = find_builtin(*argv)) < 0) {
+                                       outfmt(&errout, "%s: not found\n", *argv);
+                                       exitstatus = 1;
+                                       flushout(&errout);
+                                       return;
+                               }
+                               if (cmdentry.u.index != BLTINCMD)
+                                       break;
+                       }
+               }
+       }
+
+       /* Fork off a child process if necessary. */
+       if (cmd->ncmd.backgnd
+        || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
+        || ((flags & EV_BACKCMD) != 0
+           && (cmdentry.cmdtype != CMDBUILTIN
+                || cmdentry.u.index == DOTCMD
+                || cmdentry.u.index == EVALCMD))) {
+               jp = makejob(cmd, 1);
+               mode = cmd->ncmd.backgnd;
+               if (flags & EV_BACKCMD) {
+                       mode = FORK_NOJOB;
+                       if (pipe(pip) < 0)
+                               error("Pipe call failed");
+               }
+               if (forkshell(jp, cmd, mode) != 0)
+                       goto parent;    /* at end of routine */
+               if (flags & EV_BACKCMD) {
+                       FORCEINTON;
+                       close(pip[0]);
+                       if (pip[1] != 1) {
+                               close(1);
+                               copyfd(pip[1], 1);
+                               close(pip[1]);
+                       }
+               }
+               flags |= EV_EXIT;
+       }
+
+       /* This is the child process if a fork occurred. */
+       /* Execute the command. */
+       if (cmdentry.cmdtype == CMDFUNCTION) {
+               trputs("Shell function:  ");  trargs(argv);
+               redirect(cmd->ncmd.redirect, REDIR_PUSH);
+               saveparam = shellparam;
+               shellparam.malloc = 0;
+               shellparam.nparam = argc - 1;
+               shellparam.p = argv + 1;
+               shellparam.optnext = NULL;
+               INTOFF;
+               savelocalvars = localvars;
+               localvars = NULL;
+               INTON;
+               if (setjmp(jmploc.loc)) {
+                       if (exception == EXSHELLPROC)
+                               freeparam((struct shparam *)&saveparam);
+                       else {
+                               freeparam(&shellparam);
+                               shellparam = saveparam;
+                       }
+                       poplocalvars();
+                       localvars = savelocalvars;
+                       handler = savehandler;
+                       longjmp(handler->loc, 1);
+               }
+               savehandler = handler;
+               handler = &jmploc;
+               for (sp = varlist.list ; sp ; sp = sp->next)
+                       mklocal(sp->text);
+               funcnest++;
+               evaltree(cmdentry.u.func, 0);
+               funcnest--;
+               INTOFF;
+               poplocalvars();
+               localvars = savelocalvars;
+               freeparam(&shellparam);
+               shellparam = saveparam;
+               handler = savehandler;
+               popredir();
+               INTON;
+               if (evalskip == SKIPFUNC) {
+                       evalskip = 0;
+                       skipcount = 0;
+               }
+               if (flags & EV_EXIT)
+                       exitshell(exitstatus);
+       } else if (cmdentry.cmdtype == CMDBUILTIN) {
+               trputs("builtin command:  ");  trargs(argv);
+               mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH;
+               if (flags == EV_BACKCMD) {
+                       memout.nleft = 0;
+                       memout.nextc = memout.buf;
+                       memout.bufsize = 64;
+                       mode |= REDIR_BACKQ;
+               }
+               redirect(cmd->ncmd.redirect, mode);
+               savecmdname = commandname;
+               cmdenviron = varlist.list;
+               e = -1;
+               if (setjmp(jmploc.loc)) {
+                       e = exception;
+                       exitstatus = (e == EXINT)? SIGINT+128 : 2;
+                       goto cmddone;
+               }
+               savehandler = handler;
+               handler = &jmploc;
+               commandname = argv[0];
+               argptr = argv + 1;
+               optptr = NULL;                  /* initialize nextopt */
+               exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv);
+               flushall();
+cmddone:
+               out1 = &output;
+               out2 = &errout;
+               freestdout();
+               if (e != EXSHELLPROC) {
+                       commandname = savecmdname;
+                       if (flags & EV_EXIT) {
+                               exitshell(exitstatus);
+                       }
+               }
+               handler = savehandler;
+               if (e != -1) {
+                       if (e != EXERROR || cmdentry.u.index == BLTINCMD
+                                              || cmdentry.u.index == DOTCMD
+                                              || cmdentry.u.index == EVALCMD
+#ifndef NO_HISTORY
+                                              || cmdentry.u.index == HISTCMD
+#endif
+                                              || cmdentry.u.index == EXECCMD)
+                               exraise(e);
+                       FORCEINTON;
+               }
+               if (cmdentry.u.index != EXECCMD)
+                       popredir();
+               if (flags == EV_BACKCMD) {
+                       backcmd->buf = memout.buf;
+                       backcmd->nleft = memout.nextc - memout.buf;
+                       memout.buf = NULL;
+               }
+       } else {
+               trputs("normal command:  ");  trargs(argv);
+               clearredir();
+               redirect(cmd->ncmd.redirect, 0);
+               for (sp = varlist.list ; sp ; sp = sp->next)
+                       setvareq(sp->text, VEXPORT|VSTACK);
+               envp = environment();
+               shellexec(argv, envp, pathval(), cmdentry.u.index);
+               /*NOTREACHED*/
+       }
+       goto out;
+
+parent:        /* parent process gets here (if we forked) */
+       if (mode == 0) {        /* argument to fork */
+               INTOFF;
+               exitstatus = waitforjob(jp);
+               INTON;
+       } else if (mode == 2) {
+               backcmd->fd = pip[0];
+               close(pip[1]);
+               backcmd->jp = jp;
+       }
+
+out:
+       if (lastarg)
+               setvar("_", lastarg, 0);
+       popstackmark(&smark);
+}
+
+
+
+/*
+ * Search for a command.  This is called before we fork so that the
+ * location of the command will be available in the parent as well as
+ * the child.  The check for "goodname" is an overly conservative
+ * check that the name will not be subject to expansion.
+ */
+
+STATIC void
+prehash(n)
+       union node *n;
+{
+       struct cmdentry entry;
+
+       if (n->type == NCMD && n->ncmd.args)
+               if (goodname(n->ncmd.args->narg.text))
+                       find_command(n->ncmd.args->narg.text, &entry, 0,
+                                    pathval());
+}
+
+
+
+/*
+ * Builtin commands.  Builtin commands whose functions are closely
+ * tied to evaluation are implemented here.
+ */
+
+/*
+ * No command given, or a bltin command with no arguments.  Set the
+ * specified variables.
+ */
+
+int
+bltincmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       listsetvar(cmdenviron);
+       /* 
+        * Preserve exitstatus of a previous possible redirection
+        * as POSIX mandates 
+        */
+       return exitstatus;
+}
+
+
+/*
+ * Handle break and continue commands.  Break, continue, and return are
+ * all handled by setting the evalskip flag.  The evaluation routines
+ * above all check this flag, and if it is set they start skipping
+ * commands rather than executing them.  The variable skipcount is
+ * the number of loops to break/continue, or the number of function
+ * levels to return.  (The latter is always 1.)  It should probably
+ * be an error to break out of more loops than exist, but it isn't
+ * in the standard shell so we don't make it one here.
+ */
+
+int
+breakcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       int n;
+
+       n = 1;
+       if (argc > 1)
+               n = number(argv[1]);
+       if (n > loopnest)
+               n = loopnest;
+       if (n > 0) {
+               evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
+               skipcount = n;
+       }
+       return 0;
+}
+
+
+/*
+ * The return command.
+ */
+
+int
+returncmd(argc, argv) 
+       int argc;
+       char **argv; 
+{
+       int ret;
+
+       ret = exitstatus;
+       if (argc > 1)
+               ret = number(argv[1]);
+       if (funcnest) {
+               evalskip = SKIPFUNC;
+               skipcount = 1;
+       }
+       return ret;
+}
+
+
+int
+falsecmd(argc, argv) 
+       int argc;
+       char **argv; 
+{
+       return 1;
+}
+
+
+int
+truecmd(argc, argv)  
+       int argc;
+       char **argv; 
+{
+       return 0;
+}
+
+
+int
+execcmd(argc, argv) 
+       int argc;
+       char **argv; 
+{
+       if (argc > 1) {
+               struct strlist *sp;
+
+               iflag = 0;              /* exit on error */
+               mflag = 0;
+               optschanged();
+               for (sp = cmdenviron; sp ; sp = sp->next)
+                       setvareq(sp->text, VEXPORT|VSTACK);
+               shellexec(argv + 1, environment(), pathval(), 0);
+
+       }
+       return 0;
+}
diff --git a/usr/src/bin/sh/eval.h b/usr/src/bin/sh/eval.h
new file mode 100644 (file)
index 0000000..3b21293
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * 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.
+ *
+ *     @(#)eval.h      8.2 (Berkeley) 5/4/95
+ */
+
+extern char *commandname;      /* currently executing command */
+extern int exitstatus;         /* exit status of last command */
+extern struct strlist *cmdenviron;  /* environment for builtin command */
+
+
+struct backcmd {               /* result of evalbackcmd */
+       int fd;                 /* file descriptor to read from */
+       char *buf;              /* buffer */
+       int nleft;              /* number of chars in buffer */
+       struct job *jp;         /* job structure for command */
+};
+
+int evalcmd __P((int, char **));
+void evalstring __P((char *));
+union node;    /* BLETCH for ansi C */
+void evaltree __P((union node *, int));
+void evalbackcmd __P((union node *, struct backcmd *));
+int bltincmd __P((int, char **));
+int breakcmd __P((int, char **));
+int returncmd __P((int, char **));
+int falsecmd __P((int, char **));
+int truecmd __P((int, char **));
+int execcmd __P((int, char **));
+
+/* in_function returns nonzero if we are currently evaluating a function */
+#define in_function()  funcnest
+extern int funcnest;
diff --git a/usr/src/bin/sh/exec.c b/usr/src/bin/sh/exec.c
new file mode 100644 (file)
index 0000000..9b46b01
--- /dev/null
@@ -0,0 +1,821 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)exec.c     8.4 (Berkeley) 6/8/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/*
+ * When commands are first encountered, they are entered in a hash table.
+ * This ensures that a full path search will not have to be done for them
+ * on each invocation.
+ *
+ * We should investigate converting to a linear search, even though that
+ * would make the command name "hash" a misnomer.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "parser.h"
+#include "redir.h"
+#include "eval.h"
+#include "exec.h"
+#include "builtins.h"
+#include "var.h"
+#include "options.h"
+#include "input.h"
+#include "output.h"
+#include "syntax.h"
+#include "memalloc.h"
+#include "error.h"
+#include "init.h"
+#include "mystring.h"
+#include "show.h"
+#include "jobs.h"
+
+
+#define CMDTABLESIZE 31                /* should be prime */
+#define ARB 1                  /* actual size determined at run time */
+
+
+
+struct tblentry {
+       struct tblentry *next;  /* next entry in hash chain */
+       union param param;      /* definition of builtin function */
+       short cmdtype;          /* index identifying command */
+       char rehash;            /* if set, cd done since entry created */
+       char cmdname[ARB];      /* name of command */
+};
+
+
+STATIC struct tblentry *cmdtable[CMDTABLESIZE];
+STATIC int builtinloc = -1;            /* index in path of %builtin, or -1 */
+
+
+STATIC void tryexec __P((char *, char **, char **));
+STATIC void execinterp __P((char **, char **));
+STATIC void printentry __P((struct tblentry *, int));
+STATIC void clearcmdentry __P((int));
+STATIC struct tblentry *cmdlookup __P((char *, int));
+STATIC void delete_cmd_entry __P((void));
+
+
+
+/*
+ * Exec a program.  Never returns.  If you change this routine, you may
+ * have to change the find_command routine as well.
+ */
+
+void
+shellexec(argv, envp, path, index)
+       char **argv, **envp;
+       char *path;
+       int index;
+{
+       char *cmdname;
+       int e;
+
+       if (strchr(argv[0], '/') != NULL) {
+               tryexec(argv[0], argv, envp);
+               e = errno;
+       } else {
+               e = ENOENT;
+               while ((cmdname = padvance(&path, argv[0])) != NULL) {
+                       if (--index < 0 && pathopt == NULL) {
+                               tryexec(cmdname, argv, envp);
+                               if (errno != ENOENT && errno != ENOTDIR)
+                                       e = errno;
+                       }
+                       stunalloc(cmdname);
+               }
+       }
+       error2(argv[0], errmsg(e, E_EXEC));
+}
+
+
+STATIC void
+tryexec(cmd, argv, envp)
+       char *cmd;
+       char **argv;
+       char **envp;
+       {
+       int e;
+#ifndef BSD
+       char *p;
+#endif
+
+#ifdef SYSV
+       do {
+               execve(cmd, argv, envp);
+       } while (errno == EINTR);
+#else
+       execve(cmd, argv, envp);
+#endif
+       e = errno;
+       if (e == ENOEXEC) {
+               initshellproc();
+               setinputfile(cmd, 0);
+               commandname = arg0 = savestr(argv[0]);
+#ifndef BSD
+               pgetc(); pungetc();             /* fill up input buffer */
+               p = parsenextc;
+               if (parsenleft > 2 && p[0] == '#' && p[1] == '!') {
+                       argv[0] = cmd;
+                       execinterp(argv, envp);
+               }
+#endif
+               setparam(argv + 1);
+               exraise(EXSHELLPROC);
+               /*NOTREACHED*/
+       }
+       errno = e;
+}
+
+
+#ifndef BSD
+/*
+ * Execute an interpreter introduced by "#!", for systems where this
+ * feature has not been built into the kernel.  If the interpreter is
+ * the shell, return (effectively ignoring the "#!").  If the execution
+ * of the interpreter fails, exit.
+ *
+ * This code peeks inside the input buffer in order to avoid actually
+ * reading any input.  It would benefit from a rewrite.
+ */
+
+#define NEWARGS 5
+
+STATIC void
+execinterp(argv, envp)
+       char **argv, **envp;
+       {
+       int n;
+       char *inp;
+       char *outp;
+       char c;
+       char *p;
+       char **ap;
+       char *newargs[NEWARGS];
+       int i;
+       char **ap2;
+       char **new;
+
+       n = parsenleft - 2;
+       inp = parsenextc + 2;
+       ap = newargs;
+       for (;;) {
+               while (--n >= 0 && (*inp == ' ' || *inp == '\t'))
+                       inp++;
+               if (n < 0)
+                       goto bad;
+               if ((c = *inp++) == '\n')
+                       break;
+               if (ap == &newargs[NEWARGS])
+bad:             error("Bad #! line");
+               STARTSTACKSTR(outp);
+               do {
+                       STPUTC(c, outp);
+               } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n');
+               STPUTC('\0', outp);
+               n++, inp--;
+               *ap++ = grabstackstr(outp);
+       }
+       if (ap == newargs + 1) {        /* if no args, maybe no exec is needed */
+               p = newargs[0];
+               for (;;) {
+                       if (equal(p, "sh") || equal(p, "ash")) {
+                               return;
+                       }
+                       while (*p != '/') {
+                               if (*p == '\0')
+                                       goto break2;
+                               p++;
+                       }
+                       p++;
+               }
+break2:;
+       }
+       i = (char *)ap - (char *)newargs;               /* size in bytes */
+       if (i == 0)
+               error("Bad #! line");
+       for (ap2 = argv ; *ap2++ != NULL ; );
+       new = ckmalloc(i + ((char *)ap2 - (char *)argv));
+       ap = newargs, ap2 = new;
+       while ((i -= sizeof (char **)) >= 0)
+               *ap2++ = *ap++;
+       ap = argv;
+       while (*ap2++ = *ap++);
+       shellexec(new, envp, pathval(), 0);
+}
+#endif
+
+
+
+/*
+ * Do a path search.  The variable path (passed by reference) should be
+ * set to the start of the path before the first call; padvance will update
+ * this value as it proceeds.  Successive calls to padvance will return
+ * the possible path expansions in sequence.  If an option (indicated by
+ * a percent sign) appears in the path entry then the global variable
+ * pathopt will be set to point to it; otherwise pathopt will be set to
+ * NULL.
+ */
+
+char *pathopt;
+
+char *
+padvance(path, name)
+       char **path;
+       char *name;
+       {
+       register char *p, *q;
+       char *start;
+       int len;
+
+       if (*path == NULL)
+               return NULL;
+       start = *path;
+       for (p = start ; *p && *p != ':' && *p != '%' ; p++);
+       len = p - start + strlen(name) + 2;     /* "2" is for '/' and '\0' */
+       while (stackblocksize() < len)
+               growstackblock();
+       q = stackblock();
+       if (p != start) {
+               memcpy(q, start, p - start);
+               q += p - start;
+               *q++ = '/';
+       }
+       strcpy(q, name);
+       pathopt = NULL;
+       if (*p == '%') {
+               pathopt = ++p;
+               while (*p && *p != ':')  p++;
+       }
+       if (*p == ':')
+               *path = p + 1;
+       else
+               *path = NULL;
+       return stalloc(len);
+}
+
+
+
+/*** Command hashing code ***/
+
+
+int
+hashcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+       int c;
+       int verbose;
+       struct cmdentry entry;
+       char *name;
+
+       verbose = 0;
+       while ((c = nextopt("rv")) != '\0') {
+               if (c == 'r') {
+                       clearcmdentry(0);
+               } else if (c == 'v') {
+                       verbose++;
+               }
+       }
+       if (*argptr == NULL) {
+               for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+                       for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+                               printentry(cmdp, verbose);
+                       }
+               }
+               return 0;
+       }
+       while ((name = *argptr) != NULL) {
+               if ((cmdp = cmdlookup(name, 0)) != NULL
+                && (cmdp->cmdtype == CMDNORMAL
+                    || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)))
+                       delete_cmd_entry();
+               find_command(name, &entry, 1, pathval());
+               if (verbose) {
+                       if (entry.cmdtype != CMDUNKNOWN) {      /* if no error msg */
+                               cmdp = cmdlookup(name, 0);
+                               printentry(cmdp, verbose);
+                       }
+                       flushall();
+               }
+               argptr++;
+       }
+       return 0;
+}
+
+
+STATIC void
+printentry(cmdp, verbose)
+       struct tblentry *cmdp;
+       int verbose;
+       {
+       int index;
+       char *path;
+       char *name;
+
+       if (cmdp->cmdtype == CMDNORMAL) {
+               index = cmdp->param.index;
+               path = pathval();
+               do {
+                       name = padvance(&path, cmdp->cmdname);
+                       stunalloc(name);
+               } while (--index >= 0);
+               out1str(name);
+       } else if (cmdp->cmdtype == CMDBUILTIN) {
+               out1fmt("builtin %s", cmdp->cmdname);
+       } else if (cmdp->cmdtype == CMDFUNCTION) {
+               out1fmt("function %s", cmdp->cmdname);
+               if (verbose) {
+                       INTOFF;
+                       name = commandtext(cmdp->param.func);
+                       out1c(' ');
+                       out1str(name);
+                       ckfree(name);
+                       INTON;
+               }
+#ifdef DEBUG
+       } else {
+               error("internal error: cmdtype %d", cmdp->cmdtype);
+#endif
+       }
+       if (cmdp->rehash)
+               out1c('*');
+       out1c('\n');
+}
+
+
+
+/*
+ * Resolve a command name.  If you change this routine, you may have to
+ * change the shellexec routine as well.
+ */
+
+void
+find_command(name, entry, printerr, path)
+       char *name;
+       struct cmdentry *entry;
+       int printerr;
+       char *path;
+{
+       struct tblentry *cmdp;
+       int index;
+       int prev;
+       char *fullname;
+       struct stat statb;
+       int e;
+       int i;
+
+       /* If name contains a slash, don't use the hash table */
+       if (strchr(name, '/') != NULL) {
+               entry->cmdtype = CMDNORMAL;
+               entry->u.index = 0;
+               return;
+       }
+
+       /* If name is in the table, and not invalidated by cd, we're done */
+       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0)
+               goto success;
+
+       /* If %builtin not in path, check for builtin next */
+       if (builtinloc < 0 && (i = find_builtin(name)) >= 0) {
+               INTOFF;
+               cmdp = cmdlookup(name, 1);
+               cmdp->cmdtype = CMDBUILTIN;
+               cmdp->param.index = i;
+               INTON;
+               goto success;
+       }
+
+       /* We have to search path. */
+       prev = -1;              /* where to start */
+       if (cmdp) {             /* doing a rehash */
+               if (cmdp->cmdtype == CMDBUILTIN)
+                       prev = builtinloc;
+               else
+                       prev = cmdp->param.index;
+       }
+
+       e = ENOENT;
+       index = -1;
+loop:
+       while ((fullname = padvance(&path, name)) != NULL) {
+               stunalloc(fullname);
+               index++;
+               if (pathopt) {
+                       if (prefix("builtin", pathopt)) {
+                               if ((i = find_builtin(name)) < 0)
+                                       goto loop;
+                               INTOFF;
+                               cmdp = cmdlookup(name, 1);
+                               cmdp->cmdtype = CMDBUILTIN;
+                               cmdp->param.index = i;
+                               INTON;
+                               goto success;
+                       } else if (prefix("func", pathopt)) {
+                               /* handled below */
+                       } else {
+                               goto loop;      /* ignore unimplemented options */
+                       }
+               }
+               /* if rehash, don't redo absolute path names */
+               if (fullname[0] == '/' && index <= prev) {
+                       if (index < prev)
+                               goto loop;
+                       TRACE(("searchexec \"%s\": no change\n", name));
+                       goto success;
+               }
+               while (stat(fullname, &statb) < 0) {
+#ifdef SYSV
+                       if (errno == EINTR)
+                               continue;
+#endif
+                       if (errno != ENOENT && errno != ENOTDIR)
+                               e = errno;
+                       goto loop;
+               }
+               e = EACCES;     /* if we fail, this will be the error */
+               if (!S_ISREG(statb.st_mode))
+                       goto loop;
+               if (pathopt) {          /* this is a %func directory */
+                       stalloc(strlen(fullname) + 1);
+                       readcmdfile(fullname);
+                       if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION)
+                               error("%s not defined in %s", name, fullname);
+                       stunalloc(fullname);
+                       goto success;
+               }
+#ifdef notdef
+               if (statb.st_uid == geteuid()) {
+                       if ((statb.st_mode & 0100) == 0)
+                               goto loop;
+               } else if (statb.st_gid == getegid()) {
+                       if ((statb.st_mode & 010) == 0)
+                               goto loop;
+               } else {
+                       if ((statb.st_mode & 01) == 0)
+                               goto loop;
+               }
+#endif
+               TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
+               INTOFF;
+               cmdp = cmdlookup(name, 1);
+               cmdp->cmdtype = CMDNORMAL;
+               cmdp->param.index = index;
+               INTON;
+               goto success;
+       }
+
+       /* We failed.  If there was an entry for this command, delete it */
+       if (cmdp)
+               delete_cmd_entry();
+       if (printerr)
+               outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC));
+       entry->cmdtype = CMDUNKNOWN;
+       return;
+
+success:
+       cmdp->rehash = 0;
+       entry->cmdtype = cmdp->cmdtype;
+       entry->u = cmdp->param;
+}
+
+
+
+/*
+ * Search the table of builtin commands.
+ */
+
+int
+find_builtin(name)
+       char *name;
+{
+       register const struct builtincmd *bp;
+
+       for (bp = builtincmd ; bp->name ; bp++) {
+               if (*bp->name == *name && equal(bp->name, name))
+                       return bp->code;
+       }
+       return -1;
+}
+
+
+
+/*
+ * Called when a cd is done.  Marks all commands so the next time they
+ * are executed they will be rehashed.
+ */
+
+void
+hashcd() {
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+
+       for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) {
+               for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+                       if (cmdp->cmdtype == CMDNORMAL
+                        || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))
+                               cmdp->rehash = 1;
+               }
+       }
+}
+
+
+
+/*
+ * Called before PATH is changed.  The argument is the new value of PATH;
+ * pathval() still returns the old value at this point.  Called with
+ * interrupts off.
+ */
+
+void
+changepath(newval)
+       char *newval;
+{
+       char *old, *new;
+       int index;
+       int firstchange;
+       int bltin;
+
+       old = pathval();
+       new = newval;
+       firstchange = 9999;     /* assume no change */
+       index = 0;
+       bltin = -1;
+       for (;;) {
+               if (*old != *new) {
+                       firstchange = index;
+                       if ((*old == '\0' && *new == ':')
+                        || (*old == ':' && *new == '\0'))
+                               firstchange++;
+                       old = new;      /* ignore subsequent differences */
+               }
+               if (*new == '\0')
+                       break;
+               if (*new == '%' && bltin < 0 && prefix("builtin", new + 1))
+                       bltin = index;
+               if (*new == ':') {
+                       index++;
+               }
+               new++, old++;
+       }
+       if (builtinloc < 0 && bltin >= 0)
+               builtinloc = bltin;             /* zap builtins */
+       if (builtinloc >= 0 && bltin < 0)
+               firstchange = 0;
+       clearcmdentry(firstchange);
+       builtinloc = bltin;
+}
+
+
+/*
+ * Clear out command entries.  The argument specifies the first entry in
+ * PATH which has changed.
+ */
+
+STATIC void
+clearcmdentry(firstchange)
+       int firstchange;
+{
+       struct tblentry **tblp;
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+
+       INTOFF;
+       for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+               pp = tblp;
+               while ((cmdp = *pp) != NULL) {
+                       if ((cmdp->cmdtype == CMDNORMAL &&
+                            cmdp->param.index >= firstchange)
+                        || (cmdp->cmdtype == CMDBUILTIN &&
+                            builtinloc >= firstchange)) {
+                               *pp = cmdp->next;
+                               ckfree(cmdp);
+                       } else {
+                               pp = &cmdp->next;
+                       }
+               }
+       }
+       INTON;
+}
+
+
+/*
+ * Delete all functions.
+ */
+
+#ifdef mkinit
+MKINIT void deletefuncs();
+
+SHELLPROC {
+       deletefuncs();
+}
+#endif
+
+void
+deletefuncs() {
+       struct tblentry **tblp;
+       struct tblentry **pp;
+       struct tblentry *cmdp;
+
+       INTOFF;
+       for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) {
+               pp = tblp;
+               while ((cmdp = *pp) != NULL) {
+                       if (cmdp->cmdtype == CMDFUNCTION) {
+                               *pp = cmdp->next;
+                               freefunc(cmdp->param.func);
+                               ckfree(cmdp);
+                       } else {
+                               pp = &cmdp->next;
+                       }
+               }
+       }
+       INTON;
+}
+
+
+
+/*
+ * Locate a command in the command hash table.  If "add" is nonzero,
+ * add the command to the table if it is not already present.  The
+ * variable "lastcmdentry" is set to point to the address of the link
+ * pointing to the entry, so that delete_cmd_entry can delete the
+ * entry.
+ */
+
+struct tblentry **lastcmdentry;
+
+
+STATIC struct tblentry *
+cmdlookup(name, add)
+       char *name;
+       int add;
+{
+       int hashval;
+       register char *p;
+       struct tblentry *cmdp;
+       struct tblentry **pp;
+
+       p = name;
+       hashval = *p << 4;
+       while (*p)
+               hashval += *p++;
+       hashval &= 0x7FFF;
+       pp = &cmdtable[hashval % CMDTABLESIZE];
+       for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) {
+               if (equal(cmdp->cmdname, name))
+                       break;
+               pp = &cmdp->next;
+       }
+       if (add && cmdp == NULL) {
+               INTOFF;
+               cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB
+                                       + strlen(name) + 1);
+               cmdp->next = NULL;
+               cmdp->cmdtype = CMDUNKNOWN;
+               cmdp->rehash = 0;
+               strcpy(cmdp->cmdname, name);
+               INTON;
+       }
+       lastcmdentry = pp;
+       return cmdp;
+}
+
+/*
+ * Delete the command entry returned on the last lookup.
+ */
+
+STATIC void
+delete_cmd_entry() {
+       struct tblentry *cmdp;
+
+       INTOFF;
+       cmdp = *lastcmdentry;
+       *lastcmdentry = cmdp->next;
+       ckfree(cmdp);
+       INTON;
+}
+
+
+
+#ifdef notdef
+void
+getcmdentry(name, entry)
+       char *name;
+       struct cmdentry *entry; 
+       {
+       struct tblentry *cmdp = cmdlookup(name, 0);
+
+       if (cmdp) {
+               entry->u = cmdp->param;
+               entry->cmdtype = cmdp->cmdtype;
+       } else {
+               entry->cmdtype = CMDUNKNOWN;
+               entry->u.index = 0;
+       }
+}
+#endif
+
+
+/*
+ * Add a new command entry, replacing any existing command entry for
+ * the same name.
+ */
+
+void
+addcmdentry(name, entry)
+       char *name;
+       struct cmdentry *entry;
+       {
+       struct tblentry *cmdp;
+
+       INTOFF;
+       cmdp = cmdlookup(name, 1);
+       if (cmdp->cmdtype == CMDFUNCTION) {
+               freefunc(cmdp->param.func);
+       }
+       cmdp->cmdtype = entry->cmdtype;
+       cmdp->param = entry->u;
+       INTON;
+}
+
+
+/*
+ * Define a shell function.
+ */
+
+void
+defun(name, func)
+       char *name;
+       union node *func;
+       {
+       struct cmdentry entry;
+
+       INTOFF;
+       entry.cmdtype = CMDFUNCTION;
+       entry.u.func = copyfunc(func);
+       addcmdentry(name, &entry);
+       INTON;
+}
+
+
+/*
+ * Delete a function if it exists.
+ */
+
+int
+unsetfunc(name)
+       char *name;
+       {
+       struct tblentry *cmdp;
+
+       if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) {
+               freefunc(cmdp->param.func);
+               delete_cmd_entry();
+               return (0);
+       }
+       return (1);
+}
diff --git a/usr/src/bin/sh/exec.h b/usr/src/bin/sh/exec.h
new file mode 100644 (file)
index 0000000..c61236c
--- /dev/null
@@ -0,0 +1,64 @@
+/*-
+ * 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.
+ *
+ *     @(#)exec.h      8.3 (Berkeley) 6/8/95
+ */
+
+/* values of cmdtype */
+#define CMDUNKNOWN -1          /* no entry in table for command */
+#define CMDNORMAL 0            /* command is an executable program */
+#define CMDBUILTIN 1           /* command is a shell builtin */
+#define CMDFUNCTION 2          /* command is a shell function */
+
+
+struct cmdentry {
+       int cmdtype;
+       union param {
+               int index;
+               union node *func;
+       } u;
+};
+
+
+extern char *pathopt;          /* set by padvance */
+
+void shellexec __P((char **, char **, char *, int));
+char *padvance __P((char **, char *));
+int hashcmd __P((int, char **));
+void find_command __P((char *, struct cmdentry *, int, char *));
+int find_builtin __P((char *));
+void hashcd __P((void));
+void changepath __P((char *));
+void deletefuncs __P((void));
+void getcmdentry __P((char *, struct cmdentry *));
+void addcmdentry __P((char *, struct cmdentry *));
+void defun __P((char *, union node *));
+int unsetfunc __P((char *));
diff --git a/usr/src/bin/sh/expand.c b/usr/src/bin/sh/expand.c
new file mode 100644 (file)
index 0000000..8ebdf3f
--- /dev/null
@@ -0,0 +1,1329 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)expand.c   8.5 (Berkeley) 5/15/95";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <stdlib.h>
+
+/*
+ * Routines to expand arguments to commands.  We have to deal with
+ * backquotes, shell variables, and file metacharacters.
+ */
+
+#include "shell.h"
+#include "main.h"
+#include "nodes.h"
+#include "eval.h"
+#include "expand.h"
+#include "syntax.h"
+#include "parser.h"
+#include "jobs.h"
+#include "options.h"
+#include "var.h"
+#include "input.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+#include "arith.h"
+#include "show.h"
+
+/*
+ * Structure specifying which parts of the string should be searched
+ * for IFS characters.
+ */
+
+struct ifsregion {
+       struct ifsregion *next; /* next region in list */
+       int begoff;             /* offset of start of region */
+       int endoff;             /* offset of end of region */
+       int nulonly;            /* search for nul bytes only */
+};
+
+
+char *expdest;                 /* output of current string */
+struct nodelist *argbackq;     /* list of back quote expressions */
+struct ifsregion ifsfirst;     /* first struct in list of ifs regions */
+struct ifsregion *ifslastp;    /* last struct in list */
+struct arglist exparg;         /* holds expanded arg list */
+
+STATIC void argstr __P((char *, int));
+STATIC char *exptilde __P((char *, int));
+STATIC void expbackq __P((union node *, int, int));
+STATIC int subevalvar __P((char *, char *, int, int, int));
+STATIC char *evalvar __P((char *, int));
+STATIC int varisset __P((int));
+STATIC void varvalue __P((int, int, int));
+STATIC void recordregion __P((int, int, int));
+STATIC void ifsbreakup __P((char *, struct arglist *));
+STATIC void expandmeta __P((struct strlist *, int));
+STATIC void expmeta __P((char *, char *));
+STATIC void addfname __P((char *));
+STATIC struct strlist *expsort __P((struct strlist *));
+STATIC struct strlist *msort __P((struct strlist *, int));
+STATIC int pmatch __P((char *, char *));
+STATIC char *cvtnum __P((int, char *));
+
+/*
+ * Expand shell variables and backquotes inside a here document.
+ */
+
+void
+expandhere(arg, fd)
+       union node *arg;        /* the document */
+       int fd;                 /* where to write the expanded version */
+       {
+       herefd = fd;
+       expandarg(arg, (struct arglist *)NULL, 0);
+       xwrite(fd, stackblock(), expdest - stackblock());
+}
+
+
+/*
+ * Perform variable substitution and command substitution on an argument,
+ * placing the resulting list of arguments in arglist.  If EXP_FULL is true,
+ * perform splitting and file name expansion.  When arglist is NULL, perform
+ * here document expansion.
+ */
+
+void
+expandarg(arg, arglist, flag)
+       union node *arg;
+       struct arglist *arglist;
+       int flag;
+{
+       struct strlist *sp;
+       char *p;
+
+       argbackq = arg->narg.backquote;
+       STARTSTACKSTR(expdest);
+       ifsfirst.next = NULL;
+       ifslastp = NULL;
+       argstr(arg->narg.text, flag);
+       if (arglist == NULL) {
+               return;                 /* here document expanded */
+       }
+       STPUTC('\0', expdest);
+       p = grabstackstr(expdest);
+       exparg.lastp = &exparg.list;
+       /*
+        * TODO - EXP_REDIR
+        */
+       if (flag & EXP_FULL) {
+               ifsbreakup(p, &exparg);
+               *exparg.lastp = NULL;
+               exparg.lastp = &exparg.list;
+               expandmeta(exparg.list, flag);
+       } else {
+               if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
+                       rmescapes(p);
+               sp = (struct strlist *)stalloc(sizeof (struct strlist));
+               sp->text = p;
+               *exparg.lastp = sp;
+               exparg.lastp = &sp->next;
+       }
+       while (ifsfirst.next != NULL) {
+               struct ifsregion *ifsp;
+               INTOFF;
+               ifsp = ifsfirst.next->next;
+               ckfree(ifsfirst.next);
+               ifsfirst.next = ifsp;
+               INTON;
+       }
+       *exparg.lastp = NULL;
+       if (exparg.list) {
+               *arglist->lastp = exparg.list;
+               arglist->lastp = exparg.lastp;
+       }
+}
+
+
+
+/*
+ * Perform variable and command substitution.  If EXP_FULL is set, output CTLESC
+ * characters to allow for further processing.  Otherwise treat
+ * $@ like $* since no splitting will be performed.
+ */
+
+STATIC void
+argstr(p, flag)
+       register char *p;
+       int flag;
+{
+       register char c;
+       int quotes = flag & (EXP_FULL | EXP_CASE);      /* do CTLESC */
+       int firsteq = 1;
+
+       if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
+               p = exptilde(p, flag);
+       for (;;) {
+               switch (c = *p++) {
+               case '\0':
+               case CTLENDVAR: /* ??? */
+                       goto breakloop;
+               case CTLESC:
+                       if (quotes)
+                               STPUTC(c, expdest);
+                       c = *p++;
+                       STPUTC(c, expdest);
+                       break;
+               case CTLVAR:
+                       p = evalvar(p, flag);
+                       break;
+               case CTLBACKQ:
+               case CTLBACKQ|CTLQUOTE:
+                       expbackq(argbackq->n, c & CTLQUOTE, flag);
+                       argbackq = argbackq->next;
+                       break;
+               case CTLENDARI:
+                       expari(flag);
+                       break;
+               case ':':
+               case '=':
+                       /*
+                        * sort of a hack - expand tildes in variable
+                        * assignments (after the first '=' and after ':'s).
+                        */
+                       STPUTC(c, expdest);
+                       if (flag & EXP_VARTILDE && *p == '~') {
+                               if (c == '=') {
+                                       if (firsteq)
+                                               firsteq = 0;
+                                       else
+                                               break;
+                               }
+                               p = exptilde(p, flag);
+                       }
+                       break;
+               default:
+                       STPUTC(c, expdest);
+               }
+       }
+breakloop:;
+}
+
+STATIC char *
+exptilde(p, flag)
+       char *p;
+       int flag;
+{
+       char c, *startp = p;
+       struct passwd *pw;
+       char *home;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       while ((c = *p) != '\0') {
+               switch(c) {
+               case CTLESC:
+                       return (startp);
+               case ':':
+                       if (flag & EXP_VARTILDE)
+                               goto done;
+                       break;
+               case '/':
+                       goto done;
+               }
+               p++;
+       }
+done:
+       *p = '\0';
+       if (*(startp+1) == '\0') {
+               if ((home = lookupvar("HOME")) == NULL)
+                       goto lose;
+       } else {
+               if ((pw = getpwnam(startp+1)) == NULL)
+                       goto lose;
+               home = pw->pw_dir;
+       }
+       if (*home == '\0')
+               goto lose;
+       *p = c;
+       while ((c = *home++) != '\0') {
+               if (quotes && SQSYNTAX[c] == CCTL)
+                       STPUTC(CTLESC, expdest);
+               STPUTC(c, expdest);
+       }
+       return (p);
+lose:
+       *p = c;
+       return (startp);
+}
+
+
+/*
+ * Expand arithmetic expression.  Backup to start of expression,
+ * evaluate, place result in (backed up) result, adjust string position.
+ */
+void
+expari(flag)
+       int flag;
+{
+       char *p, *start;
+       int result;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       /*
+        * This routine is slightly over-compilcated for
+        * efficiency.  First we make sure there is
+        * enough space for the result, which may be bigger
+        * than the expression if we add exponentation.  Next we
+        * scan backwards looking for the start of arithmetic.  If the
+        * next previous character is a CTLESC character, then we
+        * have to rescan starting from the beginning since CTLESC
+        * characters have to be processed left to right.  
+        */
+       CHECKSTRSPACE(8, expdest);
+       USTPUTC('\0', expdest); 
+       start = stackblock();
+       p = expdest;
+       while (*p != CTLARI && p >= start)
+               --p;
+       if (*p != CTLARI)
+               error("missing CTLARI (shouldn't happen)");
+       if (p > start && *(p-1) == CTLESC)
+               for (p = start; *p != CTLARI; p++)
+                       if (*p == CTLESC)
+                               p++;
+       if (quotes)
+               rmescapes(p+1);
+       result = arith(p+1);
+       fmtstr(p, 10, "%d", result);
+       while (*p++)
+               ;
+       result = expdest - p + 1;
+       STADJUST(-result, expdest);
+}
+
+
+/*
+ * Expand stuff in backwards quotes.
+ */
+
+STATIC void
+expbackq(cmd, quoted, flag)
+       union node *cmd;
+       int quoted;
+       int flag;
+{
+       struct backcmd in;
+       int i;
+       char buf[128];
+       char *p;
+       char *dest = expdest;
+       struct ifsregion saveifs, *savelastp;
+       struct nodelist *saveargbackq;
+       char lastc;
+       int startloc = dest - stackblock();
+       char const *syntax = quoted? DQSYNTAX : BASESYNTAX;
+       int saveherefd;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       INTOFF;
+       saveifs = ifsfirst;
+       savelastp = ifslastp;
+       saveargbackq = argbackq;
+       saveherefd = herefd;      
+       herefd = -1;
+       p = grabstackstr(dest);
+       evalbackcmd(cmd, &in);
+       ungrabstackstr(p, dest);
+       ifsfirst = saveifs;
+       ifslastp = savelastp;
+       argbackq = saveargbackq;
+       herefd = saveherefd;
+
+       p = in.buf;
+       lastc = '\0';
+       for (;;) {
+               if (--in.nleft < 0) {
+                       if (in.fd < 0)
+                               break;
+                       while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR);
+                       TRACE(("expbackq: read returns %d\n", i));
+                       if (i <= 0)
+                               break;
+                       p = buf;
+                       in.nleft = i - 1;
+               }
+               lastc = *p++;
+               if (lastc != '\0') {
+                       if (quotes && syntax[lastc] == CCTL)
+                               STPUTC(CTLESC, dest);
+                       STPUTC(lastc, dest);
+               }
+       }
+
+       /* Eat all trailing newlines */
+       for (p--; lastc == '\n'; lastc = *--p)
+               STUNPUTC(dest);
+
+       if (in.fd >= 0)
+               close(in.fd);
+       if (in.buf)
+               ckfree(in.buf);
+       if (in.jp)
+               exitstatus = waitforjob(in.jp);
+       if (quoted == 0)
+               recordregion(startloc, dest - stackblock(), 0);
+       TRACE(("evalbackq: size=%d: \"%.*s\"\n",
+               (dest - stackblock()) - startloc,
+               (dest - stackblock()) - startloc,
+               stackblock() + startloc));
+       expdest = dest;
+       INTON;
+}
+
+
+
+STATIC int
+subevalvar(p, str, subtype, startloc, varflags)
+       char *p;
+       char *str;
+       int subtype;
+       int startloc;
+       int varflags;
+{
+
+       char *startp;
+       char *loc;
+       int c = 0;
+       int saveherefd = herefd;
+       struct nodelist *saveargbackq = argbackq;
+       herefd = -1;
+       argstr(p, 0);
+       STACKSTRNUL(expdest);
+       herefd = saveherefd;
+       argbackq = saveargbackq;
+       startp = stackblock() + startloc;
+
+       switch (subtype) {
+       case VSASSIGN:
+               setvar(str, startp, 0);
+               STADJUST(startp - expdest, expdest);
+               varflags &= ~VSNUL;
+               if (c != 0)
+                       *loc = c;
+               return 1;
+
+       case VSQUESTION:
+               if (*p != CTLENDVAR) {
+                       outfmt(&errout, "%s\n", startp);
+                       error((char *)NULL);
+               }
+               error("%.*s: parameter %snot set", p - str - 1,
+                     str, (varflags & VSNUL) ? "null or "
+                                             : nullstr);
+               return 0;
+
+       case VSTRIMLEFT:
+               for (loc = startp; loc < str - 1; loc++) {
+                       c = *loc;
+                       *loc = '\0';
+                       if (patmatch(str, startp)) {
+                               *loc = c;
+                               goto recordleft;
+                       }
+                       *loc = c;
+               }
+               return 0;
+
+       case VSTRIMLEFTMAX:
+               for (loc = str - 1; loc >= startp; loc--) {
+                       c = *loc;
+                       *loc = '\0';
+                       if (patmatch(str, startp)) {
+                               *loc = c;
+                               goto recordleft;
+                       }
+                       *loc = c;
+               }
+               return 0;
+
+       case VSTRIMRIGHT:
+               for (loc = str - 1; loc >= startp; loc--) {
+                       if (patmatch(str, loc)) {
+                               expdest = loc;
+                               return 1;
+                       }
+               }
+               return 0;
+
+       case VSTRIMRIGHTMAX:
+               for (loc = startp; loc < str - 1; loc++) {
+                       if (patmatch(str, loc)) {
+                               expdest = loc;
+                               return 1;
+                       }
+               }
+               return 0;
+
+
+       default:
+               abort();
+       }
+
+recordleft:
+       expdest = (str - 1) - (loc - startp);
+       while (loc != str - 1)
+               *startp++ = *loc++;
+       return 1;
+}
+
+
+/*
+ * Expand a variable, and return a pointer to the next character in the
+ * input string.
+ */
+
+STATIC char *
+evalvar(p, flag)
+       char *p;
+       int flag;
+{
+       int subtype;
+       int varflags;
+       char *var;
+       char *val;
+       char *pat;
+       int c;
+       int set;
+       int special;
+       int startloc;
+       int varlen;
+       int easy;
+       int quotes = flag & (EXP_FULL | EXP_CASE);
+
+       varflags = *p++;
+       subtype = varflags & VSTYPE;
+       var = p;
+       special = 0;
+       if (! is_name(*p))
+               special = 1;
+       p = strchr(p, '=') + 1;
+again: /* jump here after setting a variable with ${var=text} */
+       if (special) {
+               set = varisset(*var);
+               val = NULL;
+       } else {
+               val = lookupvar(var);
+               if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) {
+                       val = NULL;
+                       set = 0;
+               } else
+                       set = 1;
+       }
+       varlen = 0;
+       startloc = expdest - stackblock();
+       if (set && subtype != VSPLUS) {
+               /* insert the value of the variable */
+               if (special) {
+                       char *exp, *oexpdest = expdest;
+                       varvalue(*var, varflags & VSQUOTE, flag & EXP_FULL);
+                       if (subtype == VSLENGTH) {
+                               for (exp = oexpdest;exp != expdest; exp++)
+                                       varlen++;
+                               expdest = oexpdest;
+                       }
+               } else {
+                       char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 
+                                                                 : BASESYNTAX;
+
+                       if (subtype == VSLENGTH) {
+                               for (;*val; val++)
+                                       varlen++;
+                       }
+                       else {
+                               while (*val) {
+                                       if (quotes && syntax[*val] == CCTL)
+                                               STPUTC(CTLESC, expdest);
+                                       STPUTC(*val++, expdest);
+                               }
+
+                       }
+               }
+       }
+               
+       if (subtype == VSPLUS)
+               set = ! set;
+
+       easy = ((varflags & VSQUOTE) == 0 || 
+               (*var == '@' && shellparam.nparam != 1));
+
+
+       switch (subtype) {
+       case VSLENGTH:
+               expdest = cvtnum(varlen, expdest);
+               goto record;
+
+       case VSNORMAL:
+               if (!easy)
+                       break;
+record:
+               recordregion(startloc, expdest - stackblock(), 
+                            varflags & VSQUOTE);
+               break;
+
+       case VSPLUS:
+       case VSMINUS:
+               if (!set) {
+                       argstr(p, flag);
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
+
+       case VSTRIMLEFT:
+       case VSTRIMLEFTMAX:
+       case VSTRIMRIGHT:
+       case VSTRIMRIGHTMAX:
+               if (!set)
+                       break;
+               /*
+                * Terminate the string and start recording the pattern
+                * right after it
+                */
+               STPUTC('\0', expdest);
+               pat = expdest;
+               if (subevalvar(p, pat, subtype, startloc, varflags))
+                       goto record;
+               break;
+
+       case VSASSIGN:
+       case VSQUESTION:
+               if (!set) {
+                       if (subevalvar(p, var, subtype, startloc, varflags))
+                               goto again;
+                       break;
+               }
+               if (easy)
+                       goto record;
+               break;
+
+       default:
+               abort();
+       }
+
+       if (subtype != VSNORMAL) {      /* skip to end of alternative */
+               int nesting = 1;
+               for (;;) {
+                       if ((c = *p++) == CTLESC)
+                               p++;
+                       else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) {
+                               if (set)
+                                       argbackq = argbackq->next;
+                       } else if (c == CTLVAR) {
+                               if ((*p++ & VSTYPE) != VSNORMAL)
+                                       nesting++;
+                       } else if (c == CTLENDVAR) {
+                               if (--nesting == 0)
+                                       break;
+                       }
+               }
+       }
+       return p;
+}
+
+
+
+/*
+ * Test whether a specialized variable is set.
+ */
+
+STATIC int
+varisset(name)
+       char name;
+       {
+       char **ap;
+
+       if (name == '!') {
+               if (backgndpid == -1)
+                       return 0;
+       } else if (name == '@' || name == '*') {
+               if (*shellparam.p == NULL)
+                       return 0;
+       } else if ((unsigned)(name -= '1') <= '9' - '1') {
+               ap = shellparam.p;
+               do {
+                       if (*ap++ == NULL)
+                               return 0;
+               } while (--name >= 0);
+       }
+       return 1;
+}
+
+
+
+/*
+ * Add the value of a specialized variable to the stack string.
+ */
+
+STATIC void
+varvalue(name, quoted, allow_split)
+       char name;
+       int quoted;
+       int allow_split;
+{
+       int num;
+       char *p;
+       int i;
+       extern int oexitstatus;
+       char sep;
+       char **ap;
+       char const *syntax;
+
+#define STRTODEST(p) \
+       do {\
+       if (allow_split) { \
+               syntax = quoted? DQSYNTAX : BASESYNTAX; \
+               while (*p) { \
+                       if (syntax[*p] == CCTL) \
+                               STPUTC(CTLESC, expdest); \
+                       STPUTC(*p++, expdest); \
+               } \
+       } else \
+               while (*p) \
+                       STPUTC(*p++, expdest); \
+       } while (0)
+
+
+       switch (name) {
+       case '$':
+               num = rootpid;
+               goto numvar;
+       case '?':
+               num = oexitstatus;
+               goto numvar;
+       case '#':
+               num = shellparam.nparam;
+               goto numvar;
+       case '!':
+               num = backgndpid;
+numvar:
+               expdest = cvtnum(num, expdest);
+               break;
+       case '-':
+               for (i = 0 ; i < NOPTS ; i++) {
+                       if (optlist[i].val)
+                               STPUTC(optlist[i].letter, expdest);
+               }
+               break;
+       case '@':
+               if (allow_split) {
+                       sep = '\0';
+                       goto allargs;
+               }
+               /* fall through */                      
+       case '*':
+               sep = ' ';
+allargs:
+               for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
+                       STRTODEST(p);
+                       if (*ap)
+                               STPUTC(sep, expdest);
+               }
+               break;
+       case '0':
+               p = arg0;
+               STRTODEST(p);
+               break;
+       default:
+               if ((unsigned)(name -= '1') <= '9' - '1') {
+                       p = shellparam.p[name];
+                       STRTODEST(p);
+               }
+               break;
+       }
+}
+
+
+
+/*
+ * Record the the fact that we have to scan this region of the
+ * string for IFS characters.
+ */
+
+STATIC void
+recordregion(start, end, nulonly) 
+       int start;
+       int end;
+       int nulonly;
+{
+       register struct ifsregion *ifsp;
+
+       if (ifslastp == NULL) {
+               ifsp = &ifsfirst;
+       } else {
+               ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion));
+               ifslastp->next = ifsp;
+       }
+       ifslastp = ifsp;
+       ifslastp->next = NULL;
+       ifslastp->begoff = start;
+       ifslastp->endoff = end;
+       ifslastp->nulonly = nulonly;
+}
+
+
+
+/*
+ * Break the argument string into pieces based upon IFS and add the
+ * strings to the argument list.  The regions of the string to be
+ * searched for IFS characters have been stored by recordregion.
+ */
+STATIC void
+ifsbreakup(string, arglist)
+       char *string;
+       struct arglist *arglist;
+       {
+       struct ifsregion *ifsp;
+       struct strlist *sp;
+       char *start;
+       register char *p;
+       char *q;
+       char *ifs;
+       int ifsspc;
+
+
+       start = string;
+       if (ifslastp != NULL) {
+               ifsp = &ifsfirst;
+               do {
+                       p = string + ifsp->begoff;
+                       ifs = ifsp->nulonly? nullstr : ifsval();
+                       ifsspc = strchr(ifs, ' ') != NULL;
+                       while (p < string + ifsp->endoff) {
+                               q = p;
+                               if (*p == CTLESC)
+                                       p++;
+                               if (strchr(ifs, *p++)) {
+                                       if (q > start || !ifsspc) {
+                                               *q = '\0';
+                                               sp = (struct strlist *)stalloc(sizeof *sp);
+                                               sp->text = start;
+                                               *arglist->lastp = sp;
+                                               arglist->lastp = &sp->next;
+                                       }
+                                       if (ifsspc) {
+                                               for (;;) {
+                                                       if (p >= string + ifsp->endoff)
+                                                               break;
+                                                       q = p;
+                                                       if (*p == CTLESC)
+                                                               p++;
+                                                       if (strchr(ifs, *p++) == NULL) {
+                                                               p = q;
+                                                               break;
+                                                       }
+                                               }
+                                       }
+                                       start = p;
+                               }
+                       }
+               } while ((ifsp = ifsp->next) != NULL);
+               if (*start || (!ifsspc && start > string)) {
+                       sp = (struct strlist *)stalloc(sizeof *sp);
+                       sp->text = start;
+                       *arglist->lastp = sp;
+                       arglist->lastp = &sp->next;
+               }
+       } else {
+               sp = (struct strlist *)stalloc(sizeof *sp);
+               sp->text = start;
+               *arglist->lastp = sp;
+               arglist->lastp = &sp->next;
+       }
+}
+
+
+
+/*
+ * Expand shell metacharacters.  At this point, the only control characters
+ * should be escapes.  The results are stored in the list exparg.
+ */
+
+char *expdir;
+
+
+STATIC void
+expandmeta(str, flag)
+       struct strlist *str;
+       int flag;
+{
+       char *p;
+       struct strlist **savelastp;
+       struct strlist *sp;
+       char c;
+       /* TODO - EXP_REDIR */
+
+       while (str) {
+               if (fflag)
+                       goto nometa;
+               p = str->text;
+               for (;;) {                      /* fast check for meta chars */
+                       if ((c = *p++) == '\0')
+                               goto nometa;
+                       if (c == '*' || c == '?' || c == '[' || c == '!')
+                               break;
+               }
+               savelastp = exparg.lastp;
+               INTOFF;
+               if (expdir == NULL) {
+                       int i = strlen(str->text);
+                       expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */
+               }
+
+               expmeta(expdir, str->text);
+               ckfree(expdir);
+               expdir = NULL;
+               INTON;
+               if (exparg.lastp == savelastp) {
+                       /* 
+                        * no matches 
+                        */
+nometa:
+                       *exparg.lastp = str;
+                       rmescapes(str->text);
+                       exparg.lastp = &str->next;
+               } else {
+                       *exparg.lastp = NULL;
+                       *savelastp = sp = expsort(*savelastp);
+                       while (sp->next != NULL)
+                               sp = sp->next;
+                       exparg.lastp = &sp->next;
+               }
+               str = str->next;
+       }
+}
+
+
+/*
+ * Do metacharacter (i.e. *, ?, [...]) expansion.
+ */
+
+STATIC void
+expmeta(enddir, name)
+       char *enddir;
+       char *name;
+       {
+       register char *p;
+       char *q;
+       char *start;
+       char *endname;
+       int metaflag;
+       struct stat statb;
+       DIR *dirp;
+       struct dirent *dp;
+       int atend;
+       int matchdot;
+
+       metaflag = 0;
+       start = name;
+       for (p = name ; ; p++) {
+               if (*p == '*' || *p == '?')
+                       metaflag = 1;
+               else if (*p == '[') {
+                       q = p + 1;
+                       if (*q == '!')
+                               q++;
+                       for (;;) {
+                               if (*q == CTLESC)
+                                       q++;
+                               if (*q == '/' || *q == '\0')
+                                       break;
+                               if (*++q == ']') {
+                                       metaflag = 1;
+                                       break;
+                               }
+                       }
+               } else if (*p == '!' && p[1] == '!'     && (p == name || p[-1] == '/')) {
+                       metaflag = 1;
+               } else if (*p == '\0')
+                       break;
+               else if (*p == CTLESC)
+                       p++;
+               if (*p == '/') {
+                       if (metaflag)
+                               break;
+                       start = p + 1;
+               }
+       }
+       if (metaflag == 0) {    /* we've reached the end of the file name */
+               if (enddir != expdir)
+                       metaflag++;
+               for (p = name ; ; p++) {
+                       if (*p == CTLESC)
+                               p++;
+                       *enddir++ = *p;
+                       if (*p == '\0')
+                               break;
+               }
+               if (metaflag == 0 || stat(expdir, &statb) >= 0)
+                       addfname(expdir);
+               return;
+       }
+       endname = p;
+       if (start != name) {
+               p = name;
+               while (p < start) {
+                       if (*p == CTLESC)
+                               p++;
+                       *enddir++ = *p++;
+               }
+       }
+       if (enddir == expdir) {
+               p = ".";
+       } else if (enddir == expdir + 1 && *expdir == '/') {
+               p = "/";
+       } else {
+               p = expdir;
+               enddir[-1] = '\0';
+       }
+       if ((dirp = opendir(p)) == NULL)
+               return;
+       if (enddir != expdir)
+               enddir[-1] = '/';
+       if (*endname == 0) {
+               atend = 1;
+       } else {
+               atend = 0;
+               *endname++ = '\0';
+       }
+       matchdot = 0;
+       if (start[0] == '.' || (start[0] == CTLESC && start[1] == '.'))
+               matchdot++;
+       while (! int_pending() && (dp = readdir(dirp)) != NULL) {
+               if (dp->d_name[0] == '.' && ! matchdot)
+                       continue;
+               if (patmatch(start, dp->d_name)) {
+                       if (atend) {
+                               scopy(dp->d_name, enddir);
+                               addfname(expdir);
+                       } else {
+                               char *q;
+                               for (p = enddir, q = dp->d_name;
+                                    (*p++ = *q++) != '\0';)
+                                       continue;
+                               p[-1] = '/';
+                               expmeta(p, endname);
+                       }
+               }
+       }
+       closedir(dirp);
+       if (! atend)
+               endname[-1] = '/';
+}
+
+
+/*
+ * Add a file name to the list.
+ */
+
+STATIC void
+addfname(name)
+       char *name;
+       {
+       char *p;
+       struct strlist *sp;
+
+       p = stalloc(strlen(name) + 1);
+       scopy(name, p);
+       sp = (struct strlist *)stalloc(sizeof *sp);
+       sp->text = p;
+       *exparg.lastp = sp;
+       exparg.lastp = &sp->next;
+}
+
+
+/*
+ * Sort the results of file name expansion.  It calculates the number of
+ * strings to sort and then calls msort (short for merge sort) to do the
+ * work.
+ */
+
+STATIC struct strlist *
+expsort(str)
+       struct strlist *str;
+       {
+       int len;
+       struct strlist *sp;
+
+       len = 0;
+       for (sp = str ; sp ; sp = sp->next)
+               len++;
+       return msort(str, len);
+}
+
+
+STATIC struct strlist *
+msort(list, len)
+       struct strlist *list;
+       int len;
+{
+       struct strlist *p, *q;
+       struct strlist **lpp;
+       int half;
+       int n;
+
+       if (len <= 1)
+               return list;
+       half = len >> 1;      
+       p = list;
+       for (n = half ; --n >= 0 ; ) {
+               q = p;
+               p = p->next;
+       }
+       q->next = NULL;                 /* terminate first half of list */
+       q = msort(list, half);          /* sort first half of list */
+       p = msort(p, len - half);               /* sort second half */
+       lpp = &list;
+       for (;;) {
+               if (strcmp(p->text, q->text) < 0) {
+                       *lpp = p;
+                       lpp = &p->next;
+                       if ((p = *lpp) == NULL) {
+                               *lpp = q;
+                               break;
+                       }
+               } else {
+                       *lpp = q;
+                       lpp = &q->next;
+                       if ((q = *lpp) == NULL) {
+                               *lpp = p;
+                               break;
+                       }
+               }
+       }
+       return list;
+}
+
+
+
+/*
+ * Returns true if the pattern matches the string.
+ */
+
+int
+patmatch(pattern, string)
+       char *pattern;
+       char *string;
+       {
+#ifdef notdef
+       if (pattern[0] == '!' && pattern[1] == '!')
+               return 1 - pmatch(pattern + 2, string);
+       else
+#endif
+               return pmatch(pattern, string);
+}
+
+
+STATIC int
+pmatch(pattern, string)
+       char *pattern;
+       char *string;
+       {
+       register char *p, *q;
+       register char c;
+
+       p = pattern;
+       q = string;
+       for (;;) {
+               switch (c = *p++) {
+               case '\0':
+                       goto breakloop;
+               case CTLESC:
+                       if (*q++ != *p++)
+                               return 0;
+                       break;
+               case '?':
+                       if (*q++ == '\0')
+                               return 0;
+                       break;
+               case '*':
+                       c = *p;
+                       if (c != CTLESC && c != '?' && c != '*' && c != '[') {
+                               while (*q != c) {
+                                       if (*q == '\0')
+                                               return 0;
+                                       q++;
+                               }
+                       }
+                       do {
+                               if (pmatch(p, q))
+                                       return 1;
+                       } while (*q++ != '\0');
+                       return 0;
+               case '[': {
+                       char *endp;
+                       int invert, found;
+                       char chr;
+
+                       endp = p;
+                       if (*endp == '!')
+                               endp++;
+                       for (;;) {
+                               if (*endp == '\0')
+                                       goto dft;               /* no matching ] */
+                               if (*endp == CTLESC)
+                                       endp++;
+                               if (*++endp == ']')
+                                       break;
+                       }
+                       invert = 0;
+                       if (*p == '!') {
+                               invert++;
+                               p++;
+                       }
+                       found = 0;
+                       chr = *q++;
+                       if (chr == '\0')
+                               return 0;
+                       c = *p++;
+                       do {
+                               if (c == CTLESC)
+                                       c = *p++;
+                               if (*p == '-' && p[1] != ']') {
+                                       p++;
+                                       if (*p == CTLESC)
+                                               p++;
+                                       if (chr >= c && chr <= *p)
+                                               found = 1;
+                                       p++;
+                               } else {
+                                       if (chr == c)
+                                               found = 1;
+                               }
+                       } while ((c = *p++) != ']');
+                       if (found == invert)
+                               return 0;
+                       break;
+               }
+dft:           default:
+                       if (*q++ != c)
+                               return 0;
+                       break;
+               }
+       }
+breakloop:
+       if (*q != '\0')
+               return 0;
+       return 1;
+}
+
+
+
+/*
+ * Remove any CTLESC characters from a string.
+ */
+
+void
+rmescapes(str)
+       char *str;
+       {
+       register char *p, *q;
+
+       p = str;
+       while (*p != CTLESC) {
+               if (*p++ == '\0')
+                       return;
+       }
+       q = p;
+       while (*p) {
+               if (*p == CTLESC)
+                       p++;
+               *q++ = *p++;
+       }
+       *q = '\0';
+}
+
+
+
+/*
+ * See if a pattern matches in a case statement.
+ */
+
+int
+casematch(pattern, val)
+       union node *pattern;
+       char *val;
+       {
+       struct stackmark smark;
+       int result;
+       char *p;
+
+       setstackmark(&smark);
+       argbackq = pattern->narg.backquote;
+       STARTSTACKSTR(expdest);
+       ifslastp = NULL;
+       argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
+       STPUTC('\0', expdest);
+       p = grabstackstr(expdest);
+       result = patmatch(p, val);
+       popstackmark(&smark);
+       return result;
+}
+
+/*
+ * Our own itoa().
+ */
+
+STATIC char *
+cvtnum(num, buf)
+       int num;
+       char *buf;
+       {
+       char temp[32];
+       int neg = num < 0;
+       char *p = temp + 31;
+
+       temp[31] = '\0';
+
+       do {
+               *--p = num % 10 + '0';
+       } while ((num /= 10) != 0);
+
+       if (neg)
+               *--p = '-';
+
+       while (*p)
+               STPUTC(*p++, buf);
+       return buf;
+}
diff --git a/usr/src/bin/sh/expand.h b/usr/src/bin/sh/expand.h
new file mode 100644 (file)
index 0000000..d00c849
--- /dev/null
@@ -0,0 +1,62 @@
+/*-
+ * 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.
+ *
+ *     @(#)expand.h    8.2 (Berkeley) 5/4/95
+ */
+
+struct strlist {
+       struct strlist *next;
+       char *text;
+};
+
+
+struct arglist {
+       struct strlist *list;
+       struct strlist **lastp;
+};
+
+/*
+ * expandarg() flags
+ */
+#define EXP_FULL       0x1     /* perform word splitting & file globbing */
+#define EXP_TILDE      0x2     /* do normal tilde expansion */
+#define        EXP_VARTILDE    0x4     /* expand tildes in an assignment */
+#define        EXP_REDIR       0x8     /* file glob for a redirection (1 match only) */
+#define EXP_CASE       0x10    /* keeps quotes around for CASE pattern */
+
+
+union node;
+void expandhere __P((union node *, int));
+void expandarg __P((union node *, struct arglist *, int));
+void expari __P((int));
+int patmatch __P((char *, char *));
+void rmescapes __P((char *));
+int casematch __P((union node *, char *));
diff --git a/usr/src/bin/sh/funcs/cmv b/usr/src/bin/sh/funcs/cmv
new file mode 100644 (file)
index 0000000..7c5828c
--- /dev/null
@@ -0,0 +1,45 @@
+# 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.
+#
+#      @(#)cmv 8.2 (Berkeley) 5/4/95
+
+# Conditional move--don't replace an existing file.
+
+cmv() {
+       if test $# != 2
+       then    echo "cmv: arg count"
+               return 2
+       fi
+       if test -f "$2" -o -w "$2"
+       then    echo "$2 exists"
+               return 2
+       fi
+       /bin/mv "$1" "$2"
+}
diff --git a/usr/src/bin/sh/funcs/dirs b/usr/src/bin/sh/funcs/dirs
new file mode 100644 (file)
index 0000000..c621eb5
--- /dev/null
@@ -0,0 +1,69 @@
+# 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.
+#
+#      @(#)dirs        8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+       SAVE=`pwd`
+       if [ "$1" = "" ] 
+       then    if [ "$DSTACK" = "" ]
+               then    echo "pushd: directory stack empty."
+                       return 1
+               fi
+               set $DSTACK
+               cd $1 || return
+               shift 1
+               DSTACK="$*"
+       else    cd $1 > /dev/null || return
+       fi
+       DSTACK="$SAVE $DSTACK"
+       dirs
+}
+
+popd () {
+       if [ "$DSTACK" = "" ] 
+       then    echo "popd: directory stack empty."
+               return 1
+       fi
+       set $DSTACK
+       cd $1
+       shift
+       DSTACK=$*
+       dirs
+}
+
+dirs () {
+       echo "`pwd` $DSTACK"
+       return 0
+}
diff --git a/usr/src/bin/sh/funcs/kill b/usr/src/bin/sh/funcs/kill
new file mode 100644 (file)
index 0000000..124cc98
--- /dev/null
@@ -0,0 +1,45 @@
+# 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.
+#
+#      @(#)kill        8.2 (Berkeley) 5/4/95
+
+# Convert job names to process ids and then run /bin/kill.
+
+kill() {
+       local args x
+       args=
+       for x in "$@"
+       do      case $x in
+               %*)     x=`jobid "$x"` ;;
+               esac
+               args="$args $x"
+       done
+       /bin/kill $args
+}
diff --git a/usr/src/bin/sh/funcs/login b/usr/src/bin/sh/funcs/login
new file mode 100644 (file)
index 0000000..04a2753
--- /dev/null
@@ -0,0 +1,34 @@
+# 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.
+#
+#      @(#)login       8.2 (Berkeley) 5/4/95
+
+# replaces the login builtin in the BSD shell
+login () exec login "$@"
diff --git a/usr/src/bin/sh/funcs/newgrp b/usr/src/bin/sh/funcs/newgrp
new file mode 100644 (file)
index 0000000..7f3ae9f
--- /dev/null
@@ -0,0 +1,33 @@
+# 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.
+#
+#      @(#)newgrp      8.2 (Berkeley) 5/4/95
+
+newgrp() exec newgrp "$@"
diff --git a/usr/src/bin/sh/funcs/popd b/usr/src/bin/sh/funcs/popd
new file mode 100644 (file)
index 0000000..f5c429c
--- /dev/null
@@ -0,0 +1,69 @@
+# 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.
+#
+#      @(#)popd        8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+       SAVE=`pwd`
+       if [ "$1" = "" ] 
+       then    if [ "$DSTACK" = "" ]
+               then    echo "pushd: directory stack empty."
+                       return 1
+               fi
+               set $DSTACK
+               cd $1 || return
+               shift 1
+               DSTACK="$*"
+       else    cd $1 > /dev/null || return
+       fi
+       DSTACK="$SAVE $DSTACK"
+       dirs
+}
+
+popd () {
+       if [ "$DSTACK" = "" ] 
+       then    echo "popd: directory stack empty."
+               return 1
+       fi
+       set $DSTACK
+       cd $1
+       shift
+       DSTACK=$*
+       dirs
+}
+
+dirs () {
+       echo "`pwd` $DSTACK"
+       return 0
+}
diff --git a/usr/src/bin/sh/funcs/pushd b/usr/src/bin/sh/funcs/pushd
new file mode 100644 (file)
index 0000000..b320d63
--- /dev/null
@@ -0,0 +1,69 @@
+# 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.
+#
+#      @(#)pushd       8.2 (Berkeley) 5/4/95
+
+# pushd, popd, and dirs --- written by Chris Bertin
+# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris
+# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW
+
+pushd () {
+       SAVE=`pwd`
+       if [ "$1" = "" ] 
+       then    if [ "$DSTACK" = "" ]
+               then    echo "pushd: directory stack empty."
+                       return 1
+               fi
+               set $DSTACK
+               cd $1 || return
+               shift 1
+               DSTACK="$*"
+       else    cd $1 > /dev/null || return
+       fi
+       DSTACK="$SAVE $DSTACK"
+       dirs
+}
+
+popd () {
+       if [ "$DSTACK" = "" ] 
+       then    echo "popd: directory stack empty."
+               return 1
+       fi
+       set $DSTACK
+       cd $1
+       shift
+       DSTACK=$*
+       dirs
+}
+
+dirs () {
+       echo "`pwd` $DSTACK"
+       return 0
+}
diff --git a/usr/src/bin/sh/funcs/suspend b/usr/src/bin/sh/funcs/suspend
new file mode 100644 (file)
index 0000000..420246e
--- /dev/null
@@ -0,0 +1,37 @@
+# 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.
+#
+#      @(#)suspend     8.2 (Berkeley) 5/4/95
+
+suspend() {
+       local -
+       set +j
+       kill -TSTP 0
+}
diff --git a/usr/src/bin/sh/histedit.c b/usr/src/bin/sh/histedit.c
new file mode 100644 (file)
index 0000000..7f87f83
--- /dev/null
@@ -0,0 +1,472 @@
+/*-
+ * Copyright (c) 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+/*
+ * Editline and history functions (and glue).
+ */
+#include "shell.h"
+#include "parser.h"
+#include "var.h"
+#include "options.h"
+#include "main.h"
+#include "output.h"
+#include "mystring.h"
+#ifndef NO_HISTORY
+#include "myhistedit.h"
+#endif
+#include "error.h"
+#include "eval.h"
+#include "memalloc.h"
+
+#define MAXHISTLOOPS   4       /* max recursions through fc */
+#define DEFEDITOR      "ed"    /* default editor *should* be $EDITOR */
+
+History *hist; /* history cookie */
+EditLine *el;  /* editline cookie */
+int displayhist;
+static FILE *el_in, *el_out;
+
+STATIC char *fc_replace __P((const char *, char *, char *));
+
+/*
+ * Set history and editing status.  Called whenever the status may
+ * have changed (figures out what to do).
+ */
+void
+histedit() 
+{
+
+#define editing (Eflag || Vflag)
+
+       if (iflag) {
+               if (!hist) {
+                       /*
+                        * turn history on
+                        */
+                       INTOFF;
+                       hist = history_init();
+                       INTON;
+
+                       if (hist != NULL)
+                               sethistsize();
+                       else
+                               out2str("sh: can't initialize history\n");
+               }
+               if (editing && !el && isatty(0)) { /* && isatty(2) ??? */
+                       /*
+                        * turn editing on
+                        */
+                       INTOFF;
+                       if (el_in == NULL)
+                               el_in = fdopen(0, "r");
+                       if (el_out == NULL)
+                               el_out = fdopen(2, "w");
+                       if (el_in == NULL || el_out == NULL)
+                               goto bad;
+                       el = el_init(arg0, el_in, el_out);
+                       if (el != NULL) {
+                               if (hist)
+                                       el_set(el, EL_HIST, history, hist);
+                               el_set(el, EL_PROMPT, getprompt);
+                       } else {
+bad:
+                               out2str("sh: can't initialize editing\n");
+                       }
+                       INTON;
+               } else if (!editing && el) {
+                       INTOFF;
+                       el_end(el);
+                       el = NULL;
+                       INTON;
+               }
+               if (el) {
+                       if (Vflag)
+                               el_set(el, EL_EDITOR, "vi");
+                       else if (Eflag)
+                               el_set(el, EL_EDITOR, "emacs");
+               }
+       } else {
+               INTOFF;
+               if (el) {       /* no editing if not interactive */
+                       el_end(el);
+                       el = NULL;
+               }
+               if (hist) {
+                       history_end(hist);
+                       hist = NULL;
+               }
+               INTON;
+       }
+}
+
+
+void
+sethistsize()
+{
+       char *cp;
+       int histsize;
+
+       if (hist != NULL) {
+               cp = lookupvar("HISTSIZE");
+               if (cp == NULL || *cp == '\0' || 
+                  (histsize = atoi(cp)) < 0)
+                       histsize = 100;
+               history(hist, H_EVENT, histsize);
+       }
+}
+
+/*
+ *  This command is provided since POSIX decided to standardize
+ *  the Korn shell fc command.  Oh well...
+ */
+int
+histcmd(argc, argv)
+       int argc;
+       char **argv;
+{
+       extern char *optarg;
+       extern int optind, optopt, optreset;
+       int ch;
+       char *editor = NULL;
+       const HistEvent *he;
+       int lflg = 0, nflg = 0, rflg = 0, sflg = 0;
+       int i;
+       char *firststr, *laststr;
+       int first, last, direction;
+       char *pat = NULL, *repl;        /* ksh "fc old=new" crap */
+       static int active = 0;
+       struct jmploc jmploc;
+       struct jmploc *volatile savehandler;
+       char editfile[MAXPATHLEN + 1];
+       FILE *efp;
+#ifdef __GNUC__
+       /* Avoid longjmp clobbering */
+       (void) &editor;
+       (void) &lflg;
+       (void) &nflg;
+       (void) &rflg;
+       (void) &sflg;
+       (void) &firststr;
+       (void) &laststr;
+       (void) &pat;
+       (void) &repl;
+       (void) &efp;
+       (void) &argc;
+       (void) &argv;
+#endif
+
+       if (hist == NULL)
+               error("history not active");
+       
+       if (argc == 1)
+               error("missing history argument");
+
+       optreset = 1; optind = 1; /* initialize getopt */
+       while (not_fcnumber(argv[optind]) &&
+             (ch = getopt(argc, argv, ":e:lnrs")) != EOF)
+               switch ((char)ch) {
+               case 'e':
+                       editor = optarg;
+                       break;
+               case 'l':
+                       lflg = 1;
+                       break;
+               case 'n':
+                       nflg = 1;
+                       break;
+               case 'r':
+                       rflg = 1;
+                       break;
+               case 's':
+                       sflg = 1;
+                       break;
+               case ':':
+                       error("option -%c expects argument", optopt);
+               case '?':
+               default:
+                       error("unknown option: -%c", optopt);
+               }
+       argc -= optind, argv += optind;
+
+       /*
+        * If executing...
+        */
+       if (lflg == 0 || editor || sflg) {
+               lflg = 0;       /* ignore */
+               editfile[0] = '\0';
+               /*
+                * Catch interrupts to reset active counter and
+                * cleanup temp files.
+                */
+               if (setjmp(jmploc.loc)) {
+                       active = 0;
+                       if (*editfile)
+                               unlink(editfile);
+                       handler = savehandler;
+                       longjmp(handler->loc, 1);
+               }
+               savehandler = handler;
+               handler = &jmploc;
+               if (++active > MAXHISTLOOPS) {
+                       active = 0;
+                       displayhist = 0;
+                       error("called recursively too many times");
+               }
+               /*
+                * Set editor.
+                */
+               if (sflg == 0) {
+                       if (editor == NULL &&
+                           (editor = bltinlookup("FCEDIT", 1)) == NULL &&
+                           (editor = bltinlookup("EDITOR", 1)) == NULL)
+                               editor = DEFEDITOR;
+                       if (editor[0] == '-' && editor[1] == '\0') {
+                               sflg = 1;       /* no edit */
+                               editor = NULL;
+                       }
+               }
+       }
+
+       /*
+        * If executing, parse [old=new] now
+        */
+       if (lflg == 0 && argc > 0 && 
+            ((repl = strchr(argv[0], '=')) != NULL)) {
+               pat = argv[0];
+               *repl++ = '\0';
+               argc--, argv++;
+       }
+       /*
+        * determine [first] and [last]
+        */
+       switch (argc) {
+       case 0:
+               firststr = lflg ? "-16" : "-1";
+               laststr = "-1";
+               break;
+       case 1:
+               firststr = argv[0];
+               laststr = lflg ? "-1" : argv[0];
+               break;
+       case 2:
+               firststr = argv[0];
+               laststr = argv[1];
+               break;
+       default:
+               error("too many args");
+       }
+       /*
+        * Turn into event numbers.
+        */
+       first = str_to_event(firststr, 0);
+       last = str_to_event(laststr, 1);
+
+       if (rflg) {
+               i = last;
+               last = first;
+               first = i;
+       }
+       /*
+        * XXX - this should not depend on the event numbers
+        * always increasing.  Add sequence numbers or offset 
+        * to the history element in next (diskbased) release.
+        */
+       direction = first < last ? H_PREV : H_NEXT;
+
+       /*
+        * If editing, grab a temp file.
+        */
+       if (editor) {
+               int fd;
+               INTOFF;         /* easier */
+               sprintf(editfile, "%s/_shXXXXXX", _PATH_TMP);
+               if ((fd = mkstemp(editfile)) < 0)
+                       error("can't create temporary file %s", editfile);
+               if ((efp = fdopen(fd, "w")) == NULL) {
+                       close(fd);
+                       error("can't allocate stdio buffer for temp\n");
+               }
+       }
+
+       /*
+        * Loop through selected history events.  If listing or executing,
+        * do it now.  Otherwise, put into temp file and call the editor
+        * after.
+        *
+        * The history interface needs rethinking, as the following
+        * convolutions will demonstrate.
+        */
+       history(hist, H_FIRST);
+       he = history(hist, H_NEXT_EVENT, first);
+       for (;he != NULL; he = history(hist, direction)) {
+               if (lflg) {
+                       if (!nflg)
+                               out1fmt("%5d ", he->num);
+                       out1str(he->str);
+               } else {
+                       char *s = pat ? 
+                          fc_replace(he->str, pat, repl) : (char *)he->str;
+
+                       if (sflg) {
+                               if (displayhist) {
+                                       out2str(s);
+                               }
+                               evalstring(s);
+                               if (displayhist && hist) {
+                                       /*
+                                        *  XXX what about recursive and 
+                                        *  relative histnums.
+                                        */
+                                       history(hist, H_ENTER, s);
+                               }
+                       } else
+                               fputs(s, efp);
+               }
+               /*
+                * At end?  (if we were to loose last, we'd sure be
+                * messed up).
+                */
+               if (he->num == last)
+                       break;
+       }
+       if (editor) {
+               char *editcmd;
+
+               fclose(efp);
+               editcmd = stalloc(strlen(editor) + strlen(editfile) + 2);
+               sprintf(editcmd, "%s %s", editor, editfile);
+               evalstring(editcmd);    /* XXX - should use no JC command */
+               INTON;
+               readcmdfile(editfile);  /* XXX - should read back - quick tst */
+               unlink(editfile);
+       }
+               
+       if (lflg == 0 && active > 0)
+               --active;
+       if (displayhist)
+               displayhist = 0;
+       return 0;
+}
+
+STATIC char *
+fc_replace(s, p, r)
+       const char *s;
+       char *p, *r;
+{
+       char *dest;
+       int plen = strlen(p);
+
+       STARTSTACKSTR(dest);
+       while (*s) {
+               if (*s == *p && strncmp(s, p, plen) == 0) {
+                       while (*r)
+                               STPUTC(*r++, dest);
+                       s += plen;
+                       *p = '\0';      /* so no more matches */
+               } else
+                       STPUTC(*s++, dest);
+       }
+       STACKSTRNUL(dest);
+       dest = grabstackstr(dest);
+
+       return (dest);
+}
+
+int
+not_fcnumber(s)
+        char *s;
+{
+       if (s == NULL)
+               return 0;
+        if (*s == '-')
+                s++;
+       return (!is_number(s));
+}
+
+int
+str_to_event(str, last)
+       char *str;
+       int last;
+{
+       const HistEvent *he;
+       char *s = str;
+       int relative = 0;
+       int i;
+
+       he = history(hist, H_FIRST);
+       switch (*s) {
+       case '-':
+               relative = 1;
+               /*FALLTHROUGH*/
+       case '+':
+               s++;
+       }
+       if (is_number(s)) {
+               i = atoi(s);
+               if (relative) {
+                       while (he != NULL && i--) {
+                               he = history(hist, H_NEXT);
+                       }
+                       if (he == NULL)
+                               he = history(hist, H_LAST);
+               } else {
+                       he = history(hist, H_NEXT_EVENT, i);
+                       if (he == NULL) {
+                               /*
+                                * the notion of first and last is
+                                * backwards to that of the history package
+                                */
+                               he = history(hist, last ? H_FIRST : H_LAST);
+                       }
+               }
+               if (he == NULL)
+                       error("history number %s not found (internal error)",
+                              str);
+       } else {
+               /*
+                * pattern 
+                */
+               he = history(hist, H_PREV_STR, str);
+               if (he == NULL)
+                       error("history pattern not found: %s", str);
+       }
+       return (he->num);
+}
diff --git a/usr/src/bin/sh/init.h b/usr/src/bin/sh/init.h
new file mode 100644 (file)
index 0000000..f025bfa
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * 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.
+ *
+ *     @(#)init.h      8.2 (Berkeley) 5/4/95
+ */
+
+void init __P((void));
+void reset __P((void));
+void initshellproc __P((void));
diff --git a/usr/src/bin/sh/input.c b/usr/src/bin/sh/input.c
new file mode 100644 (file)
index 0000000..3fe8d16
--- /dev/null
@@ -0,0 +1,474 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c    8.3 (Berkeley) 6/9/95";
+#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"
+
+#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;
+       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 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 */
+MKINIT int parsenleft;         /* copy of parsefile->nleft */
+char *parsenextc;              /* copy of parsefile->nextc */
+MKINIT struct parsefile basepf;        /* top level input file */
+char basebuf[BUFSIZ];          /* buffer for top level input file */
+struct parsefile *parsefile = &basepf; /* current input file */
+char *pushedstring;            /* copy of parsenextc when text pushed back */
+int pushednleft;               /* copy of parsenleft when text pushed back */
+int init_editline = 0;         /* editline library initialized? */
+int whichprompt;               /* 1 == PS1, 2 == PS2 */
+
+EditLine *el;                  /* cookie for editline package */
+
+STATIC void pushfile __P((void));
+
+#ifdef mkinit
+INCLUDE "input.h"
+INCLUDE "error.h"
+
+INIT {
+       extern char basebuf[];
+
+       basepf.nextc = basepf.buf = basebuf;
+}
+
+RESET {
+       if (exception != EXSHELLPROC)
+               parsenleft = 0;            /* clear input buffer */
+       popallfiles();
+}
+
+SHELLPROC {
+       popallfiles();
+}
+#endif
+
+
+/*
+ * Read a line from the script.
+ */
+
+char *
+pfgets(line, len)
+       char *line;
+       int len;
+{
+       register 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() {
+       return pgetc_macro();
+}
+
+
+/*
+ * 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) Call read to read in the characters.
+ * 4) Delete all nul characters from the buffer.
+ */
+
+int
+preadbuffer() {
+       register char *p, *q;
+       register int i;
+       register int something;
+       extern EditLine *el;
+
+       if (parsefile->strpush) {
+               popstring();
+               if (--parsenleft >= 0)
+                       return (*parsenextc++);
+       }
+       if (parsenleft == EOF_NLEFT || parsefile->buf == NULL)
+               return PEOF;
+       flushout(&output);
+       flushout(&errout);
+retry:
+       p = parsenextc = parsefile->buf;
+       if (parsefile->fd == 0 && el) {
+               const char *rl_cp;
+               int len;
+
+               rl_cp = el_gets(el, &len);
+               if (rl_cp == NULL) {
+                       i = 0;
+                       goto eof;
+               }
+               strcpy(p, rl_cp);  /* XXX - BUFSIZE should redesign so not necessary */
+               i = len;
+
+       } else {
+               i = read(parsefile->fd, p, BUFSIZ - 1);
+       }
+eof:
+       if (i <= 0) {
+                if (i < 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;
+                                        }
+                                }
+                        }
+                }
+                parsenleft = EOF_NLEFT;
+                return PEOF;
+       }
+       parsenleft = i - 1;     /* we're returning one char in this call */
+
+       /* delete nul characters */
+       something = 0;
+       for (;;) {
+               if (*p == '\0')
+                       break;
+               if (*p != ' ' && *p != '\t' && *p != '\n')
+                       something = 1;
+               p++;
+               if (--i <= 0) {
+                       *p = '\0';
+                       goto done;              /* no nul characters */
+               }
+       }
+       /*
+        * remove nuls
+        */
+       q = p++;
+       while (--i > 0) {
+               if (*p != '\0')
+                       *q++ = *p;
+               p++;
+       }
+       *q = '\0';
+       if (q == parsefile->buf)
+               goto retry;                     /* buffer contained nothing but nuls */
+       parsenleft = q - parsefile->buf - 1;
+
+done:
+       if (parsefile->fd == 0 && hist && something) {
+               INTOFF;
+               history(hist, whichprompt == 1 ? H_ENTER : H_ADD, 
+                          parsefile->buf);
+               INTON;
+       }
+       if (vflag) {
+               /*
+                * This isn't right.  Most shells coordinate it with
+                * reading a line at a time.  I honestly don't know if its
+                * worth it.
+                */
+               i = parsenleft + 1;
+               p = parsefile->buf;
+               for (; i--; p++) 
+                       out2c(*p)
+               flushout(out2);
+       }
+       return *parsenextc++;
+}
+
+/*
+ * Undo the last call to pgetc.  Only one character may be pushed back.
+ * PEOF may be pushed back.
+ */
+
+void
+pungetc() {
+       parsenleft++;
+       parsenextc--;
+}
+
+/*
+ * Push a string back onto the input at this current parsefile level.
+ * We handle aliases this way.
+ */
+void
+pushstring(s, len, ap)
+       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->ap = (struct alias *)ap;
+       if (ap)
+               ((struct alias *)ap)->flag |= ALIASINUSE;
+       parsenextc = s;
+       parsenleft = len;
+       INTON;
+}
+
+void
+popstring()
+{
+       struct strpush *sp = parsefile->strpush;
+
+       INTOFF;
+       parsenextc = sp->prevstring;
+       parsenleft = sp->prevnleft;
+/*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(fname, push)
+       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(fd, push)
+       int fd, push;
+{
+       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);
+       parsenleft = 0;
+       plinno = 1;
+}
+
+
+/*
+ * Like setinputfile, but takes input from a string.
+ */
+
+void
+setinputstring(string, push)
+       char *string;
+       int push;
+       {
+       INTOFF;
+       if (push)
+               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() {
+       struct parsefile *pf;
+
+       parsefile->nleft = parsenleft;
+       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() {
+       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;
+       parsenextc = parsefile->nextc;
+       plinno = parsefile->linno;
+       INTON;
+}
+
+
+/*
+ * Return to top level.
+ */
+
+void
+popallfiles() {
+       while (parsefile != &basepf)
+               popfile();
+}
+
+
+
+/*
+ * Close the file(s) that the shell is reading commands from.  Called
+ * after a fork is done.
+ */
+
+void
+closescript() {
+       popallfiles();
+       if (parsefile->fd > 0) {
+               close(parsefile->fd);
+               parsefile->fd = 0;
+       }
+}
diff --git a/usr/src/bin/sh/input.h b/usr/src/bin/sh/input.h
new file mode 100644 (file)
index 0000000..997e98c
--- /dev/null
@@ -0,0 +1,60 @@
+/*-
+ * 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.
+ *
+ *     @(#)input.h     8.2 (Berkeley) 5/4/95
+ */
+
+/* PEOF (the end of file marker) is defined in syntax.h */
+
+/*
+ * The input line number.  Input.c just defines this variable, and saves
+ * and restores it when files are pushed and popped.  The user of this
+ * package must set its value.
+ */
+extern int plinno;
+extern int parsenleft;         /* number of characters left in input buffer */
+extern char *parsenextc;       /* next character in input buffer */
+extern int init_editline;      /* 0 == not setup, 1 == OK, -1 == failed */
+
+char *pfgets __P((char *, int));
+int pgetc __P((void));
+int preadbuffer __P((void));
+void pungetc __P((void));
+void pushstring __P((char *, int, void *));
+void popstring __P((void));
+void setinputfile __P((char *, int));
+void setinputfd __P((int, int));
+void setinputstring __P((char *, int)); 
+void popfile __P((void));
+void popallfiles __P((void));
+void closescript __P((void));
+
+#define pgetc_macro()  (--parsenleft >= 0? *parsenextc++ : preadbuffer())
diff --git a/usr/src/bin/sh/jobs.c b/usr/src/bin/sh/jobs.c
new file mode 100644 (file)
index 0000000..184b96a
--- /dev/null
@@ -0,0 +1,1069 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)jobs.c     8.5 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#ifdef BSD
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#include "shell.h"
+#if JOBS
+#include "sgtty.h"
+#undef CEOF                    /* syntax.h redefines this */
+#endif
+#include "redir.h"
+#include "show.h"
+#include "main.h"
+#include "parser.h"
+#include "nodes.h"
+#include "jobs.h"
+#include "options.h"
+#include "trap.h"
+#include "syntax.h"
+#include "input.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include "mystring.h"
+
+
+struct job *jobtab;            /* array of jobs */
+int njobs;                     /* size of array */
+MKINIT short backgndpid = -1;  /* pid of last background process */
+#if JOBS
+int initialpgrp;               /* pgrp of shell on invocation */
+short curjob;                  /* current job */
+#endif
+
+STATIC void restartjob __P((struct job *));
+STATIC void freejob __P((struct job *));
+STATIC struct job *getjob __P((char *));
+STATIC int dowait __P((int, struct job *));
+STATIC int onsigchild __P((void));
+STATIC int waitproc __P((int, int *));
+STATIC void cmdtxt __P((union node *));
+STATIC void cmdputs __P((char *));
+
+
+/*
+ * Turn job control on and off.
+ *
+ * Note:  This code assumes that the third arg to ioctl is a character
+ * pointer, which is true on Berkeley systems but not System V.  Since
+ * System V doesn't have job control yet, this isn't a problem now.
+ */
+
+MKINIT int jobctl;
+
+void
+setjobctl(on) 
+       int on;
+{
+#ifdef OLD_TTY_DRIVER
+       int ldisc;
+#endif
+
+       if (on == jobctl || rootshell == 0)
+               return;
+       if (on) {
+               do { /* while we are in the background */
+                       if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
+                               out2str("sh: can't access tty; job control turned off\n");
+                               mflag = 0;
+                               return;
+                       }
+                       if (initialpgrp == -1)
+                               initialpgrp = getpgrp();
+                       else if (initialpgrp != getpgrp()) {
+                               killpg(initialpgrp, SIGTTIN);
+                               continue;
+                       }
+               } while (0);
+#ifdef OLD_TTY_DRIVER
+               if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
+                       out2str("sh: need new tty driver to run job control; job control turned off\n");
+                       mflag = 0;
+                       return;
+               }
+#endif
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+               setsignal(SIGTTIN);
+               setpgid(0, rootpid);
+               ioctl(2, TIOCSPGRP, (char *)&rootpid);
+       } else { /* turning job control off */
+               setpgid(0, initialpgrp);
+               ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
+               setsignal(SIGTSTP);
+               setsignal(SIGTTOU);
+               setsignal(SIGTTIN);
+       }
+       jobctl = on;
+}
+
+
+#ifdef mkinit
+INCLUDE <stdlib.h>
+
+SHELLPROC {
+       backgndpid = -1;
+#if JOBS
+       jobctl = 0;
+#endif
+}
+
+#endif
+
+
+
+#if JOBS
+int
+fgcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       struct job *jp;
+       int pgrp;
+       int status;
+
+       jp = getjob(argv[1]);
+       if (jp->jobctl == 0)
+               error("job not created under job control");
+       pgrp = jp->ps[0].pid;
+       ioctl(2, TIOCSPGRP, (char *)&pgrp);
+       restartjob(jp);
+       INTOFF;
+       status = waitforjob(jp);
+       INTON;
+       return status;
+}
+
+
+int
+bgcmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       struct job *jp;
+
+       do {
+               jp = getjob(*++argv);
+               if (jp->jobctl == 0)
+                       error("job not created under job control");
+               restartjob(jp);
+       } while (--argc > 1);
+       return 0;
+}
+
+
+STATIC void
+restartjob(jp)
+       struct job *jp;
+{
+       struct procstat *ps;
+       int i;
+
+       if (jp->state == JOBDONE)
+               return;
+       INTOFF;
+       killpg(jp->ps[0].pid, SIGCONT);
+       for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
+               if ((ps->status & 0377) == 0177) {
+                       ps->status = -1;
+                       jp->state = 0;
+               }
+       }
+       INTON;
+}
+#endif
+
+
+int
+jobscmd(argc, argv)
+       int argc;
+       char **argv; 
+{
+       showjobs(0);
+       return 0;
+}
+
+
+/*
+ * Print a list of jobs.  If "change" is nonzero, only print jobs whose
+ * statuses have changed since the last call to showjobs.
+ *
+ * If the shell is interrupted in the process of creating a job, the
+ * result may be a job structure containing zero processes.  Such structures
+ * will be freed here.
+ */
+
+void
+showjobs(change) 
+       int change;
+{
+       int jobno;
+       int procno;
+       int i;
+       struct job *jp;
+       struct procstat *ps;
+       int col;
+       char s[64];
+
+       TRACE(("showjobs(%d) called\n", change));
+       while (dowait(0, (struct job *)NULL) > 0);
+       for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
+               if (! jp->used)
+                       continue;
+               if (jp->nprocs == 0) {
+                       freejob(jp);
+                       continue;
+               }
+               if (change && ! jp->changed)
+                       continue;
+               procno = jp->nprocs;
+               for (ps = jp->ps ; ; ps++) {    /* for each process */
+                       if (ps == jp->ps)
+                               fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
+                       else
+                               fmtstr(s, 64, "    %d ", ps->pid);
+                       out1str(s);
+                       col = strlen(s);
+                       s[0] = '\0';
+                       if (ps->status == -1) {
+                               /* don't print anything */
+                       } else if ((ps->status & 0xFF) == 0) {
+                               fmtstr(s, 64, "Exit %d", ps->status >> 8);
+                       } else {
+                               i = ps->status;
+#if JOBS
+                               if ((i & 0xFF) == 0177)
+                                       i >>= 8;
+#endif
+                               if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F])
+                                       scopy(sys_siglist[i & 0x7F], s);
+                               else
+                                       fmtstr(s, 64, "Signal %d", i & 0x7F);
+                               if (i & 0x80)
+                                       strcat(s, " (core dumped)");
+                       }
+                       out1str(s);
+                       col += strlen(s);
+                       do {
+                               out1c(' ');
+                               col++;
+                       } while (col < 30);
+                       out1str(ps->cmd);
+                       out1c('\n');
+                       if (--procno <= 0)
+                               break;
+               }
+               jp->changed = 0;
+               if (jp->state == JOBDONE) {
+                       freejob(jp);
+               }
+       }
+}
+
+
+/*
+ * Mark a job structure as unused.
+ */
+
+STATIC void
+freejob(jp)
+       struct job *jp;
+       {
+       struct procstat *ps;
+       int i;
+
+       INTOFF;
+       for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
+               if (ps->cmd != nullstr)
+                       ckfree(ps->cmd);
+       }
+       if (jp->ps != &jp->ps0)
+               ckfree(jp->ps);
+       jp->used = 0;
+#if JOBS
+       if (curjob == jp - jobtab + 1)
+               curjob = 0;
+#endif
+       INTON;
+}
+
+
+
+int
+waitcmd(argc, argv) 
+       int argc;
+       char **argv; 
+{
+       struct job *job;
+       int status;
+       struct job *jp;
+
+       if (argc > 1) {
+               job = getjob(argv[1]);
+       } else {
+               job = NULL;
+       }
+       for (;;) {      /* loop until process terminated or stopped */
+               if (job != NULL) {
+                       if (job->state) {
+                               status = job->ps[job->nprocs - 1].status;
+                               if ((status & 0xFF) == 0)
+                                       status = status >> 8 & 0xFF;
+#if JOBS
+                               else if ((status & 0xFF) == 0177)
+                                       status = (status >> 8 & 0x7F) + 128;
+#endif
+                               else
+                                       status = (status & 0x7F) + 128;
+                               if (! iflag)
+                                       freejob(job);
+                               return status;
+                       }
+               } else {
+                       for (jp = jobtab ; ; jp++) {
+                               if (jp >= jobtab + njobs) {     /* no running procs */
+                                       return 0;
+                               }
+                               if (jp->used && jp->state == 0)
+                                       break;
+                       }
+               }
+               dowait(1, (struct job *)NULL);
+       }
+}
+
+
+
+int
+jobidcmd(argc, argv)  
+       int argc;
+       char **argv; 
+{
+       struct job *jp;
+       int i;
+
+       jp = getjob(argv[1]);
+       for (i = 0 ; i < jp->nprocs ; ) {
+               out1fmt("%d", jp->ps[i].pid);
+               out1c(++i < jp->nprocs? ' ' : '\n');
+       }
+       return 0;
+}
+
+
+
+/*
+ * Convert a job name to a job structure.
+ */
+
+STATIC struct job *
+getjob(name)
+       char *name;
+       {
+       int jobno;
+       register struct job *jp;
+       int pid;
+       int i;
+
+       if (name == NULL) {
+#if JOBS
+currentjob:
+               if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
+                       error("No current job");
+               return &jobtab[jobno - 1];
+#else
+               error("No current job");
+#endif
+       } else if (name[0] == '%') {
+               if (is_digit(name[1])) {
+                       jobno = number(name + 1);
+                       if (jobno > 0 && jobno <= njobs
+                        && jobtab[jobno - 1].used != 0)
+                               return &jobtab[jobno - 1];
+#if JOBS
+               } else if (name[1] == '%' && name[2] == '\0') {
+                       goto currentjob;
+#endif
+               } else {
+                       register struct job *found = NULL;
+                       for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+                               if (jp->used && jp->nprocs > 0
+                                && prefix(name + 1, jp->ps[0].cmd)) {
+                                       if (found)
+                                               error("%s: ambiguous", name);
+                                       found = jp;
+                               }
+                       }
+                       if (found)
+                               return found;
+               }
+       } else if (is_number(name)) {
+               pid = number(name);
+               for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
+                       if (jp->used && jp->nprocs > 0
+                        && jp->ps[jp->nprocs - 1].pid == pid)
+                               return jp;
+               }
+       }
+       error("No such job: %s", name);
+       /*NOTREACHED*/
+       return NULL;
+}
+
+
+
+/*
+ * Return a new job structure,
+ */
+
+struct job *
+makejob(node, nprocs)
+       union node *node;
+       int nprocs;
+{
+       int i;
+       struct job *jp;
+
+       for (i = njobs, jp = jobtab ; ; jp++) {
+               if (--i < 0) {
+                       INTOFF;
+                       if (njobs == 0) {
+                               jobtab = ckmalloc(4 * sizeof jobtab[0]);
+                       } else {
+                               jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
+                               memcpy(jp, jobtab, njobs * sizeof jp[0]);
+                               ckfree(jobtab);
+                               jobtab = jp;
+                       }
+                       jp = jobtab + njobs;
+                       for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
+                       INTON;
+                       break;
+               }
+               if (jp->used == 0)
+                       break;
+       }
+       INTOFF;
+       jp->state = 0;
+       jp->used = 1;
+       jp->changed = 0;
+       jp->nprocs = 0;
+#if JOBS
+       jp->jobctl = jobctl;
+#endif
+       if (nprocs > 1) {
+               jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
+       } else {
+               jp->ps = &jp->ps0;
+       }
+       INTON;
+       TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs,
+           jp - jobtab + 1));
+       return jp;
+}      
+
+
+/*
+ * Fork of a subshell.  If we are doing job control, give the subshell its
+ * own process group.  Jp is a job structure that the job is to be added to.
+ * N is the command that will be evaluated by the child.  Both jp and n may
+ * be NULL.  The mode parameter can be one of the following:
+ *     FORK_FG - Fork off a foreground process.
+ *     FORK_BG - Fork off a background process.
+ *     FORK_NOJOB - Like FORK_FG, but don't give the process its own
+ *                  process group even if job control is on.
+ *
+ * When job control is turned off, background processes have their standard
+ * input redirected to /dev/null (except for the second and later processes
+ * in a pipeline).
+ */
+
+int
+forkshell(jp, n, mode)
+       union node *n;
+       struct job *jp;
+       int mode;
+{
+       int pid;
+       int pgrp;
+
+       TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n,
+           mode));
+       INTOFF;
+       pid = fork();
+       if (pid == -1) {
+               TRACE(("Fork failed, errno=%d\n", errno));
+               INTON;
+               error("Cannot fork");
+       }
+       if (pid == 0) {
+               struct job *p;
+               int wasroot;
+               int i;
+
+               TRACE(("Child shell %d\n", getpid()));
+               wasroot = rootshell;
+               rootshell = 0;
+               for (i = njobs, p = jobtab ; --i >= 0 ; p++)
+                       if (p->used)
+                               freejob(p);
+               closescript();
+               INTON;
+               clear_traps();
+#if JOBS
+               jobctl = 0;             /* do job control only in root shell */
+               if (wasroot && mode != FORK_NOJOB && mflag) {
+                       if (jp == NULL || jp->nprocs == 0)
+                               pgrp = getpid();
+                       else
+                               pgrp = jp->ps[0].pid;
+                       setpgid(0, pgrp);
+                       if (mode == FORK_FG) {
+                               /*** this causes superfluous TIOCSPGRPS ***/
+                               if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
+                                       error("TIOCSPGRP failed, errno=%d\n", errno);
+                       }
+                       setsignal(SIGTSTP);
+                       setsignal(SIGTTOU);
+               } else if (mode == FORK_BG) {
+                       ignoresig(SIGINT);
+                       ignoresig(SIGQUIT);
+                       if ((jp == NULL || jp->nprocs == 0) &&
+                           ! fd0_redirected_p ()) {
+                               close(0);
+                               if (open("/dev/null", O_RDONLY) != 0)
+                                       error("Can't open /dev/null");
+                       }
+               }
+#else
+               if (mode == FORK_BG) {
+                       ignoresig(SIGINT);
+                       ignoresig(SIGQUIT);
+                       if ((jp == NULL || jp->nprocs == 0) &&
+                           ! fd0_redirected_p ()) {
+                               close(0);
+                               if (open("/dev/null", O_RDONLY) != 0)
+                                       error("Can't open /dev/null");
+                       }
+               }
+#endif
+               if (wasroot && iflag) {
+                       setsignal(SIGINT);
+                       setsignal(SIGQUIT);
+                       setsignal(SIGTERM);
+               }
+               return pid;
+       }
+       if (rootshell && mode != FORK_NOJOB && mflag) {
+               if (jp == NULL || jp->nprocs == 0)
+                       pgrp = pid;
+               else
+                       pgrp = jp->ps[0].pid;
+               setpgid(pid, pgrp);
+       }
+       if (mode == FORK_BG)
+               backgndpid = pid;               /* set $! */
+       if (jp) {
+               struct procstat *ps = &jp->ps[jp->nprocs++];
+               ps->pid = pid;
+               ps->status = -1;
+               ps->cmd = nullstr;
+               if (iflag && rootshell && n)
+                       ps->cmd = commandtext(n);
+       }
+       INTON;
+       TRACE(("In parent shell:  child = %d\n", pid));
+       return pid;
+}
+
+
+
+/*
+ * Wait for job to finish.
+ *
+ * Under job control we have the problem that while a child process is
+ * running interrupts generated by the user are sent to the child but not
+ * to the shell.  This means that an infinite loop started by an inter-
+ * active user may be hard to kill.  With job control turned off, an
+ * interactive user may place an interactive program inside a loop.  If
+ * the interactive program catches interrupts, the user doesn't want
+ * these interrupts to also abort the loop.  The approach we take here
+ * is to have the shell ignore interrupt signals while waiting for a
+ * forground process to terminate, and then send itself an interrupt
+ * signal if the child process was terminated by an interrupt signal.
+ * Unfortunately, some programs want to do a bit of cleanup and then
+ * exit on interrupt; unless these processes terminate themselves by
+ * sending a signal to themselves (instead of calling exit) they will
+ * confuse this approach.
+ */
+
+int
+waitforjob(jp)
+       register struct job *jp;
+       {
+#if JOBS
+       int mypgrp = getpgrp();
+#endif
+       int status;
+       int st;
+
+       INTOFF;
+       TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
+       while (jp->state == 0) {
+               dowait(1, jp);
+       }
+#if JOBS
+       if (jp->jobctl) {
+               if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
+                       error("TIOCSPGRP failed, errno=%d\n", errno);
+       }
+       if (jp->state == JOBSTOPPED)
+               curjob = jp - jobtab + 1;
+#endif
+       status = jp->ps[jp->nprocs - 1].status;
+       /* convert to 8 bits */
+       if ((status & 0xFF) == 0)
+               st = status >> 8 & 0xFF;
+#if JOBS
+       else if ((status & 0xFF) == 0177)
+               st = (status >> 8 & 0x7F) + 128;
+#endif
+       else
+               st = (status & 0x7F) + 128;
+       if (! JOBS || jp->state == JOBDONE)
+               freejob(jp);
+       CLEAR_PENDING_INT;
+       if ((status & 0x7F) == SIGINT)
+               kill(getpid(), SIGINT);
+       INTON;
+       return st;
+}
+
+
+
+/*
+ * Wait for a process to terminate.
+ */
+
+STATIC int
+dowait(block, job)
+       int block;
+       struct job *job;
+{
+       int pid;
+       int status;
+       struct procstat *sp;
+       struct job *jp;
+       struct job *thisjob;
+       int done;
+       int stopped;
+       int core;
+
+       TRACE(("dowait(%d) called\n", block));
+       do {
+               pid = waitproc(block, &status);
+               TRACE(("wait returns %d, status=%d\n", pid, status));
+       } while (pid == -1 && errno == EINTR);
+       if (pid <= 0)
+               return pid;
+       INTOFF;
+       thisjob = NULL;
+       for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
+               if (jp->used) {
+                       done = 1;
+                       stopped = 1;
+                       for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
+                               if (sp->pid == -1)
+                                       continue;
+                               if (sp->pid == pid) {
+                                       TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
+                                       sp->status = status;
+                                       thisjob = jp;
+                               }
+                               if (sp->status == -1)
+                                       stopped = 0;
+                               else if ((sp->status & 0377) == 0177)
+                                       done = 0;
+                       }
+                       if (stopped) {          /* stopped or done */
+                               int state = done? JOBDONE : JOBSTOPPED;
+                               if (jp->state != state) {
+                                       TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
+                                       jp->state = state;
+#if JOBS
+                                       if (done && curjob == jp - jobtab + 1)
+                                               curjob = 0;             /* no current job */
+#endif
+                               }
+                       }
+               }
+       }
+       INTON;
+       if (! rootshell || ! iflag || (job && thisjob == job)) {
+#if JOBS
+               if ((status & 0xFF) == 0177)
+                       status >>= 8;
+#endif
+               core = status & 0x80;
+               status &= 0x7F;
+               if (status != 0 && status != SIGINT && status != SIGPIPE) {
+                       if (thisjob != job)
+                               outfmt(out2, "%d: ", pid);
+#if JOBS
+                       if (status == SIGTSTP && rootshell && iflag)
+                               outfmt(out2, "%%%d ", job - jobtab + 1);
+#endif
+                       if (status < NSIG && sys_siglist[status])
+                               out2str(sys_siglist[status]);
+                       else
+                               outfmt(out2, "Signal %d", status);
+                       if (core)
+                               out2str(" - core dumped");
+                       out2c('\n');
+                       flushout(&errout);
+               } else {
+                       TRACE(("Not printing status: status=%d\n", status));
+               }
+       } else {
+               TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
+               if (thisjob)
+                       thisjob->changed = 1;
+       }
+       return pid;
+}
+
+
+
+/*
+ * Do a wait system call.  If job control is compiled in, we accept
+ * stopped processes.  If block is zero, we return a value of zero
+ * rather than blocking.
+ *
+ * System V doesn't have a non-blocking wait system call.  It does
+ * have a SIGCLD signal that is sent to a process when one of it's
+ * children dies.  The obvious way to use SIGCLD would be to install
+ * a handler for SIGCLD which simply bumped a counter when a SIGCLD
+ * was received, and have waitproc bump another counter when it got
+ * the status of a process.  Waitproc would then know that a wait
+ * system call would not block if the two counters were different.
+ * This approach doesn't work because if a process has children that
+ * have not been waited for, System V will send it a SIGCLD when it
+ * installs a signal handler for SIGCLD.  What this means is that when
+ * a child exits, the shell will be sent SIGCLD signals continuously
+ * until is runs out of stack space, unless it does a wait call before
+ * restoring the signal handler.  The code below takes advantage of
+ * this (mis)feature by installing a signal handler for SIGCLD and
+ * then checking to see whether it was called.  If there are any
+ * children to be waited for, it will be.
+ *
+ * If neither SYSV nor BSD is defined, we don't implement nonblocking
+ * waits at all.  In this case, the user will not be informed when
+ * a background process until the next time she runs a real program
+ * (as opposed to running a builtin command or just typing return),
+ * and the jobs command may give out of date information.
+ */
+
+#ifdef SYSV
+STATIC int gotsigchild;
+
+STATIC int onsigchild() {
+       gotsigchild = 1;
+}
+#endif
+
+
+STATIC int
+waitproc(block, status)
+       int block;
+       int *status;
+{
+#ifdef BSD
+       int flags;
+
+#if JOBS
+       flags = WUNTRACED;
+#else
+       flags = 0;
+#endif
+       if (block == 0)
+               flags |= WNOHANG;
+       return wait3(status, flags, (struct rusage *)NULL);
+#else
+#ifdef SYSV
+       int (*save)();
+
+       if (block == 0) {
+               gotsigchild = 0;
+               save = signal(SIGCLD, onsigchild);
+               signal(SIGCLD, save);
+               if (gotsigchild == 0)
+                       return 0;
+       }
+       return wait(status);
+#else
+       if (block == 0)
+               return 0;
+       return wait(status);
+#endif
+#endif
+}
+
+/*
+ * return 1 if there are stopped jobs, otherwise 0
+ */
+int job_warning = 0;
+int
+stoppedjobs()
+{
+       register int jobno;
+       register struct job *jp;
+
+       if (job_warning)
+               return (0);
+       for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) {
+               if (jp->used == 0)
+                       continue;
+               if (jp->state == JOBSTOPPED) {
+                       out2str("You have stopped jobs.\n");
+                       job_warning = 2;
+                       return (1);
+               }
+       }
+
+       return (0);
+}
+
+/*
+ * Return a string identifying a command (to be printed by the
+ * jobs command.
+ */
+
+STATIC char *cmdnextc;
+STATIC int cmdnleft;
+STATIC void cmdtxt(), cmdputs();
+#define MAXCMDTEXT     200
+
+char *
+commandtext(n)
+       union node *n;
+       {
+       char *name;
+
+       cmdnextc = name = ckmalloc(MAXCMDTEXT);
+       cmdnleft = MAXCMDTEXT - 4;
+       cmdtxt(n);
+       *cmdnextc = '\0';
+       return name;
+}
+
+
+STATIC void
+cmdtxt(n)
+       union node *n;
+       {
+       union node *np;
+       struct nodelist *lp;
+       char *p;
+       int i;
+       char s[2];
+
+       if (n == NULL)
+               return;
+       switch (n->type) {
+       case NSEMI:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs("; ");
+               cmdtxt(n->nbinary.ch2);
+               break;
+       case NAND:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs(" && ");
+               cmdtxt(n->nbinary.ch2);
+               break;
+       case NOR:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs(" || ");
+               cmdtxt(n->nbinary.ch2);
+               break;
+       case NPIPE:
+               for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
+                       cmdtxt(lp->n);
+                       if (lp->next)
+                               cmdputs(" | ");
+               }
+               break;
+       case NSUBSHELL:
+               cmdputs("(");
+               cmdtxt(n->nredir.n);
+               cmdputs(")");
+               break;
+       case NREDIR:
+       case NBACKGND:
+               cmdtxt(n->nredir.n);
+               break;
+       case NIF:
+               cmdputs("if ");
+               cmdtxt(n->nif.test);
+               cmdputs("; then ");
+               cmdtxt(n->nif.ifpart);
+               cmdputs("...");
+               break;
+       case NWHILE:
+               cmdputs("while ");
+               goto until;
+       case NUNTIL:
+               cmdputs("until ");
+until:
+               cmdtxt(n->nbinary.ch1);
+               cmdputs("; do ");
+               cmdtxt(n->nbinary.ch2);
+               cmdputs("; done");
+               break;
+       case NFOR:
+               cmdputs("for ");
+               cmdputs(n->nfor.var);
+               cmdputs(" in ...");
+               break;
+       case NCASE:
+               cmdputs("case ");
+               cmdputs(n->ncase.expr->narg.text);
+               cmdputs(" in ...");
+               break;
+       case NDEFUN:
+               cmdputs(n->narg.text);
+               cmdputs("() ...");
+               break;
+       case NCMD:
+               for (np = n->ncmd.args ; np ; np = np->narg.next) {
+                       cmdtxt(np);
+                       if (np->narg.next)
+                               cmdputs(" ");
+               }
+               for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
+                       cmdputs(" ");
+                       cmdtxt(np);
+               }
+               break;
+       case NARG:
+               cmdputs(n->narg.text);
+               break;
+       case NTO:
+               p = ">";  i = 1;  goto redir;
+       case NAPPEND:
+               p = ">>";  i = 1;  goto redir;
+       case NTOFD:
+               p = ">&";  i = 1;  goto redir;
+       case NFROM:
+               p = "<";  i = 0;  goto redir;
+       case NFROMFD:
+               p = "<&";  i = 0;  goto redir;
+redir:
+               if (n->nfile.fd != i) {
+                       s[0] = n->nfile.fd + '0';
+                       s[1] = '\0';
+                       cmdputs(s);
+               }
+               cmdputs(p);
+               if (n->type == NTOFD || n->type == NFROMFD) {
+                       s[0] = n->ndup.dupfd + '0';
+                       s[1] = '\0';
+                       cmdputs(s);
+               } else {
+                       cmdtxt(n->nfile.fname);
+               }
+               break;
+       case NHERE:
+       case NXHERE:
+               cmdputs("<<...");
+               break;
+       default:
+               cmdputs("???");
+               break;
+       }
+}
+
+
+
+STATIC void
+cmdputs(s)
+       char *s;
+       {
+       register char *p, *q;
+       register char c;
+       int subtype = 0;
+
+       if (cmdnleft <= 0)
+               return;
+       p = s;
+       q = cmdnextc;
+       while ((c = *p++) != '\0') {
+               if (c == CTLESC)
+                       *q++ = *p++;
+               else if (c == CTLVAR) {
+                       *q++ = '$';
+                       if (--cmdnleft > 0)
+                               *q++ = '{';
+                       subtype = *p++;
+               } else if (c == '=' && subtype != 0) {
+                       *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
+                       subtype = 0;
+               } else if (c == CTLENDVAR) {
+                       *q++ = '}';
+               } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE)
+                       cmdnleft++;             /* ignore it */
+               else
+                       *q++ = c;
+               if (--cmdnleft <= 0) {
+                       *q++ = '.';
+                       *q++ = '.';
+                       *q++ = '.';
+                       break;
+               }
+       }
+       cmdnextc = q;
+}
diff --git a/usr/src/bin/sh/jobs.h b/usr/src/bin/sh/jobs.h
new file mode 100644 (file)
index 0000000..e730383
--- /dev/null
@@ -0,0 +1,91 @@
+/*-
+ * 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.
+ *
+ *     @(#)jobs.h      8.2 (Berkeley) 5/4/95
+ */
+
+/* Mode argument to forkshell.  Don't change FORK_FG or FORK_BG. */
+#define FORK_FG 0
+#define FORK_BG 1
+#define FORK_NOJOB 2
+
+
+/*
+ * A job structure contains information about a job.  A job is either a
+ * single process or a set of processes contained in a pipeline.  In the
+ * latter case, pidlist will be non-NULL, and will point to a -1 terminated
+ * array of pids.
+ */
+
+struct procstat {
+       short pid;              /* process id */
+       short status;           /* status flags (defined above) */
+       char *cmd;              /* text of command being run */
+};
+
+
+/* states */
+#define JOBSTOPPED 1           /* all procs are stopped */
+#define JOBDONE 2              /* all procs are completed */
+
+
+struct job {
+       struct procstat ps0;    /* status of process */
+       struct procstat *ps;    /* status or processes when more than one */
+       short nprocs;           /* number of processes */
+       short pgrp;             /* process group of this job */
+       char state;             /* true if job is finished */
+       char used;              /* true if this entry is in used */
+       char changed;           /* true if status has changed */
+#if JOBS
+       char jobctl;            /* job running under job control */
+#endif
+};
+
+extern short backgndpid;       /* pid of last background process */
+extern int job_warning;                /* user was warned about stopped jobs */
+
+void setjobctl __P((int));
+int fgcmd __P((int, char **));
+int bgcmd __P((int, char **));
+int jobscmd __P((int, char **));
+void showjobs __P((int));
+int waitcmd __P((int, char **));
+int jobidcmd __P((int, char **));
+struct job *makejob __P((union node *, int));
+int forkshell __P((struct job *, union node *, int));
+int waitforjob __P((struct job *));
+int stoppedjobs __P((void));
+char *commandtext __P((union node *));
+
+#if ! JOBS
+#define setjobctl(on)  /* do nothing */
+#endif
diff --git a/usr/src/bin/sh/machdep.h b/usr/src/bin/sh/machdep.h
new file mode 100644 (file)
index 0000000..19fbbc2
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * 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.
+ *
+ *     @(#)machdep.h   8.2 (Berkeley) 5/4/95
+ */
+
+/*
+ * Most machines require the value returned from malloc to be aligned
+ * in some way.  The following macro will get this right on many machines.
+ */
+
+#ifndef ALIGN
+union align {
+       int i;
+       char *cp;
+};
+
+#define ALIGN(nbytes)  (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
+#endif
diff --git a/usr/src/bin/sh/mail.c b/usr/src/bin/sh/mail.c
new file mode 100644 (file)
index 0000000..70c1fbb
--- /dev/null
@@ -0,0 +1,112 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mail.c     8.2 (Berkeley) 5/4/95";
+#endif /* not lint */
+
+/*
+ * Routines to check for mail.  (Perhaps make part of main.c?)
+ */
+
+#include "shell.h"
+#include "exec.h"      /* defines padvance() */
+#include "var.h"
+#include "output.h"
+#include "memalloc.h"
+#include "error.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+
+
+#define MAXMBOXES 10
+
+
+STATIC int nmboxes;                    /* number of mailboxes */
+STATIC time_t mailtime[MAXMBOXES];     /* times of mailboxes */
+
+
+
+/*
+ * Print appropriate message(s) if mail has arrived.  If the argument is
+ * nozero, then the value of MAIL has changed, so we just update the
+ * values.
+ */
+
+void
+chkmail(silent)
+       int silent;
+{
+       register int i;
+       char *mpath;
+       char *p;
+       register char *q;
+       struct stackmark smark;
+       struct stat statb;
+
+       if (silent)
+               nmboxes = 10;
+       if (nmboxes == 0)
+               return;
+       setstackmark(&smark);
+       mpath = mpathset()? mpathval() : mailval();
+       for (i = 0 ; i < nmboxes ; i++) {
+               p = padvance(&mpath, nullstr);
+               if (p == NULL)
+                       break;
+               if (*p == '\0')
+                       continue;
+               for (q = p ; *q ; q++);
+               if (q[-1] != '/')
+                       abort();
+               q[-1] = '\0';                   /* delete trailing '/' */
+#ifdef notdef /* this is what the System V shell claims to do (it lies) */
+               if (stat(p, &statb) < 0)
+                       statb.st_mtime = 0;
+               if (statb.st_mtime > mailtime[i] && ! silent) {
+                       out2str(pathopt? pathopt : "you have mail");
+                       out2c('\n');
+               }
+               mailtime[i] = statb.st_mtime;
+#else /* this is what it should do */
+               if (stat(p, &statb) < 0)
+                       statb.st_size = 0;
+               if (statb.st_size > mailtime[i] && ! silent) {
+                       out2str(pathopt? pathopt : "you have mail");
+                       out2c('\n');
+               }
+               mailtime[i] = statb.st_size;
+#endif
+       }
+       nmboxes = i;
+       popstackmark(&smark);
+}
diff --git a/usr/src/bin/sh/mail.h b/usr/src/bin/sh/mail.h
new file mode 100644 (file)
index 0000000..c113a91
--- /dev/null
@@ -0,0 +1,35 @@
+/*-
+ * 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.
+ *
+ *     @(#)mail.h      8.2 (Berkeley) 5/4/95
+ */
+
+void chkmail __P((int));
diff --git a/usr/src/bin/sh/main.c b/usr/src/bin/sh/main.c
new file mode 100644 (file)
index 0000000..eb43be4
--- /dev/null
@@ -0,0 +1,353 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1991, 1993\n\
+       The Regents of the University of California.  All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     8.6 (Berkeley) 5/28/95";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+#include "shell.h"
+#include "main.h"
+#include "mail.h"
+#include "options.h"
+#include "output.h"
+#include "parser.h"
+#include "nodes.h"
+#include "expand.h"
+#include "eval.h"
+#include "jobs.h"
+#include "input.h"
+#include "trap.h"
+#include "var.h"
+#include "show.h"
+#include "memalloc.h"
+#include "error.h"
+#include "init.h"
+#include "mystring.h"
+#include "exec.h"
+
+#define PROFILE 0
+
+int rootpid;
+int rootshell;
+STATIC union node *curcmd;
+STATIC union node *prevcmd;
+extern int errno;
+#if PROFILE
+short profile_buf[16384];
+extern int etext();
+#endif
+
+STATIC void read_profile __P((char *));
+STATIC char *find_dot_file __P((char *));
+
+/*
+ * Main routine.  We initialize things, parse the arguments, execute
+ * profiles if we're a login shell, and then call cmdloop to execute
+ * commands.  The setjmp call sets up the location to jump to when an
+ * exception occurs.  When an exception occurs the variable "state"
+ * is used to figure out how far we had gotten.
+ */
+
+int
+main(argc, argv)
+       int argc;
+       char **argv; 
+{
+       struct jmploc jmploc;
+       struct stackmark smark;
+       volatile int state;
+       char *shinit;
+
+#if PROFILE
+       monitor(4, etext, profile_buf, sizeof profile_buf, 50);
+#endif
+       state = 0;
+       if (setjmp(jmploc.loc)) {
+               /*
+                * When a shell procedure is executed, we raise the
+                * exception EXSHELLPROC to clean up before executing
+                * the shell procedure.
+                */
+               if (exception == EXERROR)
+                       exitstatus = 2;
+               if (exception == EXSHELLPROC) {
+                       rootpid = getpid();
+                       rootshell = 1;
+                       minusc = NULL;
+                       state = 3;
+               } else if (state == 0 || iflag == 0 || ! rootshell)
+                       exitshell(2);
+               reset();
+               if (exception == EXINT
+#if ATTY
+                && (! attyset() || equal(termval(), "emacs"))
+#endif
+                ) {
+                       out2c('\n');
+                       flushout(&errout);
+               }
+               popstackmark(&smark);
+               FORCEINTON;                             /* enable interrupts */
+               if (state == 1)
+                       goto state1;
+               else if (state == 2)
+                       goto state2;
+               else if (state == 3)
+                       goto state3;
+               else
+                       goto state4;
+       }
+       handler = &jmploc;
+#ifdef DEBUG
+       opentrace();
+       trputs("Shell args:  ");  trargs(argv);
+#endif
+       rootpid = getpid();
+       rootshell = 1;
+       init();
+       setstackmark(&smark);
+       procargs(argc, argv);
+       if (argv[0] && argv[0][0] == '-') {
+               state = 1;
+               read_profile("/etc/profile");
+state1:
+               state = 2;
+               read_profile(".profile");
+       } 
+state2:
+       state = 3;
+       if (getuid() == geteuid() && getgid() == getegid()) {
+               if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') {
+                       state = 3;
+                       read_profile(shinit);
+               }
+       }
+state3:
+       state = 4;
+       if (minusc) {
+               evalstring(minusc);
+       }
+       if (sflag || minusc == NULL) {
+state4:        /* XXX ??? - why isn't this before the "if" statement */
+               cmdloop(1);
+       }
+#if PROFILE
+       monitor(0);
+#endif
+       exitshell(exitstatus);
+       /*NOTREACHED*/
+       return 0;
+}
+
+
+/*
+ * Read and execute commands.  "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
+ */
+
+void
+cmdloop(top) 
+       int top;
+{
+       union node *n;
+       struct stackmark smark;
+       int inter;
+       int numeof = 0;
+
+       TRACE(("cmdloop(%d) called\n", top));
+       setstackmark(&smark);
+       for (;;) {
+               if (pendingsigs)
+                       dotrap();
+               inter = 0;
+               if (iflag && top) {
+                       inter++;
+                       showjobs(1);
+                       chkmail(0);
+                       flushout(&output);
+               }
+               n = parsecmd(inter);
+               /* showtree(n); DEBUG */
+               if (n == NEOF) {
+                       if (!top || numeof >= 50)
+                               break;
+                       if (!stoppedjobs()) {
+                               if (!Iflag)
+                                       break;
+                               out2str("\nUse \"exit\" to leave shell.\n");
+                       }
+                       numeof++;
+               } else if (n != NULL && nflag == 0) {
+                       job_warning = (job_warning == 2) ? 1 : 0;
+                       numeof = 0;
+                       evaltree(n, 0);
+               }
+               popstackmark(&smark);
+       }
+       popstackmark(&smark);           /* unnecessary */
+}
+
+
+
+/*
+ * Read /etc/profile or .profile.  Return on error.
+ */
+
+STATIC void
+read_profile(name)
+       char *name;
+       {
+       int fd;
+
+       INTOFF;
+       if ((fd = open(name, O_RDONLY)) >= 0)
+               setinputfd(fd, 1);
+       INTON;
+       if (fd < 0)
+               return;
+       cmdloop(0);
+       popfile();
+}
+
+
+
+/*
+ * Read a file containing shell functions.
+ */
+
+void
+readcmdfile(name)
+       char *name;
+{
+       int fd;
+
+       INTOFF;
+       if ((fd = open(name, O_RDONLY)) >= 0)
+               setinputfd(fd, 1);
+       else
+               error("Can't open %s", name);
+       INTON;
+       cmdloop(0);
+       popfile();
+}
+
+
+
+/*
+ * Take commands from a file.  To be compatable we should do a path
+ * search for the file, which is necessary to find sub-commands.
+ */
+
+
+STATIC char *
+find_dot_file(basename)
+       char *basename;
+{
+       static char localname[FILENAME_MAX+1];
+       char *fullname;
+       char *path = pathval();
+       struct stat statb;
+
+       /* don't try this for absolute or relative paths */
+       if( strchr(basename, '/'))
+               return basename;
+
+       while ((fullname = padvance(&path, basename)) != NULL) {
+               strcpy(localname, fullname);
+               stunalloc(fullname);
+               if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode))