X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;ds=sidebyside;f=ash.c;h=334d2fdddc37c225a59badf3bed3d534ad1fa17a;hb=51ded05b3bf4df6f126420d39a40d27ea0728aa9;hp=bab12b93b7e0a481d5ee97a0c4c4a9b211bcc5ae;hpb=cb57d551a207198fabdb8c46f96e6a0932143923;p=oweals%2Fbusybox.git diff --git a/ash.c b/ash.c index bab12b93b..334d2fddd 100644 --- a/ash.c +++ b/ash.c @@ -1,44 +1,86 @@ -/*- - * Copyright (c) 1993 - * The Regents of the University of California. All rights reserved. +/* vi: set sw=4 ts=4: */ +/* + * ash shell port for busybox + * + * Copyright (c) 1989, 1991, 1993, 1994 + * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This version of ash is adapted from the source in Debian's ash 0.3.8-5 + * package. + * + * Modified by Erik Andersen and + * Vladimir Oleynik to be used in busybox + * + * + * Original copyright notice is retained at the end of this file. */ -#undef _GNU_SOURCE -#undef ASH_TYPE -#undef ASH_GETOPTS + +/* These defines allow you to adjust the feature set to be compiled + * into the ash shell. As a rule, enabling these options will make + * ash get bigger... With all of these options off, ash adds about + * 60k to busybox on an x86 system.*/ + + +/* Enable job control. This allows you to run jobs in the background, + * which is great when ash is being used as an interactive shell, but + * it completely useless for is all you are doing is running scripts. + * This adds about 2.5k on an x86 system. */ +#undef JOBS + +/* This enables alias support in ash. If you want to support things + * like "alias ls='ls -l'" with ash, enable this. This is only useful + * when ash is used as an intractive shell. This adds about 1.5k */ +#define ASH_ALIAS + +/* If you need ash to act as a full Posix shell, with full math + * support, enable this. This option needs some work, since it + * doesn't compile right now... */ #undef ASH_MATH_SUPPORT +/* Getopts is used by shell procedures to parse positional parameters. + * You probably want to leave this disabled, and use the busybox getopt + * applet if you want to do this sort of thing. There are some scripts + * out there that use it, so it you need it, enable. Most people will + * leave this disabled. This adds 1k on an x86 system. */ +#undef ASH_GETOPTS + +/* This allows you to override shell builtins and use whatever is on + * the filesystem. This is most useful when ash is acting as a + * standalone shell. Adds about 272 bytes. */ +#undef ASH_CMDCMD + + +/* Optimize size vs speed as size */ +#define ASH_OPTIMIZE_FOR_SIZE + +/* Enable this to compile in extra debugging noise. When debugging is + * on, debugging info will be written to $HOME/trace and a quit signal + * will generate a core dump. */ +#undef DEBUG + +/* These are here to work with glibc -- Don't change these... */ +#undef FNMATCH_BROKEN +#undef GLOB_BROKEN +#undef _GNU_SOURCE + #include #include #include @@ -50,7 +92,6 @@ #include #include #include -#include #include #include #include @@ -74,9899 +115,9356 @@ #include #endif -#if JOBS +#ifdef JOBS #include -#undef CEOF /* syntax.h redefines this */ #endif -#include "cmdedit.h" #include "busybox.h" -#include "ash.h" +#include "cmdedit.h" + +/* + * This file was generated by the mksyntax program. + */ + +/* Syntax classes */ +#define CWORD 0 /* character is nothing special */ +#define CNL 1 /* newline character */ +#define CBACK 2 /* a backslash character */ +#define CSQUOTE 3 /* single quote */ +#define CDQUOTE 4 /* double quote */ +#define CENDQUOTE 5 /* a terminating quote */ +#define CBQUOTE 6 /* backwards single quote */ +#define CVAR 7 /* a dollar sign */ +#define CENDVAR 8 /* a '}' character */ +#define CLP 9 /* a left paren in arithmetic */ +#define CRP 10 /* a right paren in arithmetic */ +#define CENDFILE 11 /* end of file */ +#define CCTL 12 /* like CWORD, except it must be escaped */ +#define CSPCL 13 /* these terminate a word */ +#define CIGN 14 /* character should be ignored */ + +/* Syntax classes for is_ functions */ +#define ISDIGIT 01 /* a digit */ +#define ISUPPER 02 /* an upper case letter */ +#define ISLOWER 04 /* a lower case letter */ +#define ISUNDER 010 /* an underscore */ +#define ISSPECL 020 /* the name of a special parameter */ + +#define SYNBASE 130 +#define PEOF -130 + +#define PEOA -129 + +#define TEOF 0 +#define TNL 1 +#define TSEMI 2 +#define TBACKGND 3 +#define TAND 4 +#define TOR 5 +#define TPIPE 6 +#define TLP 7 +#define TRP 8 +#define TENDCASE 9 +#define TENDBQUOTE 10 +#define TREDIR 11 +#define TWORD 12 +#define TASSIGN 13 +#define TNOT 14 +#define TCASE 15 +#define TDO 16 +#define TDONE 17 +#define TELIF 18 +#define TELSE 19 +#define TESAC 20 +#define TFI 21 +#define TFOR 22 +#define TIF 23 +#define TIN 24 +#define TTHEN 25 +#define TUNTIL 26 +#define TWHILE 27 +#define TBEGIN 28 +#define TEND 29 + + +#define BASESYNTAX (basesyntax + SYNBASE) +#define DQSYNTAX (dqsyntax + SYNBASE) +#define SQSYNTAX (sqsyntax + SYNBASE) +#define ARISYNTAX (arisyntax + SYNBASE) + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\205' */ +#define CTLARI '\206' +#define CTLENDARI '\207' +#define CTLQUOTEMARK '\210' + +#define is_digit(c) ((c)>='0' && (c)<='9') +#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c))) +#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c)))) +#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c)))) +#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT)) +#define digit_val(c) ((c) - '0') #define _DIAGASSERT(x) -#define ATABSIZE 39 -#define S_DFL 1 /* default signal handling (SIG_DFL) */ -#define S_CATCH 2 /* signal is caught */ -#define S_IGN 3 /* signal is ignored (SIG_IGN) */ -#define S_HARD_IGN 4 /* signal is ignored permenantly */ -#define S_RESET 5 /* temporary - to reset a hard ignored sig */ +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ -struct alias *atab[ATABSIZE]; +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ -static void setalias __P((char *, char *)); -static struct alias **hashalias __P((const char *)); -static struct alias *freealias __P((struct alias *)); -static struct alias **__lookupalias __P((const char *)); -static char *trap[NSIG]; /* trap handler commands */ -static char sigmode[NSIG - 1]; /* current value of signal */ -static char gotsig[NSIG - 1]; /* indicates specified signal received */ -static int pendingsigs; /* indicates some signal received */ +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output to pipe */ -static void -setalias(name, val) - char *name, *val; -{ - struct alias *ap, **app; +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ - app = __lookupalias(name); - ap = *app; - INTOFF; - if (ap) { - if (!(ap->flag & ALIASINUSE)) { - ckfree(ap->val); - } - ap->val = savestr(val); - ap->flag &= ~ALIASDEAD; - } else { - /* not found */ - ap = ckmalloc(sizeof (struct alias)); - ap->name = savestr(name); - ap->val = savestr(val); - ap->flag = 0; - ap->next = 0; - *app = ap; - } - INTON; -} +#if defined(BSD) +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif -static int -unalias(name) - char *name; - { - struct alias **app; +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ - app = __lookupalias(name); +#ifndef ALIGN +union align { + int i; + char *cp; +}; - if (*app) { - INTOFF; - *app = freealias(*app); - INTON; - return (0); - } +#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1)) +#endif - return (1); -} +#ifdef BB_LOCALE_SUPPORT +#include +static void change_lc_all(const char *value); +static void change_lc_ctype(const char *value); +#endif + +/* + * 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. :-)) + */ -#ifdef mkinit -static void rmaliases __P((void)); +static void onint (void); +static volatile int suppressint; +static volatile int intpending; -SHELLPROC { - rmaliases(); -} +#define INTOFF suppressint++ +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#else +static void __inton (void); +static void forceinton (void); +#define INTON __inton() +#define FORCEINTON forceinton() #endif -static void -rmaliases() { - struct alias *ap, **app; - int i; +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending - INTOFF; - for (i = 0; i < ATABSIZE; i++) { - app = &atab[i]; - for (ap = *app; ap; ap = *app) { - *app = freealias(*app); - if (ap == *app) { - app = &ap->next; - } - } - } - INTON; -} -struct alias * -lookupalias(name, check) - const char *name; - int check; -{ - struct alias *ap = *__lookupalias(name); +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif - if (check && ap && (ap->flag & ALIASINUSE)) - return (NULL); - return (ap); -} +static inline pointer ckmalloc (int sz) { return xmalloc(sz); } +static inline pointer ckrealloc(void *p, int sz) { return xrealloc(p, sz); } +static inline char * savestr (const char *s) { return xstrdup(s); } +static pointer stalloc (int); +static void stunalloc (pointer); +static void ungrabstackstr (char *, char *); +static char * growstackstr(void); +static char * makestrspace(size_t newlen); +static char *sstrdup (const char *); /* - * TODO - sort output + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. */ -static int -aliascmd(argc, argv) - int argc; - char **argv; -{ - char *n, *v; - int ret = 0; - struct alias *ap; - if (argc == 1) { - int i; +#define MINSIZE 504 /* minimum size of a block */ - for (i = 0; i < ATABSIZE; i++) - for (ap = atab[i]; ap; ap = ap->next) { - printalias(ap); - } - return (0); - } - while ((n = *++argv) != NULL) { - if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ - if ((ap = *__lookupalias(n)) == NULL) { - outfmt(out2, "%s: %s not found\n", "alias", n); - ret = 1; - } else - printalias(ap); - } - else { - *v++ = '\0'; - setalias(n, v); - } - } - return (ret); -} +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; -static int -unaliascmd(argc, argv) - int argc; - char **argv; -{ - int i; +static struct stack_block stackbase; +static struct stack_block *stackp = &stackbase; +static struct stackmark *markp; +static char *stacknxt = stackbase.space; +static int stacknleft = MINSIZE; - while ((i = nextopt("a")) != '\0') { - if (i == 'a') { - rmaliases(); - return (0); - } - } - for (i = 0; *argptr; argptr++) { - if (unalias(*argptr)) { - outfmt(out2, "%s: %s not found\n", "unalias", *argptr); - i = 1; - } - } - return (i); -} +#define equal(s1, s2) (strcmp(s1, s2) == 0) -static struct alias ** -hashalias(p) - const char *p; - { - unsigned int hashval; +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() - hashval = *p << 4; - while (*p) - hashval+= *p++; - return &atab[hashval % ATABSIZE]; -} +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); } +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) -static struct alias * -freealias(struct alias *ap) { - struct alias *next; - if (ap->flag & ALIASINUSE) { - ap->flag |= ALIASDEAD; - return ap; - } +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) - next = ap->next; - ckfree(ap->name); - ckfree(ap->val); - ckfree(ap); - return next; -} +#define ckfree(p) free((pointer)(p)) -static void -printalias(const struct alias *ap) { - char *p; - p = single_quote(ap->val); - out1fmt("alias %s=%s\n", ap->name, p); - stunalloc(p); -} +#ifdef DEBUG +#define TRACE(param) trace param +static void trace (const char *, ...); +static void trargs (char **); +static void showtree (union node *); +static void trputc (int); +static void trputs (const char *); +static void opentrace (void); +#else +#define TRACE(param) +#endif + +#define NSEMI 0 +#define NCMD 1 +#define NPIPE 2 +#define NREDIR 3 +#define NBACKGND 4 +#define NSUBSHELL 5 +#define NAND 6 +#define NOR 7 +#define NIF 8 +#define NWHILE 9 +#define NUNTIL 10 +#define NFOR 11 +#define NCASE 12 +#define NCLIST 13 +#define NDEFUN 14 +#define NARG 15 +#define NTO 16 +#define NFROM 17 +#define NFROMTO 18 +#define NAPPEND 19 +#define NTOOV 20 +#define NTOFD 21 +#define NFROMFD 22 +#define NHERE 23 +#define NXHERE 24 +#define NNOT 25 + +/* + * 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 */ +#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */ + + +#define NOPTS 16 + +static char optet_vals[NOPTS]; + +static const char * const optlist[NOPTS] = { + "e" "errexit", + "f" "noglob", + "I" "ignoreeof", + "i" "interactive", + "m" "monitor", + "n" "noexec", + "s" "stdin", + "x" "xtrace", + "v" "verbose", + "V" "vi", + "E" "emacs", + "C" "noclobber", + "a" "allexport", + "b" "notify", + "u" "nounset", + "q" "quietprofile" +}; -static struct alias ** -__lookupalias(const char *name) { - struct alias **app = hashalias(name); +#define optent_name(optent) (optent+1) +#define optent_letter(optent) optent[0] +#define optent_val(optent) optet_vals[optent] + +#define eflag optent_val(0) +#define fflag optent_val(1) +#define Iflag optent_val(2) +#define iflag optent_val(3) +#define mflag optent_val(4) +#define nflag optent_val(5) +#define sflag optent_val(6) +#define xflag optent_val(7) +#define vflag optent_val(8) +#define Vflag optent_val(9) +#define Eflag optent_val(10) +#define Cflag optent_val(11) +#define aflag optent_val(12) +#define bflag optent_val(13) +#define uflag optent_val(14) +#define qflag optent_val(15) + + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +struct nbinary { + int type; + union node *ch1; + union node *ch2; +}; - for (; *app; app = &(*app)->next) { - if (equal(name, (*app)->name)) { - break; - } - } - return app; -} +struct ncmd { + int type; + int backgnd; + union node *assign; + union node *args; + union node *redirect; +}; -#ifdef ASH_MATH_SUPPORT -/* The generated file arith.c has been snipped. If you want this - * stuff back in, feel free to add it to your own copy. */ -#endif -/* - * This file was generated by the mkbuiltins program. - */ +struct npipe { + int type; + int backgnd; + struct nodelist *cmdlist; +}; -static int bgcmd __P((int, char **)); -static int breakcmd __P((int, char **)); -static int cdcmd __P((int, char **)); -static int commandcmd __P((int, char **)); -static int dotcmd __P((int, char **)); -static int evalcmd __P((int, char **)); -static int execcmd __P((int, char **)); -static int exitcmd __P((int, char **)); -static int exportcmd __P((int, char **)); -static int histcmd __P((int, char **)); -static int fgcmd __P((int, char **)); -static int hashcmd __P((int, char **)); -static int jobscmd __P((int, char **)); -static int killcmd __P((int, char **)); -static int localcmd __P((int, char **)); -static int pwdcmd __P((int, char **)); -static int readcmd __P((int, char **)); -static int returncmd __P((int, char **)); -static int setcmd __P((int, char **)); -static int setvarcmd __P((int, char **)); -static int shiftcmd __P((int, char **)); -static int trapcmd __P((int, char **)); -static int umaskcmd __P((int, char **)); -static int unaliascmd __P((int, char **)); -static int unsetcmd __P((int, char **)); -static int waitcmd __P((int, char **)); -static int aliascmd __P((int, char **)); -static int ulimitcmd __P((int, char **)); -static int timescmd __P((int, char **)); -#ifdef ASH_MATH_SUPPORT -static int expcmd __P((int, char **)); -#endif -#ifdef ASH_TYPE -static int typecmd __P((int, char **)); -#endif -#ifdef ASH_GETOPTS -static int getoptscmd __P((int, char **)); -#endif -#ifndef BB_TRUE_FALSE -static int true_main __P((int, char **)); -static int false_main __P((int, char **)); -#endif -static struct builtincmd *DOTCMD; -static struct builtincmd *BLTINCMD; -static struct builtincmd *COMMANDCMD; -static struct builtincmd *EXECCMD; -static struct builtincmd *EVALCMD; +struct nredir { + int type; + union node *n; + union node *redirect; +}; -/* It is CRUCIAL that this listing be kept in ascii order, otherwise - * the binary search in find_builtin() will stop working. If you value - * your kneecaps, you'll be sure to *make sure* that any changes made - * to this array result in the listing remaining in ascii order. You - * have been warned. - */ -static const struct builtincmd builtincmds[] = { - { ".", dotcmd, 1 }, - { ":", true_main, 1 }, - { "alias", aliascmd, 6 }, - { "bg", bgcmd, 2 }, - { "break", breakcmd, 1 }, - { "builtin", bltincmd, 1 }, - { "cd", cdcmd, 2 }, - { "chdir", cdcmd, 0 }, - { "command", commandcmd, 2 }, - { "continue", breakcmd, 1 }, - { "eval", evalcmd, 1 }, - { "exec", execcmd, 1 }, - { "exit", exitcmd, 1 }, -#ifdef ASH_MATH_SUPPORT - { "exp", expcmd, 0 }, -#endif - { "export", exportcmd, 5 }, - { "false", false_main, 2 }, - { "fc", histcmd, 2 }, - { "fg", fgcmd, 2 }, -#ifdef ASH_GETOPTS - { "getopts", getoptscmd, 2 }, -#endif - { "hash", hashcmd, 0 }, - { "jobs", jobscmd, 2 }, - { "kill", killcmd, 2 }, -#ifdef ASH_MATH_SUPPORT - { "let", expcmd, 0 }, -#endif - { "local", localcmd, 4 }, - { "pwd", pwdcmd, 0 }, - { "read", readcmd, 2 }, - { "readonly", exportcmd, 5 }, - { "return", returncmd, 1 }, - { "set", setcmd, 1 }, - { "setvar", setvarcmd, 0 }, - { "shift", shiftcmd, 1 }, - { "times", timescmd, 1 }, - { "trap", trapcmd, 1 }, - { "true", true_main, 2 }, -#ifdef ASH_TYPE - { "type", typecmd, 0 }, -#endif - { "ulimit", ulimitcmd, 0 }, - { "umask", umaskcmd, 2 }, - { "unalias", unaliascmd, 2 }, - { "unset", unsetcmd, 1 }, - { "wait", waitcmd, 2 }, + +struct nif { + int type; + union node *test; + union node *ifpart; + union node *elsepart; }; -#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) ) -/* $NetBSD: cd.c,v 1.27 1999/07/09 03:05:49 christos Exp $ */ +struct nfor { + int type; + union node *args; + union node *body; + char *var; +}; -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -static int docd __P((char *, int)); -static char *getcomponent __P((void)); -static void updatepwd __P((char *)); -static void getpwd __P((void)); +struct ncase { + int type; + union node *expr; + union node *cases; +}; -static char *curdir = nullstr; /* current working directory */ -static char *cdcomppath; -#ifdef mkinit -INCLUDE "cd.h" -INIT { - setpwd(0, 0); -} -#endif +struct nclist { + int type; + union node *next; + union node *pattern; + union node *body; +}; -static int -cdcmd(argc, argv) - int argc; - char **argv; -{ - const char *dest; - const char *path; - char *p; - struct stat statb; - int print = 0; - nextopt(nullstr); - if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL) - error("HOME not set"); - if (*dest == '\0') - dest = "."; - if (dest[0] == '-' && dest[1] == '\0') { - dest = bltinlookup("OLDPWD"); - if (!dest || !*dest) { - dest = curdir; - } - print = 1; - if (dest) - print = 1; - else - dest = "."; - } - if (*dest == '/' || (path = bltinlookup("CDPATH")) == 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] != '\0') - p += 2; - print = strcmp(p, dest); - } - if (docd(p, print) >= 0) - return 0; +struct narg { + int type; + union node *next; + char *text; + struct nodelist *backquote; +}; - } - } - error("can't cd to %s", dest); - /* NOTREACHED */ -} +struct nfile { + int type; + union node *next; + int fd; + union node *fname; + char *expfname; +}; -/* - * Actually do the chdir. In an interactive shell, print the - * directory name if "print" is nonzero. - */ -static int -docd(dest, print) - char *dest; - int print; -{ - char *p; - char *q; - char *component; - struct stat statb; - int first; - int badstat; +struct ndup { + int type; + union node *next; + int fd; + int dupfd; + union node *vname; +}; - TRACE(("docd(\"%s\", %d) called\n", dest, print)); - /* - * Check each component of the path. If we find a symlink or - * something we can't stat, clear curdir to force a getcwd() - * next time we get the value of the current directory. - */ - badstat = 0; - cdcomppath = sstrdup(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) - || (S_ISLNK(statb.st_mode))) { - /* print = 1; */ - badstat = 1; - break; - } - } +struct nhere { + int type; + union node *next; + int fd; + union node *doc; +}; - INTOFF; - if (chdir(dest) < 0) { - INTON; - return -1; - } - updatepwd(badstat ? NULL : dest); - INTON; - if (print && iflag) - out1fmt(snlfmt, curdir); - return 0; -} +struct nnot { + int type; + union node *com; +}; -/* - * Get the next component of the path name pointed to by cdcomppath. - * This routine overwrites the string pointed to by cdcomppath. - */ -static char * -getcomponent() { - char *p; - char *start; +union node { + int type; + struct nbinary nbinary; + struct ncmd ncmd; + struct npipe npipe; + struct nredir nredir; + struct nif nif; + struct nfor nfor; + struct ncase ncase; + struct nclist nclist; + struct narg narg; + struct nfile nfile; + struct ndup ndup; + struct nhere nhere; + struct nnot nnot; +}; - 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; -} +struct nodelist { + struct nodelist *next; + union node *n; +}; + +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 */ +}; + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + const struct builtincmd *cmd; + } u; +}; + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + char *prevstring; + int prevnleft; +#ifdef ASH_ALIAS + struct alias *ap; /* if push was associated with an alias */ +#endif + char *string; /* remember the string since it may change */ +}; + +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of chars left in this buffer */ + char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; + struct stackmark *marknext; +}; +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + char **p; /* parameter list */ + int optind; /* next parameter to be processed by getopts */ + int optoff; /* used by getopts */ +}; /* - * 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. + * 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. */ +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ -static void -updatepwd(dir) - char *dir; - { - char *new; - char *p; - size_t len; - hashcd(); /* update command hash table */ - /* - * If our argument is NULL, we don't know the current directory - * any more because we traversed a symbolic link or something - * we couldn't stat(). - */ - if (dir == NULL || curdir == nullstr) { - setpwd(0, 1); - return; - } - len = strlen(dir); - cdcomppath = sstrdup(dir); - STARTSTACKSTR(new); - if (*dir != '/') { - 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); - setpwd(stackblock(), 1); -} +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 int exerrno = 0; /* Last exec error */ -static int -pwdcmd(argc, argv) - int argc; - char **argv; -{ - out1fmt(snlfmt, curdir); - return 0; -} +static void tryexec (char *, char **, char **); +static void printentry (struct tblentry *, int); +static void clearcmdentry (int); +static struct tblentry *cmdlookup (const char *, int); +static void delete_cmd_entry (void); +static int path_change (const char *, int *); +static void flushall (void); +static void out2fmt (const char *, ...) + __attribute__((__format__(__printf__,1,2))); +static int xwrite (int, const char *, int); -#define MAXPWD 256 +static void outstr (const char *p, FILE *file) { fputs(p, file); } +static void out1str(const char *p) { outstr(p, stdout); } +static void out2str(const char *p) { outstr(p, stderr); } -/* - * Find out what the current directory is. If we already know the current - * directory, this routine returns immediately. - */ -static void -getpwd() -{ - char buf[MAXPWD]; - - /* - * Things are a bit complicated here; we could have just used - * getcwd, but traditionally getcwd is implemented using popen - * to /bin/pwd. This creates a problem for us, since we cannot - * keep track of the job if it is being ran behind our backs. - * So we re-implement getcwd(), and we suppress interrupts - * throughout the process. This is not completely safe, since - * the user can still break out of it by killing the pwd program. - * We still try to use getcwd for systems that we know have a - * c implementation of getcwd, that does not open a pipe to - * /bin/pwd. - */ -#if defined(__NetBSD__) || defined(__SVR4) || defined(__GLIBC__) - - if (getcwd(buf, sizeof(buf)) == NULL) { - char *pwd = getenv("PWD"); - struct stat stdot, stpwd; - - if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && - stat(pwd, &stpwd) != -1 && - stdot.st_dev == stpwd.st_dev && - stdot.st_ino == stpwd.st_ino) { - curdir = savestr(pwd); - return; - } - error("getcwd() failed: %s", strerror(errno)); - } - curdir = savestr(buf); +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define out2c(c) putc((c), stderr) #else - { - char *p; - int i; - int status; - struct job *jp; - int pip[2]; - - if (pipe(pip) < 0) - error("Pipe call failed"); - jp = makejob((union node *)NULL, 1); - if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { - (void) close(pip[0]); - if (pip[1] != 1) { - close(1); - dup_as_newfd(pip[1], 1); - close(pip[1]); - } - (void) execl("/bin/pwd", "pwd", (char *)0); - error("Cannot exec /bin/pwd"); - } - (void) 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; - } - (void) 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); -#endif -} - -static void -setpwd(const char *val, int setold) -{ - if (setold) { - setvar("OLDPWD", curdir, VEXPORT); - } - INTOFF; - if (curdir != nullstr) { - free(curdir); - curdir = nullstr; - } - if (!val) { - getpwd(); - } else { - curdir = savestr(val); - } - INTON; - setvar("PWD", curdir, VEXPORT); -} - -/* $NetBSD: error.c,v 1.23 2000/07/03 03:26:19 matt Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * Errors and exceptions. - */ - -/* - * Code to handle exceptions in C. - */ - -struct jmploc *handler; -static int exception; -volatile int suppressint; -volatile int intpending; - - -static void exverror __P((int, const char *, va_list)) - __attribute__((__noreturn__)); - -/* - * 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". - */ - -static void -exraise(e) - int e; -{ -#ifdef DEBUG - if (handler == NULL) - abort(); +static void out2c(int c) { putc(c, stderr); } #endif - 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.) - */ +/* syntax table used when not in quotes */ +static const char basesyntax[257] = { + CENDFILE, CSPCL, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CSPCL, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CSPCL, CSQUOTE, CSPCL, CSPCL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CSPCL, CSPCL, CWORD, + CSPCL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CSPCL, CENDVAR, + CWORD +}; -static void -onint() { - sigset_t mysigset; +/* syntax table used when in double quotes */ +static const char dqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CENDQUOTE,CWORD, CVAR, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CBACK, CCTL, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CCTL +}; - if (suppressint) { - intpending++; - return; - } - intpending = 0; - sigemptyset(&mysigset); - sigprocmask(SIG_SETMASK, &mysigset, NULL); - if (rootshell && iflag) - exraise(EXINT); - else { - signal(SIGINT, SIG_DFL); - raise(SIGINT); - } - /* NOTREACHED */ -} +/* syntax table used when in single quotes */ +static const char sqsyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CENDQUOTE,CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL, CWORD, CWORD, CCTL, + CWORD, CCTL, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CCTL, CCTL, CCTL, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CCTL +}; + +/* syntax table used when in arithmetic */ +static const char arisyntax[257] = { + CENDFILE, CIGN, CWORD, CCTL, + CCTL, CCTL, CCTL, CCTL, + CCTL, CCTL, CCTL, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CNL, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CDQUOTE, CWORD, CVAR, CWORD, + CWORD, CSQUOTE, CLP, CRP, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CBACK, CWORD, + CWORD, CWORD, CBQUOTE, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CWORD, + CWORD, CWORD, CWORD, CENDVAR, + CWORD +}; + +/* character classification table */ +static const char is_type[257] = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, ISSPECL, + 0, ISSPECL, ISSPECL, 0, + 0, 0, 0, 0, + ISSPECL, 0, 0, ISSPECL, + 0, 0, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, + 0, 0, 0, 0, + 0, ISSPECL, ISSPECL, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, ISUPPER, ISUPPER, ISUPPER, + ISUPPER, 0, 0, 0, + 0, ISUNDER, 0, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, ISLOWER, ISLOWER, ISLOWER, + ISLOWER, 0, 0, 0, + 0 +}; +/* Array indicating which tokens mark the end of a list */ +static const char tokendlist[] = { + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 1, +}; + +static const char *const tokname[] = { + "end of file", + "newline", + "\";\"", + "\"&\"", + "\"&&\"", + "\"||\"", + "\"|\"", + "\"(\"", + "\")\"", + "\";;\"", + "\"`\"", + "redirection", + "word", + "assignment", + "\"!\"", + "\"case\"", + "\"do\"", + "\"done\"", + "\"elif\"", + "\"else\"", + "\"esac\"", + "\"fi\"", + "\"for\"", + "\"if\"", + "\"in\"", + "\"then\"", + "\"until\"", + "\"while\"", + "\"{\"", + "\"}\"", +}; + +#define KWDOFFSET 14 + +static const char *const parsekwd[] = { + "!", + "case", + "do", + "done", + "elif", + "else", + "esac", + "fi", + "for", + "if", + "in", + "then", + "until", + "while", + "{", + "}" +}; + + +static int plinno = 1; /* input line number */ + +static int parselleft; /* copy of parsefile->lleft */ + +static struct parsefile basepf; /* top level input file */ +static char basebuf[BUFSIZ]; /* buffer for top level input file */ +static struct parsefile *parsefile = &basepf; /* current input file */ /* - * Exverror 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. + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. */ -static void -exverror(cond, msg, ap) - int cond; - const char *msg; - va_list ap; -{ - CLEAR_PENDING_INT; - INTOFF; -#ifdef DEBUG - if (msg) - TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); - else - TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); -#endif - if (msg) { - if (commandname) - outfmt(&errout, "%s: ", commandname); - doformat(&errout, msg, ap); -#if FLUSHERR - outc('\n', &errout); -#else - outcslow('\n', &errout); -#endif - } - flushall(); - exraise(cond); - /* NOTREACHED */ -} +static int tokpushback; /* last token pushed back */ +#define NEOF ((union node *)&tokpushback) +static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ -#ifdef __STDC__ -static void -error(const char *msg, ...) +static void error (const char *, ...) __attribute__((__noreturn__)); +static void exerror (int, const char *, ...) __attribute__((__noreturn__)); +static void shellexec (char **, char **, const char *, int) + __attribute__((noreturn)); +static void exitshell (int) __attribute__((noreturn)); + +static int goodname(const char *); +static void ignoresig (int); +static void onsig (int); +static void dotrap (void); +static int decode_signal (const char *, int); + +static void shprocvar(void); +static void deletefuncs(void); +static void setparam (char **); +static void freeparam (volatile struct shparam *); + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 +#define SKIPFILE 4 + +/* 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 */ + +#define DO_ERR 1 /* find_command prints errors */ +#define DO_ABS 2 /* find_command checks absolute paths */ +#define DO_NOFUN 4 /* find_command ignores functions */ +#define DO_BRUTE 8 /* find_command ignores hash table */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 0x08 /* text is staticly allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ + void (*func) (const char *); + /* function to be called when */ + /* the variable gets set/unset */ +}; + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define rmescapes(p) _rmescapes((p), 0) +static char *_rmescapes (char *, int); #else -static void -error(va_alist) - va_dcl +static void rmescapes (char *); #endif -{ -#ifndef __STDC__ - const char *msg; -#endif - va_list ap; -#ifdef __STDC__ - va_start(ap, msg); -#else - va_start(ap); - msg = va_arg(ap, const char *); -#endif - exverror(EXERROR, msg, ap); - /* NOTREACHED */ - va_end(ap); -} +static int casematch (union node *, const char *); +static void clearredir(void); +static void popstring(void); +static void readcmdfile (const char *); -#ifdef __STDC__ -static void -exerror(int cond, const char *msg, ...) -#else -static void -exerror(va_alist) - va_dcl -#endif -{ -#ifndef __STDC__ - int cond; - const char *msg; -#endif - va_list ap; -#ifdef __STDC__ - va_start(ap, msg); -#else - va_start(ap); - cond = va_arg(ap, int); - msg = va_arg(ap, const char *); -#endif - exverror(cond, msg, ap); - /* NOTREACHED */ - va_end(ap); -} +static int number (const char *); +static int is_number (const char *, int *num); +static char *single_quote (const char *); +static int nextopt (const char *); +static void redirect (union node *, int); +static void popredir (void); +static int dup_as_newfd (int, int); +static void changepath(const char *newval); +static void getoptsreset(const char *value); -/* - * Table of error messages. - */ -struct errname { - short errcode; /* error number */ - short action; /* operation which encountered the error */ - const char *msg; /* text describing the error */ -}; +static int parsenleft; /* copy of parsefile->nleft */ +static char *parsenextc; /* copy of parsefile->nextc */ +static int rootpid; /* pid of main shell */ +static int rootshell; /* true if we aren't a child of the main shell */ +static const char spcstr[] = " "; +static const char snlfmt[] = "%s\n"; -#define ALL (E_OPEN|E_CREAT|E_EXEC) +static int sstrnleft; +static int herefd = -1; -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" }, - { EEXIST, E_CREAT,"file exists" }, -#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" }, +static struct localvar *localvars; + +static struct var vifs; +static struct var vmail; +static struct var vmpath; +static struct var vpath; +static struct var vps1; +static struct var vps2; +static struct var voptind; +#ifdef BB_LOCALE_SUPPORT +static struct var vlc_all; +static struct var vlc_ctype; #endif -#ifdef ETIMEDOUT - { ETIMEDOUT, ALL, "remote access failed" }, + +struct varinit { + struct var *var; + int flags; + const char *text; + void (*func) (const char *); +}; + +static const char defpathvar[] = + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; +#define defpath (defpathvar + 5) + +#ifdef IFS_BROKEN +static const char defifsvar[] = "IFS= \t\n"; +#define defifs (defifsvar + 4) +#else +static const char defifs[] = " \t\n"; #endif -#ifdef ELOOP - { ELOOP, ALL, "symbolic link loop" }, + +static const struct varinit varinit[] = { +#ifdef IFS_BROKEN + { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar, +#else + { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=", #endif - { E2BIG, E_EXEC, "argument list too long" }, -#ifdef ELIBACC - { ELIBACC, E_EXEC, "shared library missing" }, + NULL }, + { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", + NULL }, + { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", + NULL }, + { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar, + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", + NULL }, + { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", + getoptsreset }, +#ifdef BB_LOCALE_SUPPORT + { &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=", + change_lc_all }, + { &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=", + change_lc_ctype }, #endif - { 0, 0, NULL }, + { NULL, 0, NULL, + NULL } }; +#define VTABSIZE 39 + +static struct var *vartab[VTABSIZE]; /* - * 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. + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. */ -static const 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; -} +#define ifsval() (vifs.text + 4) +#define ifsset() ((vifs.flags & VUNSET) == 0) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define optindval() (voptind.text + 7) +#define mpathset() ((vmpath.flags & VUNSET) == 0) -#ifdef REALLY_SMALL -static void -__inton() { - if (--suppressint == 0 && intpending) { - onint(); - } -} -#endif -/* $NetBSD: eval.c,v 1.57 2001/02/04 19:52:06 christos Exp $ */ +static void initvar (void); +static void setvar (const char *, const char *, int); +static void setvareq (char *, int); +static void listsetvar (struct strlist *); +static const char *lookupvar (const char *); +static const char *bltinlookup (const char *); +static char **environment (void); +static int showvarscmd (int, char **); +static void mklocal (char *); +static void poplocalvars (void); +static int unsetvar (const char *); +static int varequal (const char *, const char *); -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +static char *arg0; /* value of $0 */ +static struct shparam shellparam; /* current positional parameters */ +static char **argptr; /* argument list for builtin commands */ +static char *optionarg; /* set by nextopt (like getopt) */ +static char *optptr; /* used by nextopt */ +static char *minusc; /* argument to -c option */ -/* 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 */ - -static int evalskip; /* set if we are skipping commands */ -static int skipcount; /* number of levels to skip */ -static int loopnest; /* current loop nesting level */ -static int funcnest; /* depth of function calls */ - - -static char *commandname; -struct strlist *cmdenviron; -static int exitstatus; /* exit status of last command */ -static int oexitstatus; /* saved exit status */ - - -static void evalloop __P((union node *, int)); -static void evalfor __P((union node *, int)); -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 *)); -#ifdef notyet -static void evalcommand __P((union node *, int, struct backcmd *)); -#else -static void evalcommand __P((union node *, int)); -#endif -static void prehash __P((union node *)); -static void eprintlist __P((struct strlist *)); +#ifdef ASH_ALIAS -/* - * Called to reset things after an exception. - */ +#define ALIASINUSE 1 +#define ALIASDEAD 2 -#ifdef mkinit -INCLUDE "eval.h" +#define ATABSIZE 39 -RESET { - evalskip = 0; - loopnest = 0; - funcnest = 0; -} +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; -SHELLPROC { - exitstatus = 0; -} -#endif +static struct alias *atab[ATABSIZE]; +static void setalias (char *, char *); +static struct alias **hashalias (const char *); +static struct alias *freealias (struct alias *); +static struct alias **__lookupalias (const char *); +static void +setalias(name, val) + char *name, *val; +{ + struct alias *ap, **app; -/* - * The eval commmand. - */ + app = __lookupalias(name); + ap = *app; + INTOFF; + if (ap) { + if (!(ap->flag & ALIASINUSE)) { + ckfree(ap->val); + } + ap->val = savestr(val); + ap->flag &= ~ALIASDEAD; + } else { + /* not found */ + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + ap->val = savestr(val); + ap->flag = 0; + ap->next = 0; + *app = ap; + } + INTON; +} static int -evalcmd(argc, argv) - int argc; - char **argv; +unalias(char *name) { - char *p; - char *concat; - char **ap; + struct alias **app; - 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, EV_TESTED); - } - return exitstatus; -} + app = __lookupalias(name); + if (*app) { + INTOFF; + *app = freealias(*app); + INTON; + return (0); + } -/* - * Execute a command or commands contained in a string. - */ + return (1); +} static void -evalstring(s, flag) - char *s; - int flag; - { - union node *n; - struct stackmark smark; +rmaliases(void) +{ + struct alias *ap, **app; + int i; - setstackmark(&smark); - setinputstring(s); - while ((n = parsecmd(0)) != NEOF) { - evaltree(n, flag); - popstackmark(&smark); + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + app = &atab[i]; + for (ap = *app; ap; ap = *app) { + *app = freealias(*app); + if (ap == *app) { + app = &ap->next; + } + } } - popfile(); - popstackmark(&smark); + INTON; +} + +static struct alias * +lookupalias(const char *name, int check) +{ + struct alias *ap = *__lookupalias(name); + + if (check && ap && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); } +static void +printalias(const struct alias *ap) { + char *p; + + p = single_quote(ap->val); + printf("alias %s=%s\n", ap->name, p); + stunalloc(p); +} /* - * Evaluate a parse tree. The value is left in the global variable - * exitstatus. + * TODO - sort output */ - -static void -evaltree(n, flags) - union node *n; - int flags; +static int +aliascmd(int argc, char **argv) { - int checkexit = 0; - if (n == NULL) { - TRACE(("evaltree(NULL) called\n")); - goto out; - } - TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type)); - switch (n->type) { - case NSEMI: - evaltree(n->nbinary.ch1, flags & EV_TESTED); - 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: { - evaltree(n->nif.test, EV_TESTED); - if (evalskip) - goto out; - if (exitstatus == 0) - evaltree(n->nif.ifpart, flags); - else if (n->nif.elsepart) - evaltree(n->nif.elsepart, flags); - else - exitstatus = 0; - break; + 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) { + printalias(ap); + } + return (0); } - case NWHILE: - case NUNTIL: - evalloop(n, flags); - break; - case NFOR: - evalfor(n, flags); - break; - case NCASE: - evalcase(n, flags); - break; - case NDEFUN: { - struct builtincmd *bcmd; - if ( - (bcmd = find_builtin(n->narg.text)) && - bcmd->flags & BUILTIN_SPECIAL - ) { - outfmt(out2, "%s is a special built-in\n", n->narg.text); - exitstatus = 1; - break; + while ((n = *++argv) != NULL) { + if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */ + if ((ap = *__lookupalias(n)) == NULL) { + out2fmt("%s: %s not found\n", "alias", n); + ret = 1; + } else + printalias(ap); + } + else { + *v++ = '\0'; + setalias(n, v); } - 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); - checkexit = 1; - break; - case NCMD: -#ifdef notyet - evalcommand(n, flags, (struct backcmd *)NULL); -#else - evalcommand(n, flags); -#endif - checkexit = 1; - break; -#ifdef DEBUG - default: - out1fmt("Node type = %d\n", n->type); -#ifndef USE_GLIBC_STDIO - flushout(out1); -#endif - break; -#endif - } -out: - if (pendingsigs) - dotrap(); - if ( - flags & EV_EXIT || - (checkexit && eflag && exitstatus && !(flags & EV_TESTED)) - ) - exitshell(exitstatus); + return (ret); } - -static void -evalloop(n, flags) - union node *n; - int flags; +static int +unaliascmd(int argc, char **argv) { - int status; + int i; - 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; + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); } - if (n->type == NWHILE) { - if (exitstatus != 0) - break; - } else { - if (exitstatus == 0) - break; + } + for (i = 0; *argptr; argptr++) { + if (unalias(*argptr)) { + out2fmt("%s: %s not found\n", "unalias", *argptr); + i = 1; } - evaltree(n->nbinary.ch2, flags & EV_TESTED); - status = exitstatus; - if (evalskip) - goto skipping; } - loopnest--; - exitstatus = status; + + return (i); } +static struct alias ** +hashalias(p) + const char *p; + { + unsigned int hashval; + hashval = *p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} -static void -evalfor(n, flags) - union node *n; - int flags; -{ - struct arglist arglist; - union node *argp; - struct strlist *sp; - struct stackmark smark; +static struct alias * +freealias(struct alias *ap) { + struct alias *next; - 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 | EXP_RECORD); - if (evalskip) - goto out; + if (ap->flag & ALIASINUSE) { + ap->flag |= ALIASDEAD; + return ap; } - *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, flags & EV_TESTED); - if (evalskip) { - if (evalskip == SKIPCONT && --skipcount <= 0) { - evalskip = 0; - continue; - } - if (evalskip == SKIPBREAK && --skipcount <= 0) - evalskip = 0; - break; - } - } - loopnest--; -out: - popstackmark(&smark); + next = ap->next; + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); + return next; } +static struct alias ** +__lookupalias(const char *name) { + struct alias **app = hashalias(name); -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; - } + for (; *app; app = &(*app)->next) { + if (equal(name, (*app)->name)) { + break; } } -out: - popstackmark(&smark); -} + return app; +} +#endif +#ifdef ASH_MATH_SUPPORT +/* The generated file arith.c has been snipped. If you want this + * stuff back in, feel free to add it to your own copy. */ +#define ARITH_NUM 257 +#define ARITH_LPAREN 258 +#define ARITH_RPAREN 259 +#define ARITH_OR 260 +#define ARITH_AND 261 +#define ARITH_BOR 262 +#define ARITH_BXOR 263 +#define ARITH_BAND 264 +#define ARITH_EQ 265 +#define ARITH_NE 266 +#define ARITH_LT 267 +#define ARITH_GT 268 +#define ARITH_GE 269 +#define ARITH_LE 270 +#define ARITH_LSHIFT 271 +#define ARITH_RSHIFT 272 +#define ARITH_ADD 273 +#define ARITH_SUB 274 +#define ARITH_MUL 275 +#define ARITH_DIV 276 +#define ARITH_REM 277 +#define ARITH_UNARYMINUS 278 +#define ARITH_UNARYPLUS 279 +#define ARITH_NOT 280 +#define ARITH_BNOT 281 + +static void expari (int); +/* From arith.y */ +static int arith (const char *); +static int expcmd (int , char **); +static void arith_lex_reset (void); +static int yylex (void); + +#endif + +static char *trap[NSIG]; /* trap handler commands */ +static char sigmode[NSIG - 1]; /* current value of signal */ +static char gotsig[NSIG - 1]; /* indicates specified signal received */ +static int pendingsigs; /* indicates some signal received */ /* - * Kick off a subshell to evaluate a tree. + * This file was generated by the mkbuiltins program. */ -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; - } -} +#ifdef JOBS +static int bgcmd (int, char **); +static int fgcmd (int, char **); +static int killcmd (int, char **); +#endif +static int bltincmd (int, char **); +static int cdcmd (int, char **); +static int breakcmd (int, char **); +#ifdef ASH_CMDCMD +static int commandcmd (int, char **); +#endif +static int dotcmd (int, char **); +static int evalcmd (int, char **); +static int execcmd (int, char **); +static int exitcmd (int, char **); +static int exportcmd (int, char **); +static int histcmd (int, char **); +static int hashcmd (int, char **); +static int helpcmd (int, char **); +static int jobscmd (int, char **); +static int localcmd (int, char **); +#ifndef BB_PWD +static int pwdcmd (int, char **); +#endif +static int readcmd (int, char **); +static int returncmd (int, char **); +static int setcmd (int, char **); +static int setvarcmd (int, char **); +static int shiftcmd (int, char **); +static int trapcmd (int, char **); +static int umaskcmd (int, char **); +#ifdef ASH_ALIAS +static int aliascmd (int, char **); +static int unaliascmd (int, char **); +#endif +static int unsetcmd (int, char **); +static int waitcmd (int, char **); +static int ulimitcmd (int, char **); +static int timescmd (int, char **); +#ifdef ASH_MATH_SUPPORT +static int expcmd (int, char **); +#endif +static int typecmd (int, char **); +#ifdef ASH_GETOPTS +static int getoptscmd (int, char **); +#endif + +#ifndef BB_TRUE_FALSE +static int true_main (int, char **); +static int false_main (int, char **); +#endif + +static void setpwd (const char *, int); + + +#define BUILTIN_NOSPEC "0" +#define BUILTIN_SPECIAL "1" +#define BUILTIN_REGULAR "2" +#define BUILTIN_ASSIGN "4" +#define BUILTIN_SPEC_ASSG "5" +#define BUILTIN_REG_ASSG "6" + +#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1) +#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2) +#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4) + +struct builtincmd { + const char *name; + int (*const builtinfunc) (int, char **); + //unsigned flags; +}; + + +/* It is CRUCIAL that this listing be kept in ascii order, otherwise + * the binary search in find_builtin() will stop working. If you value + * your kneecaps, you'll be sure to *make sure* that any changes made + * to this array result in the listing remaining in ascii order. You + * have been warned. + */ +static const struct builtincmd builtincmds[] = { + { BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */ + { BUILTIN_SPECIAL ":", true_main }, +#ifdef ASH_ALIAS + { BUILTIN_REG_ASSG "alias", aliascmd }, +#endif +#ifdef JOBS + { BUILTIN_REGULAR "bg", bgcmd }, +#endif + { BUILTIN_SPECIAL "break", breakcmd }, + { BUILTIN_SPECIAL "builtin", bltincmd }, + { BUILTIN_REGULAR "cd", cdcmd }, + { BUILTIN_NOSPEC "chdir", cdcmd }, +#ifdef ASH_CMDCMD + { BUILTIN_REGULAR "command", commandcmd }, +#endif + { BUILTIN_SPECIAL "continue", breakcmd }, + { BUILTIN_SPECIAL "eval", evalcmd }, + { BUILTIN_SPECIAL "exec", execcmd }, + { BUILTIN_SPECIAL "exit", exitcmd }, +#ifdef ASH_MATH_SUPPORT + { BUILTIN_NOSPEC "exp", expcmd }, +#endif + { BUILTIN_SPEC_ASSG "export", exportcmd }, + { BUILTIN_REGULAR "false", false_main }, + { BUILTIN_REGULAR "fc", histcmd }, +#ifdef JOBS + { BUILTIN_REGULAR "fg", fgcmd }, +#endif +#ifdef ASH_GETOPTS + { BUILTIN_REGULAR "getopts", getoptscmd }, +#endif + { BUILTIN_NOSPEC "hash", hashcmd }, + { BUILTIN_NOSPEC "help", helpcmd }, + { BUILTIN_REGULAR "jobs", jobscmd }, +#ifdef JOBS + { BUILTIN_REGULAR "kill", killcmd }, +#endif +#ifdef ASH_MATH_SUPPORT + { BUILTIN_NOSPEC "let", expcmd }, +#endif + { BUILTIN_ASSIGN "local", localcmd }, +#ifndef BB_PWD + { BUILTIN_NOSPEC "pwd", pwdcmd }, +#endif + { BUILTIN_REGULAR "read", readcmd }, + { BUILTIN_SPEC_ASSG "readonly", exportcmd }, + { BUILTIN_SPECIAL "return", returncmd }, + { BUILTIN_SPECIAL "set", setcmd }, + { BUILTIN_NOSPEC "setvar", setvarcmd }, + { BUILTIN_SPECIAL "shift", shiftcmd }, + { BUILTIN_SPECIAL "times", timescmd }, + { BUILTIN_SPECIAL "trap", trapcmd }, + { BUILTIN_REGULAR "true", true_main }, + { BUILTIN_NOSPEC "type", typecmd }, + { BUILTIN_NOSPEC "ulimit", ulimitcmd }, + { BUILTIN_REGULAR "umask", umaskcmd }, +#ifdef ASH_ALIAS + { BUILTIN_REGULAR "unalias", unaliascmd }, +#endif + { BUILTIN_SPECIAL "unset", unsetcmd }, + { BUILTIN_REGULAR "wait", waitcmd }, +}; +#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) ) +static const struct builtincmd *DOTCMD = &builtincmds[0]; +static struct builtincmd *BLTINCMD; +static struct builtincmd *EXECCMD; +static struct builtincmd *EVALCMD; +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ /* - * Compute the names of the files in a redirection list. + * 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. */ -static void -expredir(n) - union node *n; +struct procstat { + pid_t pid; /* process id */ + int status; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +static int job_warning; /* user was warned about stopped jobs */ + +#ifdef JOBS +static void setjobctl(int enable); +#else +#define setjobctl(on) /* do nothing */ +#endif + + +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 */ +#ifdef JOBS + char jobctl; /* job running under job control */ +#endif +}; + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static int backgndpid = -1; /* pid of last background process */ +#ifdef JOBS +static int initialpgrp; /* pgrp of shell on invocation */ +static int curjob; /* current job */ +static int jobctl; +#endif +static int intreceived; + +static struct job *makejob (const union node *, int); +static int forkshell (struct job *, const union node *, int); +static int waitforjob (struct job *); + +static int docd (char *, int); +static char *getcomponent (void); +static void updatepwd (const char *); +static void getpwd (void); + +static char *padvance (const char **, const char *); + +static char nullstr[1]; /* zero length string */ +static char *curdir = nullstr; /* current working directory */ +static char *cdcomppath; + +static int +cdcmd(argc, argv) + int argc; + char **argv; { - union node *redir; + const char *dest; + const char *path; + char *p; + struct stat statb; + int print = 0; - for (redir = n ; redir ; redir = redir->nfile.next) { - struct arglist fn; - fn.lastp = &fn.list; - oexitstatus = exitstatus; - switch (redir->type) { - case NFROMTO: - case NFROM: - case NTO: - case NAPPEND: - case NTOOV: - 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); + nextopt(nullstr); + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD"); + if (!dest || !*dest) { + dest = curdir; + } + print = 1; + if (dest) + print = 1; + else + dest = "."; + } + if (*dest == '/' || (path = bltinlookup("CDPATH")) == 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] != '\0') + p += 2; + print = strcmp(p, dest); } - break; + if (docd(p, print) >= 0) + return 0; + } } + error("can't cd to %s", dest); + /* NOTREACHED */ } - /* - * 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.) + * Actually do the chdir. In an interactive shell, print the + * directory name if "print" is nonzero. */ -static void -evalpipe(n) - union node *n; +static int +docd(dest, print) + char *dest; + int print; { - struct job *jp; - struct nodelist *lp; - int pipelen; - int prevfd; - int pip[2]; + char *p; + char *q; + char *component; + struct stat statb; + int first; + int badstat; - 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); - dup_as_newfd(prevfd, 0); - close(prevfd); - if (pip[0] == 0) { - pip[0] = -1; - } - } - if (pip[1] >= 0) { - if (pip[0] >= 0) { - close(pip[0]); - } - if (pip[1] != 1) { - close(1); - dup_as_newfd(pip[1], 1); - close(pip[1]); - } - } - evaltree(lp->n, EV_EXIT); + TRACE(("docd(\"%s\", %d) called\n", dest, print)); + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + cdcomppath = sstrdup(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) + || (S_ISLNK(statb.st_mode))) { + /* print = 1; */ + badstat = 1; + break; } - 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)); + + INTOFF; + if (chdir(dest) < 0) { INTON; + return -1; } + updatepwd(badstat ? NULL : dest); + INTON; + if (print && iflag) + printf(snlfmt, curdir); + return 0; } - /* - * 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. + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. */ -static void -evalbackcmd(n, result) - union node *n; - struct backcmd *result; -{ - int pip[2]; - struct job *jp; - struct stackmark smark; /* unnecessary */ +static char * +getcomponent() { + char *p; + char *start; - setstackmark(&smark); - result->fd = -1; - result->buf = NULL; - result->nleft = 0; - result->jp = NULL; - if (n == NULL) { - exitstatus = 0; - goto out; - } -#ifdef notyet - /* - * For now we disable executing builtins in the same - * context as the shell, because we are not keeping - * enough state to recover from changes that are - * supposed only to affect subshells. eg. echo "`cd /`" - */ - if (n->type == NCMD) { - exitstatus = oexitstatus; - evalcommand(n, EV_BACKCMD, result); - } else -#endif - { - 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); - dup_as_newfd(pip[1], 1); - close(pip[1]); - } - eflag = 0; - evaltree(n, EV_EXIT); - } - close(pip[1]); - result->fd = pip[0]; - result->jp = jp; + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; } -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)); + return start; } /* - * Execute a simple command. + * 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. */ +static void hashcd (void); + static void -#ifdef notyet -evalcommand(cmd, flags, backcmd) - union node *cmd; - int flags; - struct backcmd *backcmd; -#else -evalcommand(cmd, flags) - union node *cmd; - int flags; -#endif +updatepwd(const char *dir) { - struct stackmark smark; - union node *argp; - struct arglist arglist; - struct arglist varlist; - char **argv; - int argc; - char **envp; - struct strlist *sp; - int mode; -#ifdef notyet - int pip[2]; -#endif - struct cmdentry cmdentry; - struct job *jp; - char *volatile savecmdname; - volatile struct shparam saveparam; - struct localvar *volatile savelocalvars; - volatile int e; - char *lastarg; - const char *path; - const struct builtincmd *firstbltin; - struct jmploc *volatile savehandler; - struct jmploc jmploc; -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &argv; - (void) &argc; - (void) &lastarg; - (void) &flags; -#endif + char *new; + char *p; + size_t len; - /* First expand the arguments. */ - TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); - setstackmark(&smark); - arglist.lastp = &arglist.list; - varlist.lastp = &varlist.list; - arglist.list = 0; - oexitstatus = exitstatus; - exitstatus = 0; - path = pathval(); - for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { - expandarg(argp, &varlist, EXP_VARTILDE); + hashcd(); /* update command hash table */ + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == nullstr) { + setpwd(0, 1); + return; } - for ( - argp = cmd->ncmd.args; argp && !arglist.list; - argp = argp->narg.next - ) { - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + len = strlen(dir); + cdcomppath = sstrdup(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); } - if (argp) { - struct builtincmd *bcmd; - bool pseudovarflag; - bcmd = find_builtin(arglist.list->text); - pseudovarflag = bcmd && bcmd->flags & BUILTIN_ASSIGN; - for (; argp; argp = argp->narg.next) { - if (pseudovarflag && isassignment(argp->narg.text)) { - expandarg(argp, &arglist, EXP_VARTILDE); - continue; - } - expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + 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); } } - *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)); + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + setpwd(stackblock(), 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) { -#ifdef FLUSHERR - outc('+', &errout); -#else - outcslow('+', &errout); -#endif - eprintlist(varlist.list); - eprintlist(arglist.list); -#ifdef FLUSHERR - outc('\n', &errout); - flushout(&errout); -#else - outcslow('\n', &errout); +#ifndef BB_PWD +static int +pwdcmd(argc, argv) + int argc; + char **argv; +{ + printf(snlfmt, curdir); + return 0; +} #endif - } - /* Now locate the command. */ - if (argc == 0) { - cmdentry.cmdtype = CMDBUILTIN; - firstbltin = cmdentry.u.cmd = BLTINCMD; +/* + * Find out what the current directory is. If we already know the current + * directory, this routine returns immediately. + */ +static void +getpwd(void) +{ + curdir = xgetcwd(0); + if(curdir==0) + curdir = nullstr; +} + +static void +setpwd(const char *val, int setold) +{ + if (setold) { + setvar("OLDPWD", curdir, VEXPORT); + } + INTOFF; + if (curdir != nullstr) { + free(curdir); + curdir = nullstr; + } + if (!val) { + getpwd(); } else { - const char *oldpath; - int findflag = DO_ERR; - int oldfindflag; + curdir = savestr(val); + } + INTON; + setvar("PWD", curdir, VEXPORT); +} - /* - * Modify the command lookup path, if a PATH= assignment - * is present - */ - for (sp = varlist.list ; sp ; sp = sp->next) - if (varequal(sp->text, defpathvar)) { - path = sp->text + 5; - findflag |= DO_BRUTE; - } - oldpath = path; - oldfindflag = findflag; - firstbltin = 0; - for(;;) { - find_command(argv[0], &cmdentry, findflag, path); - if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ - exitstatus = 127; -#ifdef FLUSHERR - flushout(&errout); -#endif - goto out; - } - /* implement bltin and command here */ - if (cmdentry.cmdtype != CMDBUILTIN) { - break; - } - if (!firstbltin) { - firstbltin = cmdentry.u.cmd; - } - if (cmdentry.u.cmd == BLTINCMD) { - for(;;) { - struct builtincmd *bcmd; +/* + * Errors and exceptions. + */ - argv++; - if (--argc == 0) - goto found; - if (!(bcmd = find_builtin(*argv))) { - outfmt(&errout, "%s: not found\n", *argv); - exitstatus = 127; -#ifdef FLUSHERR - flushout(&errout); -#endif - goto out; - } - cmdentry.u.cmd = bcmd; - if (bcmd != BLTINCMD) - break; - } - } - if (cmdentry.u.cmd == COMMANDCMD) { - argv++; - if (--argc == 0) { - goto found; - } - if (*argv[0] == '-') { - if (!equal(argv[0], "-p")) { - argv--; - argc++; - break; - } - argv++; - if (--argc == 0) { - goto found; - } - path = defpath; - findflag |= DO_BRUTE; - } else { - path = oldpath; - findflag = oldfindflag; - } - findflag |= DO_NOFUN; - continue; - } -found: - break; - } - } - - /* Fork off a child process if necessary. */ - if (cmd->ncmd.backgnd - || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) -#ifdef notyet - || ((flags & EV_BACKCMD) != 0 - && (cmdentry.cmdtype != CMDBUILTIN - || cmdentry.u.bcmd == DOTCMD - || cmdentry.u.bcmd == EVALCMD)) -#endif - ) { - jp = makejob(cmd, 1); - mode = cmd->ncmd.backgnd; -#ifdef notyet - if (flags & EV_BACKCMD) { - mode = FORK_NOJOB; - if (pipe(pip) < 0) - error("Pipe call failed"); - } -#endif - if (forkshell(jp, cmd, mode) != 0) - goto parent; /* at end of routine */ -#ifdef notyet - if (flags & EV_BACKCMD) { - FORCEINTON; - close(pip[0]); - if (pip[1] != 1) { - close(1); - dup_as_newfd(pip[1], 1); - close(pip[1]); - } - } -#endif - flags |= EV_EXIT; - } +/* + * Code to handle exceptions in C. + */ - /* This is the child process if a fork occurred. */ - /* Execute the command. */ - if (cmdentry.cmdtype == CMDFUNCTION) { -#ifdef DEBUG - trputs("Shell function: "); trargs(argv); -#endif - exitstatus = oexitstatus; - redirect(cmd->ncmd.redirect, REDIR_PUSH); - saveparam = shellparam; - shellparam.malloc = 0; - shellparam.nparam = argc - 1; - shellparam.p = argv + 1; - INTOFF; - savelocalvars = localvars; - localvars = NULL; - INTON; - if (setjmp(jmploc.loc)) { - if (exception == EXSHELLPROC) { - freeparam((volatile struct shparam *) - &saveparam); - } else { - saveparam.optind = shellparam.optind; - saveparam.optoff = shellparam.optoff; - 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, flags & EV_TESTED); - funcnest--; - INTOFF; - poplocalvars(); - localvars = savelocalvars; - saveparam.optind = shellparam.optind; - saveparam.optoff = shellparam.optoff; - 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) { -#ifdef DEBUG - trputs("builtin command: "); trargs(argv); -#endif - mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH; -#ifdef notyet - if (flags == EV_BACKCMD) { -#ifdef USE_GLIBC_STDIO - openmemout(); -#else - memout.nleft = 0; - memout.nextc = memout.buf; - memout.bufsize = 64; -#endif - mode |= REDIR_BACKQ; - } -#endif - redirect(cmd->ncmd.redirect, mode); - savecmdname = commandname; - if (firstbltin->flags & BUILTIN_SPECIAL) { - listsetvar(varlist.list); - } else { - 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 = (*cmdentry.u.cmd->builtinfunc)(argc, argv); - flushall(); -cmddone: - exitstatus |= outerr(out1); - out1 = &output; - out2 = &errout; - freestdout(); - cmdenviron = NULL; - if (e != EXSHELLPROC) { - commandname = savecmdname; - if (flags & EV_EXIT) - exitshell(exitstatus); - } - handler = savehandler; - if (e != -1) { - if ((e != EXERROR && e != EXEXEC) - || cmdentry.u.cmd == BLTINCMD - || cmdentry.u.cmd == DOTCMD - || cmdentry.u.cmd == EVALCMD - || cmdentry.u.cmd == EXECCMD) - exraise(e); - FORCEINTON; - } - if (cmdentry.u.cmd != EXECCMD) - popredir(); -#ifdef notyet - if (flags == EV_BACKCMD) { - INTOFF; -#ifdef USE_GLIBC_STDIO - if (__closememout()) { - error( - "__closememout() failed: %s", - strerror(errno) - ); - } -#endif - backcmd->buf = memout.buf; -#ifdef USE_GLIBC_STDIO - backcmd->nleft = memout.bufsize; -#else - backcmd->nleft = memout.nextc - memout.buf; -#endif - memout.buf = NULL; - INTON; - } -#endif - } else { -#ifdef DEBUG - trputs("normal command: "); trargs(argv); -#endif - redirect(cmd->ncmd.redirect, 0); - clearredir(); - for (sp = varlist.list ; sp ; sp = sp->next) - setvareq(sp->text, VEXPORT|VSTACK); - envp = environment(); - shellexec(argv, envp, path, cmdentry.u.index); - } - goto out; +/* + * 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. + */ -parent: /* parent process gets here (if we forked) */ - if (mode == 0) { /* argument to fork */ - INTOFF; - exitstatus = waitforjob(jp); - INTON; -#ifdef notyet - } else if (mode == 2) { - backcmd->fd = pip[0]; - close(pip[1]); - backcmd->jp = jp; -#endif - } +struct jmploc { + jmp_buf loc; +}; -out: - if (lastarg) - setvar("_", lastarg, 0); - popstackmark(&smark); -} +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ +#define EXEXEC 3 /* command execution failed */ +static struct jmploc *handler; +static int exception; +static void exverror (int, const char *, va_list) + __attribute__((__noreturn__)); /* - * 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. + * 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". */ +static void exraise (int) __attribute__((__noreturn__)); + static void -prehash(n) - union node *n; +exraise(int e) { - 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()); +#ifdef DEBUG + if (handler == NULL) + abort(); +#endif + flushall(); + exception = e; + longjmp(handler->loc, 1); } - /* - * Builtin commands. Builtin commands whose functions are closely - * tied to evaluation are implemented here. + * 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.) */ -/* - * No command given, or a bltin command with no arguments. Set the - * specified variables. - */ +static void +onint(void) { + sigset_t mysigset; -int -bltincmd(argc, argv) - int argc; - char **argv; -{ - /* - * Preserve exitstatus of a previous possible redirection - * as POSIX mandates - */ - return exitstatus; + if (suppressint) { + intpending++; + return; + } + intpending = 0; + sigemptyset(&mysigset); + sigprocmask(SIG_SETMASK, &mysigset, NULL); + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + raise(SIGINT); + } + /* NOTREACHED */ } +static char *commandname; /* currently executing command */ + /* - * 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. + * Exverror 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. */ - -static int -breakcmd(argc, argv) - int argc; - char **argv; +static void +exverror(int cond, const char *msg, va_list ap) { - int n = argc > 1 ? number(argv[1]) : 1; + CLEAR_PENDING_INT; + INTOFF; - if (n > loopnest) - n = loopnest; - if (n > 0) { - evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; - skipcount = n; - } - return 0; -} - - -/* - * The return command. - */ - -static int -returncmd(argc, argv) - int argc; - char **argv; -{ - int ret = argc > 1 ? number(argv[1]) : oexitstatus; - - if (funcnest) { - evalskip = SKIPFUNC; - skipcount = 1; - return ret; - } - else { - /* Do what ksh does; skip the rest of the file */ - evalskip = SKIPFILE; - skipcount = 1; - return ret; +#ifdef DEBUG + if (msg) + TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); + else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) { + if (commandname) + out2fmt("%s: ", commandname); + vfprintf(stderr, msg, ap); + out2c('\n'); } + exraise(cond); + /* NOTREACHED */ } -#ifndef BB_TRUE_FALSE -static int -false_main(argc, argv) - int argc; - char **argv; +#ifdef __STDC__ +static void +error(const char *msg, ...) +#else +static void +error(va_alist) + va_dcl +#endif { - return 1; +#ifndef __STDC__ + const char *msg; +#endif + va_list ap; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + msg = va_arg(ap, const char *); +#endif + exverror(EXERROR, msg, ap); + /* NOTREACHED */ + va_end(ap); } -static int -true_main(argc, argv) - int argc; - char **argv; -{ - return 0; -} +#ifdef __STDC__ +static void +exerror(int cond, const char *msg, ...) +#else +static void +exerror(va_alist) + va_dcl #endif - -static 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; +#ifndef __STDC__ + int cond; + const char *msg; +#endif + va_list ap; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + cond = va_arg(ap, int); + msg = va_arg(ap, const char *); +#endif + exverror(cond, msg, ap); + /* NOTREACHED */ + va_end(ap); } -static void -eprintlist(struct strlist *sp) -{ - for (; sp; sp = sp->next) { - outfmt(&errout, " %s",sp->text); - } -} -/* $NetBSD: exec.c,v 1.32 2001/02/04 19:52:06 christos Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ /* - * 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. + * Table of error messages. */ -#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 */ +struct errname { + short errcode; /* error number */ + char action; /* operation which encountered the error */ }; +/* + * Types of operations (passed to the errmsg routine). + */ -static struct tblentry *cmdtable[CMDTABLESIZE]; -static int builtinloc = -1; /* index in path of %builtin, or -1 */ -static int exerrno = 0; /* Last exec error */ +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ +#define ALL (E_OPEN|E_CREAT|E_EXEC) -static void tryexec __P((char *, char **, char **)); -#if !defined(BSD) && !defined(linux) -static void execinterp __P((char **, char **)); +static const struct errname errormsg[] = { + { EINTR, ALL }, + { EACCES, ALL }, + { EIO, ALL }, + { ENOENT, E_OPEN }, + { ENOENT, E_CREAT }, + { ENOENT, E_EXEC }, + { ENOTDIR, E_OPEN }, + { ENOTDIR, E_CREAT }, + { ENOTDIR, E_EXEC }, + { EISDIR, ALL }, + { EEXIST, E_CREAT }, +#ifdef EMFILE + { EMFILE, ALL }, +#endif + { ENFILE, ALL }, + { ENOSPC, ALL }, +#ifdef EDQUOT + { EDQUOT, ALL }, +#endif +#ifdef ENOSR + { ENOSR, ALL }, +#endif + { ENXIO, ALL }, + { EROFS, ALL }, + { ETXTBSY, ALL }, +#ifdef EAGAIN + { EAGAIN, E_EXEC }, +#endif + { ENOMEM, ALL }, +#ifdef ENOLINK + { ENOLINK, ALL }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, ALL }, +#endif +#ifdef ECOMM + { ECOMM, ALL }, +#endif +#ifdef ESTALE + { ESTALE, ALL }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, ALL }, #endif -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)); -#ifdef ASH_TYPE -static int describe_command __P((char *, int)); +#ifdef ELOOP + { ELOOP, ALL }, +#endif + { E2BIG, E_EXEC }, +#ifdef ELIBACC + { ELIBACC, E_EXEC }, #endif -static int path_change __P((const char *, int *)); +}; +#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname)) /* - * Exec a program. Never returns. If you change this routine, you may - * have to change the find_command routine as well. + * 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. */ -static void -shellexec(argv, envp, path, idx) - char **argv, **envp; - const char *path; - int idx; +static const char * +errmsg(int e, int action) { - char *cmdname; - int e; + struct errname const *ep; + static char buf[12]; - if (strchr(argv[0], '/') != NULL) { - tryexec(argv[0], argv, envp); - e = errno; - } else { - e = ENOENT; - while ((cmdname = padvance(&path, argv[0])) != NULL) { - if (--idx < 0 && pathopt == NULL) { - tryexec(cmdname, argv, envp); - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - } - stunalloc(cmdname); - } + for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return strerror(e); } - /* Map to POSIX errors */ - switch (e) { - case EACCES: - exerrno = 126; - break; - case ENOENT: - exerrno = 127; - break; - default: - exerrno = 2; - break; - } - exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); - /* NOTREACHED */ + snprintf(buf, sizeof buf, "error %d", e); + return buf; } +#ifdef ASH_OPTIMIZE_FOR_SIZE static void -tryexec(cmd, argv, envp) - char *cmd; - char **argv; - char **envp; - { - int e; -#if !defined(BSD) && !defined(linux) - char *p; -#endif - -#ifdef SYSV - do { - execve(cmd, argv, envp); - } while (errno == EINTR); -#else - execve(cmd, argv, envp); -#endif - e = errno; - if (e == ENOEXEC) { - INTOFF; - initshellproc(); - setinputfile(cmd, 0); - commandname = arg0 = savestr(argv[0]); -#if !defined(BSD) && !defined(linux) - INTON; - pgetc(); pungetc(); /* fill up input buffer */ - p = parsenextc; - if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { - argv[0] = cmd; - execinterp(argv, envp); - } - INTOFF; -#endif - setparam(argv + 1); - exraise(EXSHELLPROC); +__inton() { + if (--suppressint == 0 && intpending) { + onint(); } - errno = e; } +static void forceinton (void) { + suppressint = 0; + if (intpending) + onint(); +} +#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 */ -#if !defined(BSD) && !defined(linux) -/* - * 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. +static int evalskip; /* set if we are skipping commands */ +static int skipcount; /* number of levels to skip */ +static int loopnest; /* current loop nesting level */ +static int funcnest; /* depth of function calls */ + + +static struct strlist *cmdenviron; /* environment for builtin command */ +static int exitstatus; /* exit status of last command */ +static int oexitstatus; /* saved exit status */ + +static void evalsubshell (const union node *, int); +static void expredir (union node *); +static void prehash (union node *); +static void eprintlist (struct strlist *); + +static union node *parsecmd(int); +/* + * Called to reset things after an exception. */ -#define NEWARGS 5 +/* + * The eval commmand. + */ +static void evalstring (char *, int); -static void -execinterp(argv, envp) - char **argv, **envp; - { - int n; - char *inp; - char *outp; - char c; +static int +evalcmd(argc, argv) + int argc; + char **argv; +{ char *p; + char *concat; 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++; + 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); } - p++; + STPUTC('\0', concat); + p = grabstackstr(concat); } -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); - /* NOTREACHED */ + evalstring(p, EV_TESTED); + } + return exitstatus; } -#endif +/* + * Execute a command or commands contained in a string. + */ + +static void evaltree (union node *, int); +static void setinputstring (char *); +static void popfile (void); +static void setstackmark(struct stackmark *mark); +static void popstackmark(struct stackmark *mark); + + +static void +evalstring(char *s, int flag) +{ + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, flag); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} +static struct builtincmd *find_builtin (const char *); +static void expandarg (union node *, struct arglist *, int); +static void calcsize (const union node *); +static union node *copynode (const union node *); /* - * 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. + * Make a copy of a parse tree. */ -static const char *pathopt; +static int funcblocksize; /* size of structures in function */ +static int funcstringsize; /* size of strings in node */ +static pointer funcblock; /* block to allocate function from */ +static char *funcstring; /* block to allocate strings from */ -static char * -padvance(path, name) - const char **path; - const char *name; - { - const char *p; - char *q; - const char *start; - int len; - if (*path == NULL) +static inline union node * +copyfunc(union node *n) +{ + if (n == 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); + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *) funcblock + funcblocksize; + return copynode(n); } +/* + * Free a parse tree. + */ +static void +freefunc(union node *n) +{ + if (n) + ckfree(n); +} -/*** Command hashing code ***/ +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ -static int -hashcmd(argc, argv) - int argc; - char **argv; +static inline void +addcmdentry(char *name, struct cmdentry *entry) { - 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); - return 0; - } else if (c == 'v') { - verbose++; - } + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); } - if (*argptr == NULL) { - for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (cmdp->cmdtype != CMDBUILTIN) { - printentry(cmdp, verbose); - } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + +static inline void +evalloop(const union node *n, int flags) +{ + 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; } - return 0; - } - c = 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, DO_ERR, pathval()); - if (entry.cmdtype == CMDUNKNOWN) c = 1; - else if (verbose) { - cmdp = cmdlookup(name, 0); - if (cmdp) printentry(cmdp, verbose); - flushall(); + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; } - argptr++; + evaltree(n->nbinary.ch2, flags & EV_TESTED); + status = exitstatus; + if (evalskip) + goto skipping; } - return c; + loopnest--; + exitstatus = status; } - static void -printentry(cmdp, verbose) - struct tblentry *cmdp; - int verbose; - { - int idx; - const char *path; - char *name; +evalfor(const union node *n, int flags) +{ + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; - if (cmdp->cmdtype == CMDNORMAL) { - idx = cmdp->param.index; - path = pathval(); - do { - name = padvance(&path, cmdp->cmdname); - stunalloc(name); - } while (--idx >= 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); - out1fmt(" %s", name); - ckfree(name); - INTON; + 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 | EXP_RECORD); + 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, flags & EV_TESTED); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; } -#ifdef DEBUG - } else { - error("internal error: cmdtype %d", cmdp->cmdtype); -#endif } - out1fmt(snlfmt, cmdp->rehash ? "*" : nullstr); + loopnest--; +out: + popstackmark(&smark); } - - -/* - * Resolve a command name. If you change this routine, you may have to - * change the shellexec routine as well. - */ - -static void -find_command(name, entry, act, path) - char *name; - struct cmdentry *entry; - int act; - const char *path; +static inline void +evalcase(const union node *n, int flags) { - struct tblentry *cmdp; - int idx; - int prev; - char *fullname; - struct stat statb; - int e; - int bltin; - int firstchange; - int updatetbl; - bool regular; - struct builtincmd *bcmd; - - /* If name contains a slash, don't use the hash table */ - if (strchr(name, '/') != NULL) { - if (act & DO_ABS) { - while (stat(name, &statb) < 0) { - #ifdef SYSV - if (errno == EINTR) - continue; - #endif - if (errno != ENOENT && errno != ENOTDIR) - e = errno; - entry->cmdtype = CMDUNKNOWN; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = -1; - return; - } - entry->cmdtype = CMDNORMAL; - entry->u.index = 0; - return; - } - - updatetbl = 1; - if (act & DO_BRUTE) { - firstchange = path_change(path, &bltin); - } else { - bltin = builtinloc; - firstchange = 9999; - } + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; - /* If name is in the table, and not invalidated by cd, we're done */ - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { - if (cmdp->cmdtype == CMDFUNCTION) { - if (act & DO_NOFUN) { - updatetbl = 0; - } else { - goto success; - } - } else if (act & DO_BRUTE) { - if ((cmdp->cmdtype == CMDNORMAL && - cmdp->param.index >= firstchange) || - (cmdp->cmdtype == CMDBUILTIN && - ((builtinloc < 0 && bltin >= 0) ? - bltin : builtinloc) >= firstchange)) { - /* need to recompute the entry */ - } else { - goto success; + 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; } - } else { - goto success; - } - } - - bcmd = find_builtin(name); - regular = bcmd && bcmd->flags & BUILTIN_REGULAR; - - if (regular) { - if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) { - goto success; - } - } else if (act & DO_BRUTE) { - if (firstchange == 0) { - updatetbl = 0; } } +out: + popstackmark(&smark); +} - /* If %builtin not in path, check for builtin next */ - if (regular || (bltin < 0 && bcmd)) { -builtin: - if (!updatetbl) { - entry->cmdtype = CMDBUILTIN; - entry->u.cmd = bcmd; - return; - } - INTOFF; - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDBUILTIN; - cmdp->param.cmd = bcmd; - INTON; - goto success; - } +/* + * 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.) + */ - /* We have to search path. */ - prev = -1; /* where to start */ - if (cmdp && cmdp->rehash) { /* doing a rehash */ - if (cmdp->cmdtype == CMDBUILTIN) - prev = builtinloc; - else - prev = cmdp->param.index; - } +static inline void +evalpipe(n) + union node *n; +{ + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; - e = ENOENT; - idx = -1; -loop: - while ((fullname = padvance(&path, name)) != NULL) { - stunalloc(fullname); - idx++; - if (idx >= firstchange) { - updatetbl = 0; + 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 (pathopt) { - if (prefix("builtin", pathopt)) { - if ((bcmd = find_builtin(name))) { - goto builtin; + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + dup_as_newfd(prevfd, 0); + close(prevfd); + if (pip[0] == 0) { + pip[0] = -1; } - continue; - } else if (!(act & DO_NOFUN) && - prefix("func", pathopt)) { - /* handled below */ - } else { - continue; /* ignore unimplemented options */ } + if (pip[1] >= 0) { + if (pip[0] >= 0) { + close(pip[0]); + } + if (pip[1] != 1) { + close(1); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); } - /* if rehash, don't redo absolute path names */ - if (fullname[0] == '/' && idx <= prev && - idx < firstchange) { - if (idx < prev) - continue; - 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)) - continue; - 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)); - /* If we aren't called with DO_BRUTE and cmdp is set, it must - be a function and we're being called with DO_NOFUN */ - if (!updatetbl) { - entry->cmdtype = CMDNORMAL; - entry->u.index = idx; - return; - } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { INTOFF; - cmdp = cmdlookup(name, 1); - cmdp->cmdtype = CMDNORMAL; - cmdp->param.index = idx; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); INTON; - goto success; } - - /* We failed. If there was an entry for this command, delete it */ - if (cmdp && updatetbl) - delete_cmd_entry(); - if (act & DO_ERR) - 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; } +static void find_command (const char *, struct cmdentry *, int, const char *); - -/* - * Search the table of builtin commands. - */ - -struct builtincmd * -find_builtin(name) - char *name; -{ - struct builtincmd *bp; - - bp = bsearch( &name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd), - pstrcmp - ); - return bp; +static int +isassignment(const char *word) { + if (!is_name(*word)) { + return 0; + } + do { + word++; + } while (is_in_name(*word)); + return *word == '='; } -/* - * Called when a cd is done. Marks all commands so the next time they - * are executed they will be rehashed. - */ - static 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; +evalcommand(union node *cmd, int flags) +{ + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + struct strlist *sp; + int mode; + struct cmdentry cmdentry; + struct job *jp; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + const char *path; + const struct builtincmd *firstbltin; + struct jmploc *volatile savehandler; + struct jmploc jmploc; +#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; + arglist.list = 0; + oexitstatus = exitstatus; + exitstatus = 0; + path = pathval(); + for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { + expandarg(argp, &varlist, EXP_VARTILDE); + } + for ( + argp = cmd->ncmd.args; argp && !arglist.list; + argp = argp->narg.next + ) { + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + if (argp) { + struct builtincmd *bcmd; + int pseudovarflag; + bcmd = find_builtin(arglist.list->text); + pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd); + for (; argp; argp = argp->narg.next) { + if (pseudovarflag && isassignment(argp->narg.text)) { + expandarg(argp, &arglist, EXP_VARTILDE); + continue; + } + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); } } -} - - - -/* - * 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. - */ - -static void -changepath(newval) - const char *newval; -{ - int firstchange; - int bltin; + *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)); - firstchange = path_change(newval, &bltin); - if (builtinloc < 0 && bltin >= 0) - builtinloc = bltin; /* zap builtins */ - clearcmdentry(firstchange); - builtinloc = bltin; -} + 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) { + out2c('+'); + eprintlist(varlist.list); + eprintlist(arglist.list); + out2c('\n'); + } -/* - * Clear out command entries. The argument specifies the first entry in - * PATH which has changed. - */ + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + firstbltin = cmdentry.u.cmd = BLTINCMD; + } else { + const char *oldpath; + int findflag = DO_ERR; + int oldfindflag; -static void -clearcmdentry(firstchange) - int firstchange; -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (sp = varlist.list ; sp ; sp = sp->next) + if (varequal(sp->text, defpathvar)) { + path = sp->text + 5; + findflag |= DO_BRUTE; + } + oldpath = path; + oldfindflag = findflag; + firstbltin = 0; + for(;;) { + find_command(argv[0], &cmdentry, findflag, path); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 127; + goto out; + } + /* implement bltin and command here */ + if (cmdentry.cmdtype != CMDBUILTIN) { + break; + } + if (!firstbltin) { + firstbltin = cmdentry.u.cmd; + } + if (cmdentry.u.cmd == BLTINCMD) { + for(;;) { + struct builtincmd *bcmd; - 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; + argv++; + if (--argc == 0) + goto found; + if (!(bcmd = find_builtin(*argv))) { + out2fmt("%s: not found\n", *argv); + exitstatus = 127; + goto out; + } + cmdentry.u.cmd = bcmd; + if (bcmd != BLTINCMD) + break; + } + } + if (cmdentry.u.cmd == find_builtin("command")) { + argv++; + if (--argc == 0) { + goto found; + } + if (*argv[0] == '-') { + if (!equal(argv[0], "-p")) { + argv--; + argc++; + break; + } + argv++; + if (--argc == 0) { + goto found; + } + path = defpath; + findflag |= DO_BRUTE; + } else { + path = oldpath; + findflag = oldfindflag; + } + findflag |= DO_NOFUN; + continue; } +found: + break; } } - INTON; -} - - -/* - * Delete all functions. - */ -#ifdef mkinit -static void deletefuncs __P((void)); + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) + ) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + flags |= EV_EXIT; + } -SHELLPROC { - deletefuncs(); -} + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); #endif - -static 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); + exitstatus = oexitstatus; + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) { + freeparam((volatile struct shparam *) + &saveparam); } else { - pp = &cmdp->next; + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + freeparam(&shellparam); + shellparam = saveparam; } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); } - } - INTON; -} + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, flags & EV_TESTED); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + saveparam.optind = shellparam.optind; + saveparam.optoff = shellparam.optoff; + 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) { +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH; + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + if (IS_BUILTIN_SPECIAL(firstbltin)) { + listsetvar(varlist.list); + } else { + 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 = (*cmdentry.u.cmd->builtinfunc)(argc, argv); + flushall(); +cmddone: + cmdenviron = NULL; + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) + exitshell(exitstatus); + } + handler = savehandler; + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.u.cmd == BLTINCMD + || cmdentry.u.cmd == DOTCMD + || cmdentry.u.cmd == EVALCMD + || cmdentry.u.cmd == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.cmd != EXECCMD) + popredir(); + } else { +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, 0); + clearredir(); + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, path, cmdentry.u.index); + } + goto out; +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} /* - * 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. + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. */ - -struct tblentry **lastcmdentry; - - -static struct tblentry * -cmdlookup(name, add) - char *name; - int add; +static void +evaltree(n, flags) + union node *n; + int flags; { - int hashval; - 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)) + int checkexit = 0; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + goto out; + } + TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & EV_TESTED); + 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: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + evaltree(n->nif.ifpart, flags); + else if (n->nif.elsepart) + evaltree(n->nif.elsepart, flags); + else + exitstatus = 0; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n, flags); + break; + case NFOR: + evalfor(n, flags); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: { + struct builtincmd *bcmd; + struct cmdentry entry; + if ( + (bcmd = find_builtin(n->narg.text)) && + IS_BUILTIN_SPECIAL(bcmd) + ) { + out2fmt("%s is a special built-in\n", n->narg.text); + exitstatus = 1; break; - pp = &cmdp->next; + } + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(n->narg.next); + addcmdentry(n->narg.text, &entry); + exitstatus = 0; + break; } - 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; + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + checkexit = 1; + break; + case NCMD: + evalcommand(n, flags); + checkexit = 1; + break; +#ifdef DEBUG + default: + printf("Node type = %d\n", n->type); + break; +#endif } - lastcmdentry = pp; - return cmdp; +out: + if (pendingsigs) + dotrap(); + if ( + flags & EV_EXIT || + (checkexit && eflag && exitstatus && !(flags & EV_TESTED)) + ) + exitshell(exitstatus); } /* - * Delete the command entry returned on the last lookup. + * Kick off a subshell to evaluate a tree. */ static void -delete_cmd_entry() { - struct tblentry *cmdp; +evalsubshell(const union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); - INTOFF; - cmdp = *lastcmdentry; - *lastcmdentry = cmdp->next; - ckfree(cmdp); - INTON; + 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 fixredir(union node *n, const char *text, int err); -#ifdef notdef static void -getcmdentry(name, entry) - char *name; - struct cmdentry *entry; - { - struct tblentry *cmdp = cmdlookup(name, 0); +expredir(union node *n) +{ + union node *redir; - if (cmdp) { - entry->u = cmdp->param; - entry->cmdtype = cmdp->cmdtype; - } else { - entry->cmdtype = CMDUNKNOWN; - entry->u.index = 0; - } + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + fn.lastp = &fn.list; + oexitstatus = exitstatus; + switch (redir->type) { + case NFROMTO: + case NFROM: + case NTO: + case NAPPEND: + case NTOOV: + 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; + } + } } -#endif /* - * Add a new command entry, replacing any existing command entry for - * the same name. + * 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. */ static void -addcmdentry(name, entry) - char *name; - struct cmdentry *entry; - { - struct tblentry *cmdp; +evalbackcmd(union node *n, struct backcmd *result) +{ + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ - INTOFF; - cmdp = cmdlookup(name, 1); - if (cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + exitstatus = 0; + goto out; } - cmdp->cmdtype = entry->cmdtype; - cmdp->param = entry->u; - INTON; + 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); + dup_as_newfd(pip[1], 1); + close(pip[1]); + } + eflag = 0; + 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)); } /* - * Define a shell function. + * Execute a simple command. + */ + +/* + * 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 -defun(name, func) - char *name; - union node *func; - { +prehash(n) + union node *n; +{ struct cmdentry entry; - entry.cmdtype = CMDFUNCTION; - entry.u.func = copyfunc(func); - addcmdentry(name, &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()); } /* - * Delete a function if it exists. + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. */ -static void -unsetfunc(name) - char *name; - { - struct tblentry *cmdp; +/* + * No command given, or a bltin command with no arguments. Set the + * specified variables. + */ - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); - delete_cmd_entry(); - } +int +bltincmd(argc, argv) + int argc; + char **argv; +{ + /* + * Preserve exitstatus of a previous possible redirection + * as POSIX mandates + */ + return exitstatus; } -#ifdef ASH_TYPE + /* - * Locate and print what a word is... + * 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. */ static int -typecmd(argc, argv) +breakcmd(argc, argv) int argc; char **argv; { - int i; - int err = 0; + int n = argc > 1 ? number(argv[1]) : 1; - for (i = 1; i < argc; i++) { - err |= describe_command(argv[i], 1); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; } - return err; + return 0; } -static int -describe_command(command, verbose) - char *command; - int verbose; -{ - struct cmdentry entry; - struct tblentry *cmdp; - const struct alias *ap; - const char *path = pathval(); - - if (verbose) { - out1str(command); - } - /* First look at the keywords */ - if (findkwd(command)) { - out1str(verbose ? " is a shell keyword" : command); - goto out; - } +/* + * The return command. + */ - /* Then look at the aliases */ - if ((ap = lookupalias(command, 0)) != NULL) { - if (verbose) { - out1fmt(" is an alias for %s", ap->val); - } else { - printalias(ap); - } - goto out; - } +static int +returncmd(argc, argv) + int argc; + char **argv; +{ + int ret = argc > 1 ? number(argv[1]) : oexitstatus; - /* Then check if it is a tracked alias */ - if ((cmdp = cmdlookup(command, 0)) != NULL) { - entry.cmdtype = cmdp->cmdtype; - entry.u = cmdp->param; - } else { - /* Finally use brute force */ - find_command(command, &entry, DO_ABS, path); + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + return ret; } - - switch (entry.cmdtype) { - case CMDNORMAL: { - int j = entry.u.index; - char *p; - if (j == -1) { - p = command; - } else { - do { - p = padvance(&path, command); - stunalloc(p); - } while (--j >= 0); - } - if (verbose) { - out1fmt( - " is%s %s", - cmdp ? " a tracked alias for" : nullstr, p - ); - } else { - out1str(p); - } - break; + else { + /* Do what ksh does; skip the rest of the file */ + evalskip = SKIPFILE; + skipcount = 1; + return ret; } +} - case CMDFUNCTION: - if (verbose) { - out1str(" is a shell function"); - } else { - out1str(command); - } - break; - - case CMDBUILTIN: - if (verbose) { - out1fmt( - " is a %sshell builtin", - entry.u.cmd->flags & BUILTIN_SPECIAL ? - "special " : nullstr - ); - } else { - out1str(command); - } - break; - - default: - if (verbose) { - out1str(": not found\n"); - } - return 127; - } -out: - out1c('\n'); - return 0; +#ifndef BB_TRUE_FALSE +static int +false_main(argc, argv) + int argc; + char **argv; +{ + return 1; } -#endif + static int -commandcmd(argc, argv) +true_main(argc, argv) int argc; char **argv; { - int c; - int default_path = 0; - int verify_only = 0; - int verbose_verify_only = 0; + return 0; +} +#endif - while ((c = nextopt("pvV")) != '\0') - switch (c) { - case 'p': - default_path = 1; - break; - case 'v': - verify_only = 1; - break; - case 'V': - verbose_verify_only = 1; - break; - default: - outfmt(out2, -"command: nextopt returned character code 0%o\n", c); - return EX_SOFTWARE; - } +/* + * Controls whether the shell is interactive or not. + */ - if (default_path + verify_only + verbose_verify_only > 1 || - !*argptr) { - outfmt(out2, -"command [-p] command [arg ...]\n"); - outfmt(out2, -"command {-v|-V} command\n"); - return EX_USAGE; - } +static void setsignal(int signo); +static void chkmail(int silent); -#ifdef ASH_TYPE - if (verify_only || verbose_verify_only) { - return describe_command(*argptr, verbose_verify_only); + +static void +setinteractive(int on) +{ + static int is_interactive; + static int do_banner=0; + + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + chkmail(1); + is_interactive = on; + if (do_banner==0 && is_interactive) { + /* Looks like they want an interactive shell */ + printf( "\n\n" BB_BANNER " Built-in shell (ash)\n"); + printf( "Enter 'help' for a list of built-in commands.\n\n"); + do_banner=1; } -#endif +} - return 0; +static void +optschanged(void) +{ + setinteractive(iflag); + setjobctl(mflag); } + static int -path_change(newval, bltin) - const char *newval; - int *bltin; +execcmd(argc, argv) + int argc; + char **argv; { - const char *old, *new; - int idx; - int firstchange; + if (argc > 1) { + struct strlist *sp; - old = pathval(); - new = newval; - firstchange = 9999; /* assume no change */ - idx = 0; - *bltin = -1; - for (;;) { - if (*old != *new) { - firstchange = idx; - 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 = idx; - if (*new == ':') { - idx++; - } - new++, old++; + 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); } - if (builtinloc >= 0 && *bltin < 0) - firstchange = 0; - return firstchange; + return 0; } -/* $NetBSD: expand.c,v 1.50 2001/02/04 19:52:06 christos Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +static void +eprintlist(struct strlist *sp) +{ + for (; sp; sp = sp->next) { + out2fmt(" %s",sp->text); + } +} /* - * Routines to expand arguments to commands. We have to deal with - * backquotes, shell variables, and file metacharacters. - */ -/* - * _rmescape() flags + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. */ -#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ -#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ + +static const char *pathopt; /* set by padvance */ + +static void +shellexec(argv, envp, path, idx) + char **argv, **envp; + const char *path; + int idx; +{ + 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 (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + + /* Map to POSIX errors */ + switch (e) { + case EACCES: + exerrno = 126; + break; + case ENOENT: + exerrno = 127; + break; + default: + exerrno = 2; + break; + } + exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, E_EXEC)); + /* NOTREACHED */ +} /* - * Structure specifying which parts of the string should be searched - * for IFS characters. + * Clear traps on a fork. */ +static void +clear_traps(void) { + char **tp; -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 */ -}; + for (tp = trap ; tp < &trap[NSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} -static 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, int, int)); -static char *evalvar __P((char *, int)); -static int varisset __P((char *, int)); -static void strtodest __P((const char *, const char *, int)); -static void varvalue __P((char *, int, int)); -static void recordregion __P((int, int, int)); -static void removerecordregions __P((int)); -static void ifsbreakup __P((char *, struct arglist *)); -static void ifsfree __P((void)); -static void expandmeta __P((struct strlist *, int)); -#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) -#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB) -#if !defined(GLOB_BROKEN) -static void addglob __P((const glob_t *)); -#endif -#endif -#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) -static void expmeta __P((char *, char *)); -#endif -static void addfname __P((char *)); -#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) -static struct strlist *expsort __P((struct strlist *)); -static struct strlist *msort __P((struct strlist *, int)); +static void +initshellproc(void) { + +#ifdef ASH_ALIAS + /* from alias.c: */ + { + rmaliases(); + } #endif -#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) -static int patmatch __P((char *, char *, int)); -static int patmatch2 __P((char *, char *, int)); -#else -static int pmatch __P((char *, char *, int)); -#define patmatch2 patmatch + /* from eval.c: */ + { + exitstatus = 0; + } + + /* from exec.c: */ + { + deletefuncs(); + } + + /* from jobs.c: */ + { + backgndpid = -1; +#ifdef JOBS + jobctl = 0; #endif -static char *cvtnum __P((int, char *)); + } + + /* from options.c: */ + { + int i; -extern int oexitstatus; + for (i = 0; i < NOPTS; i++) + optent_val(i) = 0; + optschanged(); -/* - * Expand shell variables and backquotes inside a here document. - */ + } -static 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()); + /* from redir.c: */ + { + clearredir(); + } + + /* from trap.c: */ + { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } + } + + /* from var.c: */ + { + shprocvar(); + } } +static int preadbuffer(void); +static void pushfile (void); /* - * 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. + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. */ -static void -expandarg(arg, arglist, flag) - union node *arg; - struct arglist *arglist; - int flag; +#ifndef ASH_OPTIMIZE_FOR_SIZE +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) +static int +pgetc(void) { - 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; - } - ifsfree(); - *exparg.lastp = NULL; - if (exparg.list) { - *arglist->lastp = exparg.list; - arglist->lastp = exparg.lastp; - } + return pgetc_macro(); +} +#else +static int +pgetc_macro(void) +{ + return --parsenleft >= 0? *parsenextc++ : preadbuffer(); } +static inline int +pgetc(void) +{ + return pgetc_macro(); +} +#endif /* - * 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. + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. */ static void -argstr(p, flag) - char *p; - int flag; -{ - 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 CTLQUOTEMARK: - /* "$@" syntax adherence hack */ - if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') - break; - if ((flag & EXP_FULL) != 0) - STPUTC(c, expdest); - break; - 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; -#ifdef ASH_MATH_SUPPORT - case CTLENDARI: - expari(flag); - break; -#endif - 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:; - return; -} - -static char * -exptilde(p, flag) - char *p; - int flag; -{ - char c, *startp = p; - struct passwd *pw; - const char *home; - int quotes = flag & (EXP_FULL | EXP_CASE); - - while ((c = *p) != '\0') { - switch(c) { - case CTLESC: - return (startp); - case CTLQUOTEMARK: - 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; - strtodest(home, SQSYNTAX, quotes); - return (p); -lose: - *p = c; - return (startp); +pungetc() { + parsenleft++; + parsenextc--; } -static void -removerecordregions(endoff) - int endoff; -{ - if (ifslastp == NULL) - return; +static void +popfile(void) { + struct parsefile *pf = parsefile; - if (ifsfirst.endoff > endoff) { - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - if (ifsfirst.begoff > endoff) - ifslastp = NULL; - else { - ifslastp = &ifsfirst; - ifsfirst.endoff = endoff; - } - return; - } - - ifslastp = &ifsfirst; - while (ifslastp->next && ifslastp->next->begoff < endoff) - ifslastp=ifslastp->next; - while (ifslastp->next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifslastp->next->next; - ckfree(ifslastp->next); - ifslastp->next = ifsp; - INTON; - } - if (ifslastp->endoff > endoff) - ifslastp->endoff = endoff; + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parselleft = parsefile->lleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; } -#ifdef ASH_MATH_SUPPORT /* - * Expand arithmetic expression. Backup to start of expression, - * evaluate, place result in (backed up) result, adjust string position. + * Return to top level. */ + static void -expari(flag) - int flag; -{ - char *p, *start; - int result; - int begoff; - int quotes = flag & (EXP_FULL | EXP_CASE); - int quoted; +popallfiles(void) { + while (parsefile != &basepf) + popfile(); +} - /* ifsfree(); */ +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ - /* - * This routine is slightly over-complicated 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(10, expdest); - USTPUTC('\0', expdest); - start = stackblock(); - p = expdest - 1; - 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++; +static void +closescript() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} - if (p[1] == '"') - quoted=1; - else - quoted=0; - begoff = p - start; - removerecordregions(begoff); - if (quotes) - rmescapes(p+2); - result = arith(p+2); - fmtstr(p, 12, "%d", result); - while (*p++) - ; +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ - if (quoted == 0) - recordregion(begoff, p - 1 - start, 0); - result = expdest - p + 1; - STADJUST(-result, expdest); +static void +setinputfd(fd, push) + int fd, push; +{ + (void) fcntl(fd, F_SETFD, FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = 0; + } else { + closescript(); + while (parsefile->strpush) + popstring(); + } + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parselleft = parsenleft = 0; + plinno = 1; } -#endif /* - * Expand stuff in backwards quotes. + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. */ static void -expbackq(cmd, quoted, flag) - union node *cmd; - int quoted; - int flag; +setinputfile(const char *fname, int push) { - volatile struct backcmd in; - int i; - char buf[128]; - char *p; - char *dest = expdest; - volatile struct ifsregion saveifs; - struct ifsregion *volatile savelastp; - struct nodelist *volatile saveargbackq; - char lastc; - int startloc = dest - stackblock(); - char const *syntax = quoted? DQSYNTAX : BASESYNTAX; - volatile int saveherefd; - int quotes = flag & (EXP_FULL | EXP_CASE); - struct jmploc jmploc; - struct jmploc *volatile savehandler; - int ex; - -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &dest; - (void) &syntax; -#endif - - in.fd = -1; - in.buf = 0; - in.jp = 0; + int fd; + int myfileno2; INTOFF; - saveifs = ifsfirst; - savelastp = ifslastp; - saveargbackq = argbackq; - saveherefd = herefd; - herefd = -1; - if ((ex = setjmp(jmploc.loc))) { - goto err1; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + myfileno2 = dup_as_newfd(fd, 10); + close(fd); + if (myfileno2 < 0) + error("Out of file descriptors"); + fd = myfileno2; } - savehandler = handler; - handler = &jmploc; + setinputfd(fd, push); INTON; - p = grabstackstr(dest); - evalbackcmd(cmd, (struct backcmd *) &in); - ungrabstackstr(p, dest); -err1: - INTOFF; - ifsfirst = saveifs; - ifslastp = savelastp; - argbackq = saveargbackq; - herefd = saveherefd; - if (ex) { - goto err2; - } +} - 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[(int)lastc] == CCTL) - STPUTC(CTLESC, dest); - STPUTC(lastc, dest); - } - } - /* Eat all trailing newlines */ - for (; dest > stackblock() && dest[-1] == '\n';) - STUNPUTC(dest); +static void +tryexec(char *cmd, char **argv, char **envp) +{ + int e; -err2: - if (in.fd >= 0) - close(in.fd); - if (in.buf) - ckfree(in.buf); - if (in.jp) - exitstatus = waitforjob(in.jp); - handler = savehandler; - if (ex) { - longjmp(handler->loc, 1); +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + char *name = cmd; + char** argv_l=argv; + int argc_l; +#ifdef BB_FEATURE_SH_APPLETS_ALWAYS_WIN + name = get_last_path_component(name); +#endif + argv_l=envp; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + putenv(*argv_l); + argv_l=argv; + for(argc_l=0;*argv_l!=NULL; argv_l++, argc_l++) + optind = 1; + run_applet_by_name(name, argc_l, argv); +#endif + execve(cmd, argv, envp); + e = errno; + if (e == ENOEXEC) { + INTOFF; + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); + setparam(argv + 1); + exraise(EXSHELLPROC); } - 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; + errno = e; } +static char *commandtext (const union node *); + +/* + * 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. + */ +static const char *pathopt; -static int -subevalvar(p, str, strloc, subtype, startloc, varflags, quotes) - char *p; - char *str; - int strloc; - int subtype; - int startloc; - int varflags; - int quotes; +static void growstackblock(void); + + +static char * +padvance(const char **path, const char *name) { - char *startp; - char *loc = NULL; + const char *p; char *q; - int c = 0; - int saveherefd = herefd; - struct nodelist *saveargbackq = argbackq; - int amount; - - herefd = -1; - argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); - STACKSTRNUL(expdest); - herefd = saveherefd; - argbackq = saveargbackq; - startp = stackblock() + startloc; - if (str == NULL) - str = stackblock() + strloc; + const char *start; + int len; - switch (subtype) { - case VSASSIGN: - setvar(str, startp, 0); - amount = startp - expdest; - STADJUST(amount, expdest); - varflags &= ~VSNUL; - if (c != 0) - *loc = c; - return 1; + 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); +} - case VSQUESTION: - if (*p != CTLENDVAR) { - outfmt(&errout, snlfmt, startp); - error((char *)NULL); - } - error("%.*s: parameter %snot set", p - str - 1, - str, (varflags & VSNUL) ? "null or " - : nullstr); - /* NOTREACHED */ +/* + * Wrapper around strcmp for qsort/bsearch/... + */ +static int +pstrcmp(const void *a, const void *b) +{ + return strcmp((const char *) a, *(const char *const *) b); +} - case VSTRIMLEFT: - for (loc = startp; loc < str; loc++) { - c = *loc; - *loc = '\0'; - if (patmatch2(str, startp, quotes)) - goto recordleft; - *loc = c; - if (quotes && *loc == CTLESC) - loc++; - } - return 0; +/* + * Find a keyword is in a sorted array. + */ - case VSTRIMLEFTMAX: - for (loc = str - 1; loc >= startp;) { - c = *loc; - *loc = '\0'; - if (patmatch2(str, startp, quotes)) - goto recordleft; - *loc = c; - loc--; - if (quotes && loc > startp && *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; +static const char *const * +findkwd(const char *s) +{ + return bsearch(s, parsekwd, sizeof(parsekwd) / sizeof(const char *), + sizeof(const char *), pstrcmp); +} - case VSTRIMRIGHT: - for (loc = str - 1; loc >= startp;) { - if (patmatch2(str, loc, quotes)) - goto recordright; - loc--; - if (quotes && loc > startp && *(loc - 1) == CTLESC) { - for (q = startp; q < loc; q++) - if (*q == CTLESC) - q++; - if (q > loc) - loc--; - } - } - return 0; - case VSTRIMRIGHTMAX: - for (loc = startp; loc < str - 1; loc++) { - if (patmatch2(str, loc, quotes)) - goto recordright; - if (quotes && *loc == CTLESC) - loc++; - } - return 0; +/*** Command hashing code ***/ -#ifdef DEBUG - default: - abort(); -#endif - } -recordleft: - *loc = c; - amount = ((str - 1) - (loc - startp)) - expdest; - STADJUST(amount, expdest); - while (loc != str - 1) - *startp++ = *loc++; - return 1; - -recordright: - amount = loc - expdest; - STADJUST(amount, expdest); - STPUTC('\0', expdest); - STADJUST(-1, expdest); - 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; +static int +hashcmd(argc, argv) + int argc; + char **argv; { - int subtype; - int varflags; - char *var; - char *val; - int patloc; + struct tblentry **pp; + struct tblentry *cmdp; int c; - int set; - int special; - int startloc; - int varlen; - int easy; - int quotes = flag & (EXP_FULL | EXP_CASE); + int verbose; + struct cmdentry entry; + char *name; +#ifdef ASH_ALIAS + const struct alias *ap; +#endif - 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, varflags & VSNUL); - val = NULL; - } else { - val = lookupvar(var); - if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { - val = NULL; - set = 0; - } else - set = 1; + verbose = 0; + while ((c = nextopt("rvV")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + return 0; + } else if (c == 'v' || c == 'V') { + verbose = c; + } } - varlen = 0; - startloc = expdest - stackblock(); - if (set && subtype != VSPLUS) { - /* insert the value of the variable */ - if (special) { - varvalue(var, varflags & VSQUOTE, flag); - if (subtype == VSLENGTH) { - varlen = expdest - stackblock() - startloc; - STADJUST(-varlen, expdest); - } - } else { - if (subtype == VSLENGTH) { - varlen = strlen(val); - } else { - strtodest( - val, - varflags & VSQUOTE ? - DQSYNTAX : BASESYNTAX, - quotes - ); + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype != CMDBUILTIN) { + printentry(cmdp, verbose); + } } } + return 0; } + c = 0; + while ((name = *argptr++) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0))) + delete_cmd_entry(); +#ifdef ASH_ALIAS + /* Then look at the aliases */ + if ((ap = lookupalias(name, 0)) != NULL) { + if (verbose=='v') + printf("%s is an alias for %s\n", name, ap->val); + else + printalias(ap); + continue; + } +#endif + /* First look at the keywords */ + if (findkwd(name)!=0) { + if (verbose=='v') + printf("%s is a shell keyword\n", name); + else + printf(snlfmt, name); + continue; + } - if (subtype == VSPLUS) - set = ! set; + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) c = 1; + else if (verbose) { + cmdp = cmdlookup(name, 0); + if (cmdp) printentry(cmdp, verbose=='v'); + flushall(); + } + } + return c; +} - easy = ((varflags & VSQUOTE) == 0 || - (*var == '@' && shellparam.nparam != 1)); +static void +printentry(cmdp, verbose) + struct tblentry *cmdp; + int verbose; + { + int idx; + const char *path; + char *name; + printf("%s%s", cmdp->cmdname, (verbose ? " is " : "")); + if (cmdp->cmdtype == CMDNORMAL) { + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + if(verbose) + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + if(verbose) + out1str("a shell builtin"); + } else if (cmdp->cmdtype == CMDFUNCTION) { + if (verbose) { + INTOFF; + out1str("a function\n"); + name = commandtext(cmdp->param.func); + printf("%s() {\n %s\n}", cmdp->cmdname, name); + ckfree(name); + INTON; + } +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + printf(snlfmt, cmdp->rehash ? "*" : nullstr); +} - 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; +/*** List the available builtins ***/ - 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); - patloc = expdest - stackblock(); - if (subevalvar(p, NULL, patloc, subtype, - startloc, varflags, quotes) == 0) { - int amount = (expdest - stackblock() - patloc) + 1; - STADJUST(-amount, expdest); - } - /* Remove any recorded regions beyond start of variable */ - removerecordregions(startloc); - goto record; - case VSASSIGN: - case VSQUESTION: - if (!set) { - if (subevalvar(p, var, 0, subtype, startloc, - varflags, quotes)) { - varflags &= ~VSNUL; - /* - * Remove any recorded regions beyond - * start of variable - */ - removerecordregions(startloc); - goto again; - } - break; - } - if (easy) - goto record; - break; +static int helpcmd(int argc, char** argv) +{ + int col, i; -#ifdef DEBUG - default: - abort(); -#endif + printf("\nBuilt-in commands:\n-------------------\n"); + for (col=0, i=0; i < NUMBUILTINS; i++) { + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + builtincmds[i].name+1); + if (col > 60) { + printf("\n"); + col = 0; + } } +#ifdef BB_FEATURE_SH_STANDALONE_SHELL + { + extern const struct BB_applet applets[]; + extern const size_t NUM_APPLETS; - 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; + for (i=0; i < NUM_APPLETS; i++) { + + col += printf("%c%s", ((col == 0) ? '\t' : ' '), + applets[i].name); + if (col > 60) { + printf("\n"); + col = 0; } } } - return p; +#endif + printf("\n\n"); + return EXIT_SUCCESS; } - - /* - * Test whether a specialized variable is set. + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. */ -static int -varisset(name, nulok) - char *name; - int nulok; +static int prefix (const char *, const char *); + +static void +find_command(const char *name, struct cmdentry *entry, int act, const char *path) { - if (*name == '!') - return backgndpid != -1; - else if (*name == '@' || *name == '*') { - if (*shellparam.p == NULL) - return 0; + struct tblentry *cmdp; + int idx; + int prev; + char *fullname; + struct stat statb; + int e; + int bltin; + int firstchange; + int updatetbl; + int regular; + struct builtincmd *bcmd; - if (nulok) { - char **av; + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + if (act & DO_ABS) { + while (stat(name, &statb) < 0) { + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + entry->cmdtype = CMDUNKNOWN; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = -1; + return; + } + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } - for (av = shellparam.p; *av; av++) - if (**av != '\0') - return 1; - return 0; + updatetbl = 1; + if (act & DO_BRUTE) { + firstchange = path_change(path, &bltin); + } else { + bltin = builtinloc; + firstchange = 9999; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) { + if (cmdp->cmdtype == CMDFUNCTION) { + if (act & DO_NOFUN) { + updatetbl = 0; + } else { + goto success; + } + } else if (act & DO_BRUTE) { + if ((cmdp->cmdtype == CMDNORMAL && + cmdp->param.index >= firstchange) || + (cmdp->cmdtype == CMDBUILTIN && + ((builtinloc < 0 && bltin >= 0) ? + bltin : builtinloc) >= firstchange)) { + /* need to recompute the entry */ + } else { + goto success; + } + } else { + goto success; } - } else if (is_digit(*name)) { - char *ap; - int num = atoi(name); + } - if (num > shellparam.nparam) - return 0; + bcmd = find_builtin(name); + regular = bcmd && IS_BUILTIN_REGULAR(bcmd); - if (num == 0) - ap = arg0; + if (regular) { + if (cmdp && (cmdp->cmdtype == CMDBUILTIN)) { + goto success; + } + } else if (act & DO_BRUTE) { + if (firstchange == 0) { + updatetbl = 0; + } + } + + /* If %builtin not in path, check for builtin next */ + if (regular || (bltin < 0 && bcmd)) { +builtin: + if (!updatetbl) { + entry->cmdtype = CMDBUILTIN; + entry->u.cmd = bcmd; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.cmd = bcmd; + INTON; + goto success; + } + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp && cmdp->rehash) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; else - ap = shellparam.p[num - 1]; + prev = cmdp->param.index; + } - if (nulok && (ap == NULL || *ap == '\0')) - return 0; + e = ENOENT; + idx = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + idx++; + if (idx >= firstchange) { + updatetbl = 0; + } + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((bcmd = find_builtin(name))) { + goto builtin; + } + continue; + } else if (!(act & DO_NOFUN) && + prefix("func", pathopt)) { + /* handled below */ + } else { + continue; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && idx <= prev && + idx < firstchange) { + if (idx < prev) + continue; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { + 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)) + continue; + 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; + } + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + /* If we aren't called with DO_BRUTE and cmdp is set, it must + be a function and we're being called with DO_NOFUN */ + if (!updatetbl) { + entry->cmdtype = CMDNORMAL; + entry->u.index = idx; + return; + } + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = idx; + INTON; + goto success; } - return 1; + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp && updatetbl) + delete_cmd_entry(); + if (act & DO_ERR) + out2fmt("%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; } /* - * Put a string on the stack. + * Search the table of builtin commands. */ -static void -strtodest(p, syntax, quotes) - const char *p; - const char *syntax; - int quotes; +static int +bstrcmp(const void *name, const void *b) { - while (*p) { - if (quotes && syntax[(int) *p] == CCTL) - STPUTC(CTLESC, expdest); - STPUTC(*p++, expdest); + return strcmp((const char *)name, (*(const char *const *) b)+1); +} + +static struct builtincmd * +find_builtin(const char *name) +{ + struct builtincmd *bp; + + bp = bsearch(name, builtincmds, NUMBUILTINS, sizeof(struct builtincmd), + bstrcmp + ); + return bp; +} + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +static void +hashcd(void) { + 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; + } } } /* - * Add the value of a specialized variable to the stack string. + * 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. */ static void -varvalue(name, quoted, flags) - char *name; - int quoted; - int flags; +changepath(const char *newval) { - int num; - char *p; - int i; - int sep; - int sepq = 0; - char **ap; - char const *syntax; - int allow_split = flags & EXP_FULL; - int quotes = flags & (EXP_FULL | EXP_CASE); + int firstchange; + int bltin; - syntax = quoted ? DQSYNTAX : BASESYNTAX; - 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 && quoted) { - sep = 1 << CHAR_BIT; - goto param; - } - /* fall through */ - case '*': - sep = ifsset() ? ifsval()[0] : ' '; - if (quotes) { - sepq = syntax[(int) sep] == CCTL; - } -param: - for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { - strtodest(p, syntax, quotes); - if (*ap && sep) { - if (sepq) - STPUTC(CTLESC, expdest); - STPUTC(sep, expdest); - } - } - break; - case '0': - strtodest(arg0, syntax, quotes); - break; - default: - num = atoi(name); - if (num > 0 && num <= shellparam.nparam) { - strtodest(shellparam.p[num - 1], syntax, quotes); - } - break; - } + firstchange = path_change(newval, &bltin); + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + clearcmdentry(firstchange); + builtinloc = bltin; } - /* - * Record the fact that we have to scan this region of the - * string for IFS characters. + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. */ static void -recordregion(start, end, nulonly) - int start; - int end; - int nulonly; +clearcmdentry(firstchange) + int firstchange; { - struct ifsregion *ifsp; + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; - if (ifslastp == NULL) { - ifsp = &ifsfirst; - } else { - INTOFF; - ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); - ifsp->next = NULL; - ifslastp->next = ifsp; - INTON; + 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; + } + } } - ifslastp = ifsp; - ifslastp->begoff = start; - ifslastp->endoff = end; - ifslastp->nulonly = nulonly; + INTON; } - /* - * 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. + * Delete all functions. */ -static void -ifsbreakup(string, arglist) - char *string; - struct arglist *arglist; - { - struct ifsregion *ifsp; - struct strlist *sp; - char *start; - char *p; - char *q; - const char *ifs, *realifs; - int ifsspc; - int nulonly; +static void +deletefuncs(void) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; - start = string; - ifsspc = 0; - nulonly = 0; - realifs = ifsset() ? ifsval() : defifs; - if (ifslastp != NULL) { - ifsp = &ifsfirst; - do { - p = string + ifsp->begoff; - nulonly = ifsp->nulonly; - ifs = nulonly ? nullstr : realifs; - ifsspc = 0; - while (p < string + ifsp->endoff) { - q = p; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p)) { - if (!nulonly) - ifsspc = (strchr(defifs, *p) != NULL); - /* Ignore IFS whitespace at start */ - if (q == start && ifsspc) { - p++; - start = p; - continue; - } - *q = '\0'; - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; - p++; - if (!nulonly) { - for (;;) { - if (p >= string + ifsp->endoff) { - break; - } - q = p; - if (*p == CTLESC) - p++; - if (strchr(ifs, *p) == NULL ) { - p = q; - break; - } else if (strchr(defifs, *p) == NULL) { - if (ifsspc) { - p++; - ifsspc = 0; - } else { - p = q; - break; - } - } else - p++; - } - } - start = p; - } else - p++; + 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; } - } while ((ifsp = ifsp->next) != NULL); - if (!(*start || (!ifsspc && start > string && nulonly))) { - return; } } - - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = start; - *arglist->lastp = sp; - arglist->lastp = &sp->next; -} - -static void -ifsfree() -{ - while (ifsfirst.next != NULL) { - struct ifsregion *ifsp; - INTOFF; - ifsp = ifsfirst.next->next; - ckfree(ifsfirst.next); - ifsfirst.next = ifsp; - INTON; - } - ifslastp = NULL; - ifsfirst.next = NULL; + INTON; } /* - * Expand shell metacharacters. At this point, the only control characters - * should be escapes. The results are stored in the list exparg. + * 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. */ -#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) -static void -expandmeta(str, flag) - struct strlist *str; - int flag; +static struct tblentry **lastcmdentry; + +static struct tblentry * +cmdlookup(const char *name, int add) { + int hashval; const char *p; - glob_t pglob; - /* TODO - EXP_REDIR */ + struct tblentry *cmdp; + struct tblentry **pp; - while (str) { - if (fflag) - goto nometa; - p = preglob(str->text); - INTOFF; - switch (glob(p, GLOB_NOMAGIC, 0, &pglob)) { - case 0: - if (!(pglob.gl_flags & GLOB_MAGCHAR)) - goto nometa2; - addglob(&pglob); - globfree(&pglob); - INTON; - break; - case GLOB_NOMATCH: -nometa2: - globfree(&pglob); - INTON; -nometa: - *exparg.lastp = str; - rmescapes(str->text); - exparg.lastp = &str->next; + 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; - default: /* GLOB_NOSPACE */ - error("Out of space"); - } - str = str->next; + 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; } - /* - * Add the result of glob(3) to the list. + * Delete the command entry returned on the last lookup. */ static void -addglob(pglob) - const glob_t *pglob; -{ - char **p = pglob->gl_pathv; +delete_cmd_entry() { + struct tblentry *cmdp; - do { - addfname(*p); - } while (*++p); + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; } -#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ -static 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 */ - } +static const short nodesize[26] = { + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct ncmd)), + ALIGN(sizeof (struct npipe)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nredir)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nif)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nbinary)), + ALIGN(sizeof (struct nfor)), + ALIGN(sizeof (struct ncase)), + ALIGN(sizeof (struct nclist)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct narg)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct nfile)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct ndup)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nhere)), + ALIGN(sizeof (struct nnot)), +}; - 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. + * Delete a function if it exists. */ static void -expmeta(enddir, name) - char *enddir; - char *name; - { - char *p; - const char *cp; - char *q; - char *start; - char *endname; - int metaflag; - struct stat statb; - DIR *dirp; - struct dirent *dp; - int atend; - int matchdot; +unsetfunc(char *name) +{ + struct tblentry *cmdp; - metaflag = 0; - start = name; - for (p = name ; ; p++) { - if (*p == '*' || *p == '?') - metaflag = 1; - else if (*p == '[') { - q = p + 1; - if (*q == '!') - q++; - for (;;) { - while (*q == CTLQUOTEMARK) - q++; - 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 == CTLQUOTEMARK) - continue; - 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 == CTLQUOTEMARK) - continue; - if (*p == CTLESC) - p++; - *enddir++ = *p; - if (*p == '\0') - break; - } - if (metaflag == 0 || lstat(expdir, &statb) >= 0) - addfname(expdir); - return; - } - endname = p; - if (start != name) { - p = name; - while (p < start) { - while (*p == CTLQUOTEMARK) - p++; - if (*p == CTLESC) - p++; - *enddir++ = *p++; - } - } - if (enddir == expdir) { - cp = "."; - } else if (enddir == expdir + 1 && *expdir == '/') { - cp = "/"; - } else { - cp = expdir; - enddir[-1] = '\0'; - } - if ((dirp = opendir(cp)) == NULL) - return; - if (enddir != expdir) - enddir[-1] = '/'; - if (*endname == 0) { - atend = 1; - } else { - atend = 0; - *endname++ = '\0'; - } - matchdot = 0; - p = start; - while (*p == CTLQUOTEMARK) - p++; - if (*p == CTLESC) - p++; - if (*p == '.') - matchdot++; - while (! int_pending() && (dp = readdir(dirp)) != NULL) { - if (dp->d_name[0] == '.' && ! matchdot) - continue; - if (patmatch(start, dp->d_name, 0)) { - if (atend) { - scopy(dp->d_name, enddir); - addfname(expdir); - } else { - for (p = enddir, cp = dp->d_name; - (*p++ = *cp++) != '\0';) - continue; - p[-1] = '/'; - expmeta(p, endname); - } - } + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); } - closedir(dirp); - if (! atend) - endname[-1] = '/'; } -#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ /* - * Add a file name to the list. + * Locate and print what a word is... */ -static void -addfname(name) - char *name; - { - char *p; - struct strlist *sp; - - p = sstrdup(name); - sp = (struct strlist *)stalloc(sizeof *sp); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; -} - - -#if !(defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) -/* - * 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 int +typecmd(int argc, char **argv) +{ + int i; + int err = 0; + char *argv_a[2]; -static struct strlist * -expsort(str) - struct strlist *str; - { - int len; - struct strlist *sp; + argv_a[1] = 0; - len = 0; - for (sp = str ; sp ; sp = sp->next) - len++; - return msort(str, len); + for (i = 1; i < argc; i++) { + argv_a[0] = argv[i]; + argptr = argv_a; + optptr = "v"; + err |= hashcmd(argc, argv); + } + return err; } - -static struct strlist * -msort(list, len) - struct strlist *list; - int len; +#ifdef ASH_CMDCMD +static int +commandcmd(argc, argv) + int argc; + char **argv; { - struct strlist *p, *q = NULL; - struct strlist **lpp; - int half; - int n; + int c; + int default_path = 0; + int verify_only = 0; + int verbose_verify_only = 0; - 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; - } + while ((c = nextopt("pvV")) != '\0') + switch (c) { + case 'p': + default_path = 1; + break; + case 'v': + verify_only = 1; + break; + case 'V': + verbose_verify_only = 1; + break; } - } - return list; -} -#endif - - - -/* - * Returns true if the pattern matches the string. - */ - -#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) -static int -patmatch(pattern, string, squoted) - char *pattern; - char *string; - int squoted; /* string might have quote chars */ - { - const char *p; - char *q; - - p = preglob(pattern); - q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string; - return !fnmatch(p, q, 0); -} + if (default_path + verify_only + verbose_verify_only > 1 || + !*argptr) { + out2str( + "command [-p] command [arg ...]\n" + "command {-v|-V} command\n"); + return EX_USAGE; + } + if (verify_only || verbose_verify_only) { + char *argv_a[2]; -static int -patmatch2(pattern, string, squoted) - char *pattern; - char *string; - int squoted; /* string might have quote chars */ - { - char *p; - int res; + argv_a[1] = 0; + argv_a[0] = *argptr; + argptr = argv_a; + optptr = verbose_verify_only ? "v" : "V"; /* reverse special */ + return hashcmd(argc, argv); + } - sstrnleft--; - p = grabstackstr(expdest); - res = patmatch(pattern, string, squoted); - ungrabstackstr(p, expdest); - return res; + return 0; } -#else -static int -patmatch(pattern, string, squoted) - char *pattern; - char *string; - int squoted; /* string might have quote chars */ - { -#ifdef notdef - if (pattern[0] == '!' && pattern[1] == '!') - return 1 - pmatch(pattern + 2, string); - else #endif - return pmatch(pattern, string, squoted); -} - static int -pmatch(pattern, string, squoted) - char *pattern; - char *string; - int squoted; - { - char *p, *q; - char c; +path_change(newval, bltin) + const char *newval; + int *bltin; +{ + const char *old, *new; + int idx; + int firstchange; - p = pattern; - q = string; + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + idx = 0; + *bltin = -1; for (;;) { - switch (c = *p++) { - case '\0': - goto breakloop; - case CTLESC: - if (squoted && *q == CTLESC) - q++; - if (*q++ != *p++) - return 0; - break; - case CTLQUOTEMARK: - continue; - case '?': - if (squoted && *q == CTLESC) - q++; - if (*q++ == '\0') - return 0; - break; - case '*': - c = *p; - while (c == CTLQUOTEMARK || c == '*') - c = *++p; - if (c != CTLESC && c != CTLQUOTEMARK && - c != '?' && c != '*' && c != '[') { - while (*q != c) { - if (squoted && *q == CTLESC && - q[1] == c) - break; - if (*q == '\0') - return 0; - if (squoted && *q == CTLESC) - q++; - q++; - } - } - do { - if (pmatch(p, q, squoted)) - return 1; - if (squoted && *q == CTLESC) - q++; - } while (*q++ != '\0'); - return 0; - case '[': { - char *endp; - int invert, found; - char chr; - - endp = p; - if (*endp == '!') - endp++; - for (;;) { - while (*endp == CTLQUOTEMARK) - endp++; - 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 (squoted && chr == CTLESC) - chr = *q++; - if (chr == '\0') - return 0; - c = *p++; - do { - if (c == CTLQUOTEMARK) - continue; - if (c == CTLESC) - c = *p++; - if (*p == '-' && p[1] != ']') { - p++; - while (*p == CTLQUOTEMARK) - 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; + if (*old != *new) { + firstchange = idx; + if ((*old == '\0' && *new == ':') + || (*old == ':' && *new == '\0')) + firstchange++; + old = new; /* ignore subsequent differences */ } -dft: default: - if (squoted && *q == CTLESC) - q++; - if (*q++ != c) - return 0; + if (*new == '\0') break; + if (*new == '%' && *bltin < 0 && prefix("builtin", new + 1)) + *bltin = idx; + if (*new == ':') { + idx++; } + new++, old++; } -breakloop: - if (*q != '\0') - return 0; - return 1; + if (builtinloc >= 0 && *bltin < 0) + firstchange = 0; + return firstchange; } +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ +/* + * _rmescape() flags + */ +#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ +#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ + +/* + * 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 */ +}; + + +static char *expdest; /* output of current string */ +static struct nodelist *argbackq; /* list of back quote expressions */ +static struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +static struct ifsregion *ifslastp; /* last struct in list */ +static struct arglist exparg; /* holds expanded arg list */ + +static void argstr (char *, int); +static char *exptilde (char *, int); +static void expbackq (union node *, int, int); +static int subevalvar (char *, char *, int, int, int, int, int); +static int varisset (char *, int); +static void strtodest (const char *, const char *, int); +static void varvalue (char *, int, int); +static void recordregion (int, int, int); +static void removerecordregions (int); +static void ifsbreakup (char *, struct arglist *); +static void ifsfree (void); +static void expandmeta (struct strlist *, int); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +#define preglob(p) _rmescapes((p), RMESCAPE_ALLOC | RMESCAPE_GLOB) +#if !defined(GLOB_BROKEN) +static void addglob (const glob_t *); +#endif +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static void expmeta (char *, char *); +#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +static struct strlist *expsort (struct strlist *); +static struct strlist *msort (struct strlist *, int); +#endif +static int patmatch (char *, char *, int); +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static int patmatch2 (char *, char *, int); +#else +static int pmatch (char *, char *, int); +#define patmatch2 patmatch #endif +static char *cvtnum (int, char *); + +/* + * Expand shell variables and backquotes inside a here document. + */ +/* arg: the document, fd: where to write the expanded version */ +static inline void +expandhere(union node *arg, int fd) +{ + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} /* - * Remove any CTLESC characters from a string. + * 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. */ -#if defined(__GLIBC__) && !defined(FNMATCH_BROKEN) -static char * -_rmescapes(str, flag) - char *str; +static void +expandarg(arg, arglist, flag) + union node *arg; + struct arglist *arglist; int flag; { - char *p, *q, *r; - static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; - - p = strpbrk(str, qchars); - if (!p) { - return str; - } - q = p; - r = str; - if (flag & RMESCAPE_ALLOC) { - size_t len = p - str; - q = r = stalloc(strlen(p) + len + 1); - if (len > 0) { -#ifdef _GNU_SOURCE - q = mempcpy(q, str, len); -#else - memcpy(q, str, len); - q += len; -#endif - } - } - while (*p) { - if (*p == CTLQUOTEMARK) { - p++; - continue; - } - if (*p == CTLESC) { - p++; - if (flag & RMESCAPE_GLOB && *p != '/') { - *q++ = '\\'; - } - } - *q++ = *p++; - } - *q = '\0'; - return r; -} -#else -static void -rmescapes(str) - char *str; -{ - char *p, *q; - - p = str; - while (*p != CTLESC && *p != CTLQUOTEMARK) { - if (*p++ == '\0') - return; - } - q = p; - while (*p) { - if (*p == CTLQUOTEMARK) { - p++; - continue; - } - if (*p == CTLESC) - p++; - *q++ = *p++; - } - *q = '\0'; -} -#endif - - - -/* - * See if a pattern matches in a case statement. - */ - -static int -casematch(pattern, val) - union node *pattern; - char *val; - { - struct stackmark smark; - int result; + struct strlist *sp; char *p; - setstackmark(&smark); - argbackq = pattern->narg.backquote; + argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); + ifsfirst.next = NULL; ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); + argstr(arg->narg.text, flag); + if (arglist == NULL) { + return; /* here document expanded */ + } STPUTC('\0', expdest); p = grabstackstr(expdest); - result = patmatch(p, val, 0); - popstackmark(&smark); - return result; -} - -/* - * Our own itoa(). - */ - -static char * -cvtnum(num, buf) - int num; - char *buf; - { - int len; - - CHECKSTRSPACE(32, buf); - len = sprintf(buf, "%d", num); - STADJUST(len, buf); - return buf; -} -/* $NetBSD: histedit.c,v 1.25 2001/02/04 19:52:06 christos Exp $ */ - -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * Editline and history functions (and glue). - */ -static int histcmd(argc, argv) - int argc; - char **argv; -{ - error("not compiled with history support"); - /* NOTREACHED */ + 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; + } + ifsfree(); + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } } /* - * This file was generated by the mkinit program. + * Expand a variable, and return a pointer to the next character in the + * input string. */ -extern void rmaliases __P((void)); +static inline char * +evalvar(p, flag) + char *p; + int flag; +{ + int subtype; + int varflags; + char *var; + const char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int easy; + int quotes = flag & (EXP_FULL | EXP_CASE); -extern int loopnest; /* current loop nesting level */ + 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, varflags & VSNUL); + 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) { + varvalue(var, varflags & VSQUOTE, flag); + if (subtype == VSLENGTH) { + varlen = expdest - stackblock() - startloc; + STADJUST(-varlen, expdest); + } + } else { + if (subtype == VSLENGTH) { + varlen = strlen(val); + } else { + strtodest( + val, + varflags & VSQUOTE ? + DQSYNTAX : BASESYNTAX, + quotes + ); + } + } + } -extern void deletefuncs __P((void)); + if (subtype == VSPLUS) + set = ! set; -struct strpush { - struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; - struct alias *ap; /* if push was associated with an alias */ - char *string; /* remember the string since it may change */ -}; + easy = ((varflags & VSQUOTE) == 0 || + (*var == '@' && shellparam.nparam != 1)); -struct parsefile { - struct parsefile *prev; /* preceding file on stack */ - int linno; /* current line */ - int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ - char *buf; /* input buffer */ - struct strpush *strpush; /* for pushing strings at this level */ - struct strpush basestrpush; /* so pushing one is fast */ -}; -extern int parselleft; /* copy of parsefile->lleft */ -extern struct parsefile basepf; /* top level input file */ -extern char basebuf[BUFSIZ]; /* buffer for top level input file */ + switch (subtype) { + case VSLENGTH: + expdest = cvtnum(varlen, expdest); + goto record; -extern short backgndpid; /* pid of last background process */ -extern int jobctl; + case VSNORMAL: + if (!easy) + break; +record: + recordregion(startloc, expdest - stackblock(), + varflags & VSQUOTE); + break; -extern int tokpushback; /* last token pushed back */ -extern int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ + case VSPLUS: + case VSMINUS: + if (!set) { + argstr(p, flag); + break; + } + if (easy) + goto record; + break; -struct redirtab { - struct redirtab *next; - short renamed[10]; -}; - -extern struct redirtab *redirlist; + 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); + patloc = expdest - stackblock(); + if (subevalvar(p, NULL, patloc, subtype, + startloc, varflags, quotes) == 0) { + int amount = (expdest - stackblock() - patloc) + 1; + STADJUST(-amount, expdest); + } + /* Remove any recorded regions beyond start of variable */ + removerecordregions(startloc); + goto record; -extern char sigmode[NSIG - 1]; /* current value of signal */ + case VSASSIGN: + case VSQUESTION: + if (!set) { + if (subevalvar(p, var, 0, subtype, startloc, + varflags, quotes)) { + varflags &= ~VSNUL; + /* + * Remove any recorded regions beyond + * start of variable + */ + removerecordregions(startloc); + goto again; + } + break; + } + if (easy) + goto record; + break; -extern char **environ; +#ifdef DEBUG + default: + abort(); +#endif + } + 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; +} /* - * Initialization code. + * 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 -init() { +argstr(p, flag) + char *p; + int flag; +{ + char c; + int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ + int firsteq = 1; - /* from cd.c: */ - { - setpwd(0, 0); - } + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: /* ??? */ + goto breakloop; + case CTLQUOTEMARK: + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_FULL) != 0) + STPUTC(c, expdest); + break; + 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; +#ifdef ASH_MATH_SUPPORT + case CTLENDARI: + expari(flag); + break; +#endif + 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:; + return; +} - /* from input.c: */ - { - basepf.nextc = basepf.buf = basebuf; - } +static char * +exptilde(p, flag) + char *p; + int flag; +{ + char c, *startp = p; + struct passwd *pw; + const char *home; + int quotes = flag & (EXP_FULL | EXP_CASE); - /* from output.c: */ - { -#ifdef USE_GLIBC_STDIO - initstreams(); -#endif - } + while ((c = *p) != '\0') { + switch(c) { + case CTLESC: + return (startp); + case CTLQUOTEMARK: + 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; + strtodest(home, SQSYNTAX, quotes); + return (p); +lose: + *p = c; + return (startp); +} - /* from var.c: */ - { - char **envp; - char ppid[32]; - initvar(); - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } +static void +removerecordregions(int endoff) +{ + if (ifslastp == NULL) + return; - fmtstr(ppid, sizeof(ppid), "%d", (int) getppid()); - setvar("PPID", ppid, 0); - } -} + if (ifsfirst.endoff > endoff) { + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + if (ifsfirst.begoff > endoff) + ifslastp = NULL; + else { + ifslastp = &ifsfirst; + ifsfirst.endoff = endoff; + } + return; + } + ifslastp = &ifsfirst; + while (ifslastp->next && ifslastp->next->begoff < endoff) + ifslastp=ifslastp->next; + while (ifslastp->next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifslastp->next->next; + ckfree(ifslastp->next); + ifslastp->next = ifsp; + INTON; + } + if (ifslastp->endoff > endoff) + ifslastp->endoff = endoff; +} +#ifdef ASH_MATH_SUPPORT /* - * This routine is called when an error or an interrupt occurs in an - * interactive shell and control is returned to the main command loop. + * Expand arithmetic expression. Backup to start of expression, + * evaluate, place result in (backed up) result, adjust string position. */ - static void -reset() { - - /* from eval.c: */ - { - evalskip = 0; - loopnest = 0; - funcnest = 0; - } +expari(int flag) +{ + char *p, *start; + int result; + int begoff; + int quotes = flag & (EXP_FULL | EXP_CASE); + int quoted; - /* from input.c: */ - { - if (exception != EXSHELLPROC) - parselleft = parsenleft = 0; /* clear input buffer */ - popallfiles(); - } + /* ifsfree(); */ - /* from parser.c: */ - { - tokpushback = 0; - checkkwd = 0; - checkalias = 0; - } - - /* from redir.c: */ - { - while (redirlist) - popredir(); - } + /* + * This routine is slightly over-complicated 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(10, expdest); + USTPUTC('\0', expdest); + start = stackblock(); + p = expdest - 1; + 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++; - /* from output.c: */ - { - out1 = &output; - out2 = &errout; -#ifdef USE_GLIBC_STDIO - if (memout.stream != NULL) - __closememout(); -#endif - if (memout.buf != NULL) { - ckfree(memout.buf); - memout.buf = NULL; - } - } -} + if (p[1] == '"') + quoted=1; + else + quoted=0; + begoff = p - start; + removerecordregions(begoff); + if (quotes) + rmescapes(p+2); + result = arith(p+2); + snprintf(p, 12, "%d", result); + while (*p++) + ; + if (quoted == 0) + recordregion(begoff, p - 1 - start, 0); + result = expdest - p + 1; + STADJUST(-result, expdest); +} +#endif /* - * This routine is called to initialize the shell to run a shell procedure. + * Expand stuff in backwards quotes. */ static void -initshellproc() { +expbackq(cmd, quoted, flag) + union node *cmd; + int quoted; + int flag; +{ + volatile struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + volatile struct ifsregion saveifs; + struct ifsregion *volatile savelastp; + struct nodelist *volatile saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + volatile int saveherefd; + int quotes = flag & (EXP_FULL | EXP_CASE); + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int ex; - /* from alias.c: */ - { - rmaliases(); - } +#if __GNUC__ + /* Avoid longjmp clobbering */ + (void) &dest; + (void) &syntax; +#endif - /* from eval.c: */ - { - exitstatus = 0; - } + in.fd = -1; + in.buf = 0; + in.jp = 0; - /* from exec.c: */ - { - deletefuncs(); - } + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + if ((ex = setjmp(jmploc.loc))) { + goto err1; + } + savehandler = handler; + handler = &jmploc; + INTON; + p = grabstackstr(dest); + evalbackcmd(cmd, (struct backcmd *) &in); + ungrabstackstr(p, dest); +err1: + INTOFF; + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + if (ex) { + goto err2; + } - /* from jobs.c: */ - { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif - } + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + i = safe_read(in.fd, buf, sizeof buf); + 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[(int)lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } - /* from options.c: */ - { - int i; + /* Eat all trailing newlines */ + for (; dest > stackblock() && dest[-1] == '\n';) + STUNPUTC(dest); - for (i = 0; i < NOPTS; i++) - optlist[i].val = 0; - optschanged(); +err2: + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + handler = savehandler; + if (ex) { + longjmp(handler->loc, 1); + } + 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, strloc, subtype, startloc, varflags, quotes) + char *p; + char *str; + int strloc; + int subtype; + int startloc; + int varflags; + int quotes; +{ + char *startp; + char *loc = NULL; + char *q; + int c = 0; + int saveherefd = herefd; + struct nodelist *saveargbackq = argbackq; + int amount; - /* from redir.c: */ - { - clearredir(); - } + herefd = -1; + argstr(p, subtype != VSASSIGN && subtype != VSQUESTION ? EXP_CASE : 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + argbackq = saveargbackq; + startp = stackblock() + startloc; + if (str == NULL) + str = stackblock() + strloc; - /* from trap.c: */ - { - char *sm; + switch (subtype) { + case VSASSIGN: + setvar(str, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + varflags &= ~VSNUL; + if (c != 0) + *loc = c; + return 1; - clear_traps(); - for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) { - if (*sm == S_IGN) - *sm = S_HARD_IGN; - } - } + case VSQUESTION: + if (*p != CTLENDVAR) { + out2fmt(snlfmt, startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - str - 1, + str, (varflags & VSNUL) ? "null or " + : nullstr); + /* NOTREACHED */ - /* from var.c: */ - { - shprocvar(); - } -} -/* $NetBSD: input.c,v 1.35 2001/02/04 19:52:06 christos Exp $ */ + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * This file implements the input routines used by the parser. - */ - -#ifdef BB_FEATURE_COMMAND_EDITING -unsigned int shell_context; -static const char * cmdedit_prompt; -static inline void putprompt(const char *s) { - cmdedit_prompt = s; -} -#else -static inline void putprompt(const char *s) { - out2str(s); -} -#endif - -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch2(str, startp, quotes)) + goto recordleft; + *loc = c; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; -static int plinno = 1; /* input line number */ -static int parsenleft; /* copy of parsefile->nleft */ -static int parselleft; /* copy of parsefile->lleft */ -static char *parsenextc; /* copy of parsefile->nextc */ -struct parsefile basepf; /* top level input file */ -static char basebuf[BUFSIZ]; /* buffer for top level input file */ -struct parsefile *parsefile = &basepf; /* current input file */ -static int whichprompt; /* 1 == PS1, 2 == PS2 */ + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch2(str, loc, quotes)) + goto recordright; + loc--; + if (quotes && loc > startp && *(loc - 1) == CTLESC) { + for (q = startp; q < loc; q++) + if (*q == CTLESC) + q++; + if (q > loc) + loc--; + } + } + return 0; -static void pushfile __P((void)); -static int preadfd __P((void)); + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch2(str, loc, quotes)) + goto recordright; + if (quotes && *loc == CTLESC) + loc++; + } + return 0; -#ifdef mkinit -INCLUDE -INCLUDE "input.h" -INCLUDE "error.h" +#ifdef DEBUG + default: + abort(); +#endif + } -INIT { - basepf.nextc = basepf.buf = basebuf; -} +recordleft: + *loc = c; + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; + return 1; -RESET { - if (exception != EXSHELLPROC) - parselleft = parsenleft = 0; /* clear input buffer */ - popallfiles(); +recordright: + amount = loc - expdest; + STADJUST(amount, expdest); + STPUTC('\0', expdest); + STADJUST(-1, expdest); + return 1; } -#endif /* - * Read a line from the script. + * Test whether a specialized variable is set. */ -static char * -pfgets(line, len) - char *line; - int len; +static int +varisset(name, nulok) + char *name; + int nulok; { - char *p = line; - int nleft = len; - int c; + if (*name == '!') + return backgndpid != -1; + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; - while (--nleft > 0) { - c = pgetc2(); - if (c == PEOF) { - if (p == line) - return NULL; - break; + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; } - *p++ = c; - if (c == '\n') - break; - } - *p = '\0'; - return line; -} + } else if (is_digit(*name)) { + char *ap; + int num = atoi(name); + if (num > shellparam.nparam) + return 0; -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; -static int -pgetc() -{ - return pgetc_macro(); + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; } - /* - * Same as pgetc(), but ignores PEOA. + * Put a string on the stack. */ -static int -pgetc2() -{ - int c; - do { - c = pgetc_macro(); - } while (c == PEOA); - return c; -} - - -static int -preadfd() +static void +strtodest(p, syntax, quotes) + const char *p; + const char *syntax; + int quotes; { - int nr; - char *buf = parsefile->buf; - parsenextc = buf; - -retry: -#ifdef BB_FEATURE_COMMAND_EDITING - { - if (parsefile->fd) - nr = read(parsefile->fd, buf, BUFSIZ - 1); - else { - do { - cmdedit_read_input((char*)cmdedit_prompt, buf); - nr = strlen(buf); - } while (nr <=0 || shell_context); - cmdedit_terminate(); - } - } -#else - nr = read(parsefile->fd, buf, BUFSIZ - 1); -#endif - - if (nr < 0) { - if (errno == EINTR) - goto retry; - if (parsefile->fd == 0 && errno == EWOULDBLOCK) { - int flags = fcntl(0, F_GETFL, 0); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; - if (fcntl(0, F_SETFL, flags) >= 0) { - out2str("sh: turning off NDELAY mode\n"); - goto retry; - } - } - } + while (*p) { + if (quotes && syntax[(int) *p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); } - return nr; } /* - * Refill the input buffer and return the next input character: - * - * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading - * from a string so we can't refill the buffer, return EOF. - * 3) If the is more stuff in this buffer, use it else call read to fill it. - * 4) Process input up to the next newline, deleting nul characters. + * Add the value of a specialized variable to the stack string. */ -static int -preadbuffer() +static void +varvalue(name, quoted, flags) + char *name; + int quoted; + int flags; { - char *p, *q; - int more; - char savec; + int num; + char *p; + int i; + int sep; + int sepq = 0; + char **ap; + char const *syntax; + int allow_split = flags & EXP_FULL; + int quotes = flags & (EXP_FULL | EXP_CASE); - while (parsefile->strpush) { - if ( - parsenleft == -1 && parsefile->strpush->ap && - parsenextc[-1] != ' ' && parsenextc[-1] != '\t' - ) { - return PEOA; + syntax = quoted ? DQSYNTAX : BASESYNTAX; + 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 (optent_val(i)) + STPUTC(optent_letter(optlist[i]), expdest); } - popstring(); - if (--parsenleft >= 0) - return (*parsenextc++); - } - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) - return PEOF; - flushout(&output); -#ifdef FLUSHERR - flushout(&errout); -#endif - -again: - if (parselleft <= 0) { - if ((parselleft = preadfd()) <= 0) { - parselleft = parsenleft = EOF_NLEFT; - return PEOF; + break; + case '@': + if (allow_split && quoted) { + sep = 1 << CHAR_BIT; + goto param; } - } - - q = p = parsenextc; - - /* delete nul characters */ - for (more = 1; more;) { - switch (*p) { - case '\0': - p++; /* Skip nul */ - goto check; - - - case '\n': - parsenleft = q - parsenextc; - more = 0; /* Stop processing here */ - break; + /* fall through */ + case '*': + sep = ifsset() ? ifsval()[0] : ' '; + if (quotes) { + sepq = syntax[(int) sep] == CCTL; } - - *q++ = *p++; -check: - if (--parselleft <= 0 && more) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) - goto again; - more = 0; +param: + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + strtodest(p, syntax, quotes); + if (*ap && sep) { + if (sepq) + STPUTC(CTLESC, expdest); + STPUTC(sep, expdest); + } } + break; + case '0': + strtodest(arg0, syntax, quotes); + break; + default: + num = atoi(name); + if (num > 0 && num <= shellparam.nparam) { + strtodest(shellparam.p[num - 1], syntax, quotes); + } + break; } - - savec = *q; - *q = '\0'; - - if (vflag) { - out2str(parsenextc); -#ifdef FLUSHERR - flushout(out2); -#endif - } - - *q = savec; - - return *parsenextc++; } + /* - * Undo the last call to pgetc. Only one character may be pushed back. - * PEOF may be pushed back. + * Record the fact that we have to scan this region of the + * string for IFS characters. */ static void -pungetc() { - parsenleft++; - parsenextc--; +recordregion(start, end, nulonly) + int start; + int end; + int nulonly; +{ + struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + INTOFF; + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifsp->next = NULL; + ifslastp->next = ifsp; + INTON; + } + ifslastp = ifsp; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; } + + /* - * Push a string back onto the input at this current parsefile level. - * We handle aliases this way. + * 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 -pushstring(s, len, ap) - char *s; - int len; - void *ap; +ifsbreakup(string, arglist) + char *string; + struct arglist *arglist; { - 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; - sp->string = s; - } - parsenextc = s; - parsenleft = len; - INTON; -} + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + char *p; + char *q; + const char *ifs, *realifs; + int ifsspc; + int nulonly; -static void -popstring() -{ - struct strpush *sp = parsefile->strpush; - INTOFF; - if (sp->ap) { - if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { - if (!checkalias) { - checkalias = 1; + start = string; + ifsspc = 0; + nulonly = 0; + realifs = ifsset() ? ifsval() : defifs; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + nulonly = ifsp->nulonly; + ifs = nulonly ? nullstr : realifs; + ifsspc = 0; + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p)) { + if (!nulonly) + ifsspc = (strchr(defifs, *p) != NULL); + /* Ignore IFS whitespace at start */ + if (q == start && ifsspc) { + p++; + start = p; + continue; + } + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + p++; + if (!nulonly) { + for (;;) { + if (p >= string + ifsp->endoff) { + break; + } + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p) == NULL ) { + p = q; + break; + } else if (strchr(defifs, *p) == NULL) { + if (ifsspc) { + p++; + ifsspc = 0; + } else { + p = q; + break; + } + } else + p++; + } + } + start = p; + } else + p++; } - } - if (sp->string != sp->ap->val) { - ckfree(sp->string); - } - sp->ap->flag &= ~ALIASINUSE; - if (sp->ap->flag & ALIASDEAD) { - unalias(sp->ap->name); + } while ((ifsp = ifsp->next) != NULL); + if (!(*start || (!ifsspc && start > string && nulonly))) { + return; } } - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; -/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ - parsefile->strpush = sp->prev; - if (sp != &(parsefile->basestrpush)) - ckfree(sp); - INTON; -} -/* - * Set the input to take input from a file. If push is set, push the - * old input onto the stack first. - */ + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; +} static void -setinputfile(fname, push) - const char *fname; - int push; +ifsfree() { - int fd; - int myfileno2; - - INTOFF; - if ((fd = open(fname, O_RDONLY)) < 0) - error("Can't open %s", fname); - if (fd < 10) { - myfileno2 = dup_as_newfd(fd, 10); - close(fd); - if (myfileno2 < 0) - error("Out of file descriptors"); - fd = myfileno2; + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; } - setinputfd(fd, push); - INTON; + ifslastp = NULL; + ifsfirst.next = NULL; } - /* - * Like setinputfile, but takes an open file descriptor. Call this with - * interrupts off. + * Add a file name to the list. */ static void -setinputfd(fd, push) - int fd, push; +addfname(const char *name) { - (void) fcntl(fd, F_SETFD, FD_CLOEXEC); - if (push) { - pushfile(); - parsefile->buf = 0; - } else { - closescript(); - while (parsefile->strpush) - popstring(); - } - parsefile->fd = fd; - if (parsefile->buf == NULL) - parsefile->buf = ckmalloc(BUFSIZ); - parselleft = parsenleft = 0; - plinno = 1; -} + char *p; + struct strlist *sp; + p = sstrdup(name); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} /* - * Like setinputfile, but takes input from a string. + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. */ +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) static void -setinputstring(string) - char *string; - { - INTOFF; - pushfile(); - parsenextc = string; - parsenleft = strlen(string); - parsefile->buf = NULL; - plinno = 1; - INTON; -} +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + const char *p; + glob_t pglob; + /* TODO - EXP_REDIR */ + while (str) { + if (fflag) + goto nometa; + p = preglob(str->text); + INTOFF; + switch (glob(p, GLOB_NOMAGIC, 0, &pglob)) { + case 0: + if (!(pglob.gl_flags & GLOB_MAGCHAR)) + goto nometa2; + addglob(&pglob); + globfree(&pglob); + INTON; + break; + case GLOB_NOMATCH: +nometa2: + globfree(&pglob); + INTON; +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + break; + default: /* GLOB_NOSPACE */ + error("Out of space"); + } + str = str->next; + } +} /* - * 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. + * Add the result of glob(3) to the list. */ static void -pushfile() { - struct parsefile *pf; +addglob(pglob) + const glob_t *pglob; +{ + char **p = pglob->gl_pathv; - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; - pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); - pf->prev = parsefile; - pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; - parsefile = pf; + do { + addfname(*p); + } while (*++p); } -static 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; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - plinno = parsefile->linno; - INTON; -} - +#else /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ +static char *expdir; -/* - * Return to top level. - */ static void -popallfiles() { - while (parsefile != &basepf) - popfile(); -} - - +expandmeta(str, flag) + struct strlist *str; + int flag; +{ + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + /* TODO - EXP_REDIR */ -/* - * Close the file(s) that the shell is reading commands from. Called - * after a fork is done. - */ + 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 */ + } -static void -closescript() { - popallfiles(); - if (parsefile->fd > 0) { - close(parsefile->fd); - parsefile->fd = 0; + 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; } } -/* $NetBSD: jobs.c,v 1.36 2000/05/22 10:18:47 elric Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -struct job *jobtab; /* array of jobs */ -static int njobs; /* size of array */ -short backgndpid = -1; /* pid of last background process */ -#if JOBS -static int initialpgrp; /* pgrp of shell on invocation */ -short curjob; /* current job */ -#endif -static int intreceived; - -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 *)); -#ifdef SYSV -static int onsigchild __P((void)); -#endif -static int waitproc __P((int, int *)); -static void cmdtxt __P((union node *)); -static void cmdputs __P((const char *)); -static void waitonint(int); -#if JOBS /* - * 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. + * Do metacharacter (i.e. *, ?, [...]) expansion. */ -static int jobctl; - -static void setjobctl(int enable) -{ -#ifdef OLD_TTY_DRIVER - int ldisc; -#endif +static void +expmeta(enddir, name) + char *enddir; + char *name; + { + char *p; + const char *cp; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; - if (enable == jobctl || rootshell == 0) - return; - if (enable) { - do { /* while we are in the background */ -#ifdef OLD_TTY_DRIVER - if (ioctl(fileno2, TIOCGPGRP, (char *)&initialpgrp) < 0) { -#else - initialpgrp = tcgetpgrp(fileno2); - if (initialpgrp < 0) { -#endif - out2str("sh: can't access tty; job cenabletrol 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(fileno2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { - out2str("sh: need new tty driver to run job cenabletrol; job cenabletrol turned off\n"); - mflag = 0; - return; + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + while (*q == CTLQUOTEMARK) + q++; + 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 == CTLQUOTEMARK) + continue; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; } -#endif - setsignal(SIGTSTP); - setsignal(SIGTTOU); - setsignal(SIGTTIN); - setpgid(0, rootpid); -#ifdef OLD_TTY_DRIVER - ioctl(fileno2, TIOCSPGRP, (char *)&rootpid); -#else - tcsetpgrp(fileno2, rootpid); -#endif - } else { /* turning job cenabletrol off */ - setpgid(0, initialpgrp); -#ifdef OLD_TTY_DRIVER - ioctl(fileno2, TIOCSPGRP, (char *)&initialpgrp); -#else - tcsetpgrp(fileno2, initialpgrp); -#endif - setsignal(SIGTSTP); - setsignal(SIGTTOU); - setsignal(SIGTTIN); } - jobctl = enable; + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLQUOTEMARK) + continue; + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + cp = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + cp = "/"; + } else { + cp = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(cp)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + p = start; + while (*p == CTLQUOTEMARK) + p++; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name, 0)) { + if (atend) { + strcpy(enddir, dp->d_name); + addfname(expdir); + } else { + for (p = enddir, cp = dp->d_name; + (*p++ = *cp++) != '\0';) + continue; + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; } -#endif +#endif /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ -#ifdef mkinit -INCLUDE -SHELLPROC { - backgndpid = -1; -#if JOBS - jobctl = 0; -#endif +#if !(defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN)) +/* + * 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); } -#endif +static struct strlist * +msort(list, len) + struct strlist *list; + int len; +{ + struct strlist *p, *q = NULL; + struct strlist **lpp; + int half; + int n; -/* This file was automatically created by ./mksignames. - Do not edit. Edit support/mksignames.c instead. */ + 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; +} +#endif -/* A translation list so we can be polite to our users. */ -static char *signal_names[NSIG + 2] = { - "EXIT", - "SIGHUP", - "SIGINT", - "SIGQUIT", - "SIGILL", - "SIGTRAP", - "SIGABRT", - "SIGBUS", - "SIGFPE", - "SIGKILL", - "SIGUSR1", - "SIGSEGV", - "SIGUSR2", - "SIGPIPE", - "SIGALRM", - "SIGTERM", - "SIGJUNK(16)", - "SIGCHLD", - "SIGCONT", - "SIGSTOP", - "SIGTSTP", - "SIGTTIN", - "SIGTTOU", - "SIGURG", - "SIGXCPU", - "SIGXFSZ", - "SIGVTALRM", - "SIGPROF", - "SIGWINCH", - "SIGIO", - "SIGPWR", - "SIGSYS", - "SIGRTMIN", - "SIGRTMIN+1", - "SIGRTMIN+2", - "SIGRTMIN+3", - "SIGRTMIN+4", - "SIGRTMIN+5", - "SIGRTMIN+6", - "SIGRTMIN+7", - "SIGRTMIN+8", - "SIGRTMIN+9", - "SIGRTMIN+10", - "SIGRTMIN+11", - "SIGRTMIN+12", - "SIGRTMIN+13", - "SIGRTMIN+14", - "SIGRTMIN+15", - "SIGRTMAX-15", - "SIGRTMAX-14", - "SIGRTMAX-13", - "SIGRTMAX-12", - "SIGRTMAX-11", - "SIGRTMAX-10", - "SIGRTMAX-9", - "SIGRTMAX-8", - "SIGRTMAX-7", - "SIGRTMAX-6", - "SIGRTMAX-5", - "SIGRTMAX-4", - "SIGRTMAX-3", - "SIGRTMAX-2", - "SIGRTMAX-1", - "SIGRTMAX", - "DEBUG", - (char *)0x0, -}; +/* + * Returns true if the pattern matches the string. + */ -#if JOBS +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +/* squoted: string might have quote chars */ static int -killcmd(argc, argv) - int argc; - char **argv; +patmatch(char *pattern, char *string, int squoted) { - int signo = -1; - int list = 0; - int i; - pid_t pid; - struct job *jp; - - if (argc <= 1) { -usage: - error( -"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" -"kill -l [exitstatus]" - ); - } - - if (*argv[1] == '-') { - signo = decode_signal(argv[1] + 1, 1); - if (signo < 0) { - int c; - - while ((c = nextopt("ls:")) != '\0') - switch (c) { - case 'l': - list = 1; - break; - case 's': - signo = decode_signal(optionarg, 1); - if (signo < 0) { - error( - "invalid signal number or name: %s", - optionarg - ); - } - break; -#ifdef DEBUG - default: - error( - "nextopt returned character code 0%o", c); -#endif - } - } else - argptr++; - } - - if (!list && signo < 0) - signo = SIGTERM; - - if ((signo < 0 || !*argptr) ^ list) { - goto usage; - } - - if (list) { - if (!*argptr) { - out1str("0\n"); - for (i = 1; i < NSIG; i++) { - out1fmt(snlfmt, signal_names[i] + 3); - } - return 0; - } - signo = atoi(*argptr); - if (signo > 128) - signo -= 128; - if (0 < signo && signo < NSIG) - out1fmt(snlfmt, signal_names[signo] + 3); - else - error("invalid signal number or exit status: %s", - *argptr); - return 0; - } + const char *p; + char *q; - do { - if (**argptr == '%') { - jp = getjob(*argptr); - if (jp->jobctl == 0) - error("job %s not created under job control", - *argptr); - pid = -jp->ps[0].pid; - } else - pid = atoi(*argptr); - if (kill(pid, signo) != 0) - error("%s: %s", *argptr, strerror(errno)); - } while (*++argptr); + p = preglob(pattern); + q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string; - return 0; + return !fnmatch(p, q, 0); } + static int -fgcmd(argc, argv) - int argc; - char **argv; +patmatch2(char *pattern, char *string, int squoted) { - struct job *jp; - int pgrp; - int status; + char *p; + int res; - jp = getjob(argv[1]); - if (jp->jobctl == 0) - error("job not created under job control"); - pgrp = jp->ps[0].pid; -#ifdef OLD_TTY_DRIVER - ioctl(fileno2, TIOCSPGRP, (char *)&pgrp); + sstrnleft--; + p = grabstackstr(expdest); + res = patmatch(pattern, string, squoted); + ungrabstackstr(p, expdest); + return res; +} #else - tcsetpgrp(fileno2, pgrp); -#endif - restartjob(jp); - INTOFF; - status = waitforjob(jp); - INTON; - return status; +static int +patmatch(char *pattern, char *string, int squoted) { + return pmatch(pattern, string, squoted); } static int -bgcmd(argc, argv) - int argc; - char **argv; +pmatch(char *pattern, char *string, int squoted) { - 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; -} - + char *p, *q; + char c; -static void -restartjob(jp) - struct job *jp; -{ - struct procstat *ps; - int i; + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (squoted && *q == CTLESC) + q++; + if (*q++ != *p++) + return 0; + break; + case CTLQUOTEMARK: + continue; + case '?': + if (squoted && *q == CTLESC) + q++; + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + while (c == CTLQUOTEMARK || c == '*') + c = *++p; + if (c != CTLESC && c != CTLQUOTEMARK && + c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (squoted && *q == CTLESC && + q[1] == c) + break; + if (*q == '\0') + return 0; + if (squoted && *q == CTLESC) + q++; + q++; + } + } + do { + if (pmatch(p, q, squoted)) + return 1; + if (squoted && *q == CTLESC) + q++; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; - if (jp->state == JOBDONE) - return; - INTOFF; - killpg(jp->ps[0].pid, SIGCONT); - for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { - if (WIFSTOPPED(ps->status)) { - ps->status = -1; - jp->state = 0; + endp = p; + if (*endp == '!') + endp++; + for (;;) { + while (*endp == CTLQUOTEMARK) + endp++; + 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 (squoted && chr == CTLESC) + chr = *q++; + if (chr == '\0') + return 0; + c = *p++; + do { + if (c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + while (*p == CTLQUOTEMARK) + 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 (squoted && *q == CTLESC) + q++; + if (*q++ != c) + return 0; + break; } } - INTON; +breakloop: + if (*q != '\0') + return 0; + return 1; } #endif -static 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. + * Remove any CTLESC characters from a string. */ -static void -showjobs(change) - int change; +#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) +static char * +_rmescapes(char *str, int flag) { - int jobno; - int procno; - int i; - struct job *jp; - struct procstat *ps; - int col; - char s[64]; + char *p, *q, *r; + static const char qchars[] = { CTLESC, CTLQUOTEMARK, 0 }; - 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; + p = strpbrk(str, qchars); + if (!p) { + return str; + } + q = p; + r = str; + if (flag & RMESCAPE_ALLOC) { + size_t len = p - str; + q = r = stalloc(strlen(p) + len + 1); + if (len > 0) { + memcpy(q, str, len); + q += len; } - if (change && ! jp->changed) + } + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; continue; - procno = jp->nprocs; - for (ps = jp->ps ; ; ps++) { /* for each process */ - if (ps == jp->ps) - fmtstr(s, 64, "[%d] %ld ", jobno, - (long)ps->pid); - else - fmtstr(s, 64, " %ld ", - (long)ps->pid); - out1str(s); - col = strlen(s); - s[0] = '\0'; - if (ps->status == -1) { - /* don't print anything */ - } else if (WIFEXITED(ps->status)) { - fmtstr(s, 64, "Exit %d", - WEXITSTATUS(ps->status)); - } else { -#if JOBS - if (WIFSTOPPED(ps->status)) - i = WSTOPSIG(ps->status); - else /* WIFSIGNALED(ps->status) */ -#endif - i = WTERMSIG(ps->status); - if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) - scopy(sys_siglist[i & 0x7F], s); - else - fmtstr(s, 64, "Signal %d", i & 0x7F); - if (WCOREDUMP(ps->status)) - strcat(s, " (core dumped)"); - } - out1str(s); - col += strlen(s); - out1fmt( - "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ', - ps->cmd - ); - if (--procno <= 0) - break; } - jp->changed = 0; - if (jp->state == JOBDONE) { - freejob(jp); + if (*p == CTLESC) { + p++; + if (flag & RMESCAPE_GLOB && *p != '/') { + *q++ = '\\'; + } } + *q++ = *p++; } + *q = '\0'; + return r; } - - -/* - * Mark a job structure as unused. - */ - +#else static void -freejob(jp) - struct job *jp; - { - struct procstat *ps; - int i; +rmescapes(str) + char *str; +{ + char *p, *q; - INTOFF; - for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { - if (ps->cmd != nullstr) - ckfree(ps->cmd); + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK) { + if (*p++ == '\0') + return; } - if (jp->ps != &jp->ps0) - ckfree(jp->ps); - jp->used = 0; -#if JOBS - if (curjob == jp - jobtab + 1) - curjob = 0; -#endif - INTON; + q = p; + while (*p) { + if (*p == CTLQUOTEMARK) { + p++; + continue; + } + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; } +#endif + +/* + * See if a pattern matches in a case statement. + */ static int -waitcmd(argc, argv) - int argc; - char **argv; +casematch(union node *pattern, const char *val) { - struct job *job; - int status, retval; - struct job *jp; + struct stackmark smark; + int result; + char *p; - if (--argc > 0) { -start: - job = getjob(*++argv); - } else { - job = NULL; - } - for (;;) { /* loop until process terminated or stopped */ - if (job != NULL) { - if (job->state) { - status = job->ps[job->nprocs - 1].status; - if (! iflag) - freejob(job); - if (--argc) { - goto start; - } - if (WIFEXITED(status)) - retval = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - retval = WSTOPSIG(status) + 128; -#endif - else { - /* XXX: limits number of signals */ - retval = WTERMSIG(status) + 128; - } - return retval; - } - } else { - for (jp = jobtab ; ; jp++) { - if (jp >= jobtab + njobs) { /* no running procs */ - return 0; - } - if (jp->used && jp->state == 0) - break; - } - } - if (dowait(2, 0) < 0 && errno == EINTR) { - return 129; - } - } + 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, (char *)val, 0); + popstackmark(&smark); + return result; } - - /* - * Convert a job name to a job structure. + * Our own itoa(). */ -static struct job * -getjob(name) - char *name; +static char * +cvtnum(num, buf) + int num; + char *buf; { - int jobno; - struct job *jp; - int pid; - int i; + int len; - 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 { - 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); + CHECKSTRSPACE(32, buf); + len = sprintf(buf, "%d", num); + STADJUST(len, buf); + return buf; +} +/* + * Editline and history functions (and glue). + */ +static int histcmd(argc, argv) + int argc; + char **argv; +{ + error("not compiled with history support"); /* NOTREACHED */ } +struct redirtab { + struct redirtab *next; + /* short renamed[10]; *//* Current ash support only 0-9 descriptors */ + char renamed[10]; +}; -/* - * Return a new job structure, - */ +static struct redirtab *redirlist; -struct job * -makejob(node, nprocs) - union node *node; - int nprocs; -{ - int i; - struct job *jp; +extern char **environ; - 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]); - /* Relocate `ps' pointers */ - for (i = 0; i < njobs; i++) - if (jp[i].ps == &jobtab[i].ps0) - jp[i].ps = &jp[i].ps0; - 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; + + +/* + * Initialization code. + */ + +static void +init(void) { + + /* from cd.c: */ + { + setpwd(0, 0); + } + + /* from input.c: */ + { + basepf.nextc = basepf.buf = basebuf; + } + + /* from var.c: */ + { + char **envp; + char ppid[32]; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } + + snprintf(ppid, sizeof(ppid), "%d", (int) getppid()); + setvar("PPID", ppid, 0); + } } + /* - * 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). + * This routine is called when an error or an interrupt occurs in an + * interactive shell and control is returned to the main command loop. */ -static int -forkshell(jp, n, mode) - union node *n; - struct job *jp; - int mode; -{ - int pid; - int pgrp; - const char *devnull = _PATH_DEVNULL; - const char *nullerr = "Can't open %s"; +/* 1 == check for aliases, 2 == also check for assignments */ +static int checkalias; /* also used in no alias mode for check assignments */ - 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; +static void +reset(void) { - TRACE(("Child shell %d\n", getpid())); - wasroot = rootshell; - rootshell = 0; - 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 ***/ -#ifdef OLD_TTY_DRIVER - if (ioctl(fileno2, TIOCSPGRP, (char *)&pgrp) < 0) - error("TIOCSPGRP failed, errno=%d", errno); -#else - if (tcsetpgrp(fileno2, pgrp) < 0) - error("tcsetpgrp failed, errno=%d", errno); -#endif - } - 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(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } + /* from eval.c: */ + { + evalskip = 0; + loopnest = 0; + funcnest = 0; + } + + /* from input.c: */ + { + if (exception != EXSHELLPROC) + parselleft = parsenleft = 0; /* clear input buffer */ + popallfiles(); + } + + /* from parser.c: */ + { + tokpushback = 0; + checkkwd = 0; + checkalias = 0; + } + + /* from redir.c: */ + { + while (redirlist) + popredir(); + } + +} + + + +/* + * This file implements the input routines used by the parser. + */ + +#ifdef BB_FEATURE_COMMAND_EDITING +static const char * cmdedit_prompt; +static inline void putprompt(const char *s) { + cmdedit_prompt = s; +} #else - if (mode == FORK_BG) { - ignoresig(SIGINT); - ignoresig(SIGQUIT); - if ((jp == NULL || jp->nprocs == 0) && - ! fd0_redirected_p ()) { - close(0); - if (open(devnull, O_RDONLY) != 0) - error(nullerr, devnull); - } - } -#endif - for (i = njobs, p = jobtab ; --i >= 0 ; p++) - if (p->used) - freejob(p); - 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; +static inline void putprompt(const char *s) { + out2str(s); } +#endif + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ /* - * 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. + * Same as pgetc(), but ignores PEOA. */ +#ifdef ASH_ALIAS static int -waitforjob(jp) - struct job *jp; - { -#if JOBS - int mypgrp = getpgrp(); +pgetc2() +{ + int c; + do { + c = pgetc_macro(); + } while (c == PEOA); + return c; +} +#else +static inline int pgetc2() { return pgetc_macro(); } #endif - int status; - int st; - struct sigaction act, oact; - INTOFF; - intreceived = 0; -#if JOBS - if (!jobctl) { -#else - if (!iflag) { -#endif - sigaction(SIGINT, 0, &act); - act.sa_handler = waitonint; - sigaction(SIGINT, &act, &oact); - } - TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); - while (jp->state == 0) { - dowait(1, jp); +/* + * Read a line from the script. + */ + +static inline char * +pfgets(char *line, int len) +{ + char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc2(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; } -#if JOBS - if (!jobctl) { -#else - if (!iflag) { -#endif - sigaction(SIGINT, &oact, 0); - if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT); + *p = '\0'; + return line; +} + +static inline int +preadfd(void) +{ + int nr; + char *buf = parsefile->buf; + parsenextc = buf; + +retry: +#ifdef BB_FEATURE_COMMAND_EDITING + { + if (!iflag) + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); + else { + cmdedit_read_input((char*)cmdedit_prompt, buf); + nr = strlen(buf); + } } -#if JOBS - if (jp->jobctl) { -#ifdef OLD_TTY_DRIVER - if (ioctl(fileno2, TIOCSPGRP, (char *)&mypgrp) < 0) - error("TIOCSPGRP failed, errno=%d\n", errno); #else - if (tcsetpgrp(fileno2, mypgrp) < 0) - error("tcsetpgrp failed, errno=%d\n", errno); -#endif - } - if (jp->state == JOBSTOPPED) - curjob = jp - jobtab + 1; -#endif - status = jp->ps[jp->nprocs - 1].status; - /* convert to 8 bits */ - if (WIFEXITED(status)) - st = WEXITSTATUS(status); -#if JOBS - else if (WIFSTOPPED(status)) - st = WSTOPSIG(status) + 128; + nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); #endif - else - st = WTERMSIG(status) + 128; -#if JOBS - if (jp->jobctl) { - /* - * This is truly gross. - * If we're doing job control, then we did a TIOCSPGRP which - * caused us (the shell) to no longer be in the controlling - * session -- so we wouldn't have seen any ^C/SIGINT. So, we - * intuit from the subprocess exit status whether a SIGINT - * occured, and if so interrupt ourselves. Yuck. - mycroft - */ - if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) - raise(SIGINT); + + if (nr < 0) { + 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; + } + } + } } -#endif - if (! JOBS || jp->state == JOBDONE) - freejob(jp); - INTON; - return st; + return nr; } - - -/* - * Wait for a process to terminate. - */ - -static int -dowait(block, job) - int block; - struct job *job; +static void +popstring(void) { - int pid; - int status; - struct procstat *sp; - struct job *jp; - struct job *thisjob; - int done; - int stopped; - int core; - int sig; + struct strpush *sp = parsefile->strpush; - TRACE(("dowait(%d) called\n", block)); - do { - pid = waitproc(block, &status); - TRACE(("wait returns %d, status=%d\n", pid, status)); - } while (!(block & 2) && 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(("Changing 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 (WIFSTOPPED(sp->status)) - 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 - } +#ifdef ASH_ALIAS + if (sp->ap) { + if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (!checkalias) { + checkalias = 1; } } - } - INTON; - if (! rootshell || ! iflag || (job && thisjob == job)) { - core = WCOREDUMP(status); -#if JOBS - if (WIFSTOPPED(status)) sig = WSTOPSIG(status); - else -#endif - if (WIFEXITED(status)) sig = 0; - else sig = WTERMSIG(status); + if (sp->string != sp->ap->val) { + ckfree(sp->string); + } - if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { - if (thisjob != job) - outfmt(out2, "%d: ", pid); -#if JOBS - if (sig == SIGTSTP && rootshell && iflag) - outfmt(out2, "%%%ld ", - (long)(job - jobtab + 1)); -#endif - if (sig < NSIG && sys_siglist[sig]) - out2str(sys_siglist[sig]); - else - outfmt(out2, "Signal %d", sig); - if (core) - out2str(" - core dumped"); - out2c('\n'); -#ifdef FLUSHERR - flushout(&errout); -#endif - } else { - TRACE(("Not printing status: status=%d, sig=%d\n", - status, sig)); + sp->ap->flag &= ~ALIASINUSE; + if (sp->ap->flag & ALIASDEAD) { + unalias(sp->ap->name); } - } else { - TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); - if (thisjob) - thisjob->changed = 1; } - return pid; +#endif + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; +/*dprintf("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; } - /* - * 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. + * Refill the input buffer and return the next input character: * - * 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. + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. */ -#ifdef SYSV -static int gotsigchild; - -static int onsigchild() { - gotsigchild = 1; -} -#endif - - static int -waitproc(block, status) - int block; - int *status; +preadbuffer(void) { -#ifdef BSD - int flags; + char *p, *q; + int more; + char savec; - flags = 0; -#if JOBS - if (jobctl) - flags |= WUNTRACED; + while (parsefile->strpush) { +#ifdef ASH_ALIAS + if (parsenleft == -1 && parsefile->strpush->ap && + parsenextc[-1] != ' ' && parsenextc[-1] != '\t') { + return PEOA; + } #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; + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); } - return wait(status); -#else - if (block == 0) - return 0; - return wait(status); -#endif -#endif -} + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushall(); -/* - * return 1 if there are stopped jobs, otherwise 0 - */ -static int job_warning = 0; -static int -stoppedjobs() -{ - int jobno; - struct job *jp; +again: + if (parselleft <= 0) { + if ((parselleft = preadfd()) <= 0) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } - 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); + q = p = parsenextc; + + /* delete nul characters */ + for (more = 1; more;) { + switch (*p) { + case '\0': + p++; /* Skip nul */ + goto check; + + + case '\n': + parsenleft = q - parsenextc; + more = 0; /* Stop processing here */ + break; + } + + *q++ = *p++; +check: + if (--parselleft <= 0 && more) { + parsenleft = q - parsenextc - 1; + if (parsenleft < 0) + goto again; + more = 0; } } - return (0); + savec = *q; + *q = '\0'; + + if (vflag) { + out2str(parsenextc); + } + + *q = savec; + + return *parsenextc++; } + /* - * Return a string identifying a command (to be printed by the - * jobs command. + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. */ +static void +pushstring(char *s, int len, void *ap) +{ + struct strpush *sp; -static char *cmdnextc; -static int cmdnleft; -#define MAXCMDTEXT 200 + 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; +#ifdef ASH_ALIAS + sp->ap = (struct alias *)ap; + if (ap) { + ((struct alias *)ap)->flag |= ALIASINUSE; + sp->string = s; + } +#endif + parsenextc = s; + parsenleft = len; + INTON; +} -static char * -commandtext(n) - union node *n; - { - char *name; - cmdnextc = name = ckmalloc(MAXCMDTEXT); - cmdnleft = MAXCMDTEXT - 4; - cmdtxt(n); - *cmdnextc = '\0'; - return name; +/* + * Like setinputfile, but takes input from a string. + */ + +static void +setinputstring(char *string) +{ + INTOFF; + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; } + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + static void -cmdtxt(n) - union node *n; - { - union node *np; - struct nodelist *lp; - const char *p; - int i; - char s[2]; +pushfile(void) { + struct parsefile *pf; - 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(spcstr); - } - for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { - cmdputs(spcstr); - 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 NTOOV: - p = ">|"; i = 1; goto redir; - case NFROM: - p = "<"; i = 0; goto redir; - case NFROMFD: - p = "<&"; i = 0; goto redir; - case NFROMTO: - 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; - } + parsefile->nleft = parsenleft; + parsefile->lleft = parselleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; } +#ifdef JOBS +static void restartjob (struct job *); +#endif +static void freejob (struct job *); +static struct job *getjob (const char *); +static int dowait (int, struct job *); +static void waitonint(int); -static void -cmdputs(s) - const char *s; - { - const char *p; - char *q; - 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; -} +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +static int fd0_redirected = 0; -static void waitonint(int sig) { - intreceived = 1; - return; +/* Return true if fd 0 has already been redirected at least once. */ +static inline int +fd0_redirected_p () { + return fd0_redirected != 0; } -/* $NetBSD: mail.c,v 1.14 2000/07/03 03:26:19 matt Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +static void dupredirect (const union node *, int, int fd1dup); +#ifdef JOBS /* - * Routines to check for mail. (Perhaps make part of main.c?) + * 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. */ -#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. - */ -static void -chkmail(silent) - int silent; +static void setjobctl(int enable) { - int i; - const char *mpath; - char *p; - char *q; - struct stackmark smark; - struct stat statb; +#ifdef OLD_TTY_DRIVER + int ldisc; +#endif - if (silent) - nmboxes = 10; - if (nmboxes == 0) + if (enable == jobctl || rootshell == 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++); -#ifdef DEBUG - if (q[-1] != '/') - abort(); + if (enable) { + do { /* while we are in the background */ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { +#else + initialpgrp = tcgetpgrp(2); + if (initialpgrp < 0) { #endif - 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) { - outfmt( - &errout, snlfmt, - pathopt? pathopt : "you have mail" - ); - } - 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) { - outfmt( - &errout, snlfmt, - pathopt? pathopt : "you have mail" - ); + 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; } - mailtime[i] = statb.st_size; #endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + setpgid(0, rootpid); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&rootpid); +#else + tcsetpgrp(2, rootpid); +#endif + } else { /* turning job control off */ + setpgid(0, initialpgrp); +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); +#else + tcsetpgrp(2, initialpgrp); +#endif + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); } - nmboxes = i; - popstackmark(&smark); + jobctl = enable; } -/* $NetBSD: main.c,v 1.40 2001/02/04 19:52:06 christos Exp $ */ +#endif -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +/* A translation list so we can be polite to our users. */ +static char *signal_names[NSIG + 2] = { + "EXIT", + "SIGHUP", + "SIGINT", + "SIGQUIT", + "SIGILL", + "SIGTRAP", + "SIGABRT", + "SIGBUS", + "SIGFPE", + "SIGKILL", + "SIGUSR1", + "SIGSEGV", + "SIGUSR2", + "SIGPIPE", + "SIGALRM", + "SIGTERM", + "SIGJUNK(16)", + "SIGCHLD", + "SIGCONT", + "SIGSTOP", + "SIGTSTP", + "SIGTTIN", + "SIGTTOU", + "SIGURG", + "SIGXCPU", + "SIGXFSZ", + "SIGVTALRM", + "SIGPROF", + "SIGWINCH", + "SIGIO", + "SIGPWR", + "SIGSYS", +#ifdef SIGRTMIN + "SIGRTMIN", + "SIGRTMIN+1", + "SIGRTMIN+2", + "SIGRTMIN+3", + "SIGRTMIN+4", + "SIGRTMIN+5", + "SIGRTMIN+6", + "SIGRTMIN+7", + "SIGRTMIN+8", + "SIGRTMIN+9", + "SIGRTMIN+10", + "SIGRTMIN+11", + "SIGRTMIN+12", + "SIGRTMIN+13", + "SIGRTMIN+14", + "SIGRTMIN+15", + "SIGRTMAX-15", + "SIGRTMAX-14", + "SIGRTMAX-13", + "SIGRTMAX-12", + "SIGRTMAX-11", + "SIGRTMAX-10", + "SIGRTMAX-9", + "SIGRTMAX-8", + "SIGRTMAX-7", + "SIGRTMAX-6", + "SIGRTMAX-5", + "SIGRTMAX-4", + "SIGRTMAX-3", + "SIGRTMAX-2", + "SIGRTMAX-1", + "SIGRTMAX", +#endif + "DEBUG", + (char *)0x0, +}; -#define PROFILE 0 -static int rootpid; -static int rootshell; -#if PROFILE -short profile_buf[16384]; -extern int etext(); -#endif - -static void read_profile __P((const char *)); -static char *find_dot_file __P((char *)); -int shell_main __P((int, char **)); - -extern int oexitstatus; -/* - * 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 -shell_main(argc, argv) +#ifdef JOBS +static int +killcmd(argc, argv) int argc; char **argv; { - struct jmploc jmploc; - struct stackmark smark; - volatile int state; - char *shinit; + int signo = -1; + int list = 0; + int i; + pid_t pid; + struct job *jp; - DOTCMD = find_builtin("."); - BLTINCMD = find_builtin("builtin"); - COMMANDCMD = find_builtin("command"); - EXECCMD = find_builtin("exec"); - EVALCMD = find_builtin("eval"); + if (argc <= 1) { +usage: + error( +"Usage: kill [-s sigspec | -signum | -sigspec] [pid | job]... or\n" +"kill -l [exitstatus]" + ); + } -#if PROFILE - monitor(4, etext, profile_buf, sizeof profile_buf, 50); -#endif -#if defined(linux) || defined(__GNU__) - signal(SIGCHLD, SIG_DFL); -#endif - state = 0; - if (setjmp(jmploc.loc)) { - INTOFF; - /* - * When a shell procedure is executed, we raise the - * exception EXSHELLPROC to clean up before executing - * the shell procedure. - */ - switch (exception) { - case EXSHELLPROC: - rootpid = getpid(); - rootshell = 1; - minusc = NULL; - state = 3; - break; + if (*argv[1] == '-') { + signo = decode_signal(argv[1] + 1, 1); + if (signo < 0) { + int c; - case EXEXEC: - exitstatus = exerrno; - break; + while ((c = nextopt("ls:")) != '\0') + switch (c) { + case 'l': + list = 1; + break; + case 's': + signo = decode_signal(optionarg, 1); + if (signo < 0) { + error( + "invalid signal number or name: %s", + optionarg + ); + } + break; +#ifdef DEBUG + default: + error( + "nextopt returned character code 0%o", c); +#endif + } + } else + argptr++; + } - case EXERROR: - exitstatus = 2; - break; + if (!list && signo < 0) + signo = SIGTERM; - default: - break; - } + if ((signo < 0 || !*argptr) ^ list) { + goto usage; + } - if (exception != EXSHELLPROC) { - if (state == 0 || iflag == 0 || ! rootshell) - exitshell(exitstatus); - } - reset(); - if (exception == EXINT -#if ATTY - && (! attyset() || equal(termval(), "emacs")) -#endif - ) { - out2c('\n'); -#ifdef FLUSHERR - flushout(out2); -#endif + if (list) { + if (!*argptr) { + out1str("0\n"); + for (i = 1; i < NSIG; i++) { + printf(snlfmt, signal_names[i] + 3); + } + return 0; } - popstackmark(&smark); - FORCEINTON; /* enable interrupts */ - if (state == 1) - goto state1; - else if (state == 2) - goto state2; - else if (state == 3) - goto state3; + signo = atoi(*argptr); + if (signo > 128) + signo -= 128; + if (0 < signo && signo < NSIG) + printf(snlfmt, signal_names[signo] + 3); 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; -#ifndef linux - if (getuid() == geteuid() && getgid() == getegid()) { -#endif - if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { - state = 3; - read_profile(shinit); - } -#ifndef linux + error("invalid signal number or exit status: %s", + *argptr); + return 0; } -#endif -state3: - state = 4; - if (sflag == 0 || minusc) { - static int sigs[] = { - SIGINT, SIGQUIT, SIGHUP, -#ifdef SIGTSTP - SIGTSTP, -#endif - SIGPIPE - }; -#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) - int i; - for (i = 0; i < SIGSSIZE; i++) - setsignal(sigs[i]); - } + do { + if (**argptr == '%') { + jp = getjob(*argptr); + if (jp->jobctl == 0) + error("job %s not created under job control", + *argptr); + pid = -jp->ps[0].pid; + } else + pid = atoi(*argptr); + if (kill(pid, signo) != 0) + error("%s: %m", *argptr); + } while (*++argptr); - if (minusc) - evalstring(minusc, 0); + return 0; +} - if (sflag || minusc == NULL) { -state4: /* XXX ??? - why isn't this before the "if" statement */ - cmdloop(1); - } -#if PROFILE - monitor(0); +static 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; +#ifdef OLD_TTY_DRIVER + ioctl(2, TIOCSPGRP, (char *)&pgrp); +#else + tcsetpgrp(2, pgrp); #endif - exitshell(exitstatus); - /* NOTREACHED */ + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; } -/* - * Read and execute commands. "Top" is nonzero for the top level command - * loop; it turns on prompting if the shell is interactive. - */ +static 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 -cmdloop(top) - int top; +restartjob(jp) + struct job *jp; { - union node *n; - struct stackmark smark; - int inter; - int numeof = 0; + struct procstat *ps; + int i; - 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); - setstackmark(&smark); - if (evalskip == SKIPFILE) { - evalskip = 0; - break; + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = 0; } } - popstackmark(&smark); + INTON; } +#endif + +static void showjobs(int change); +static int +jobscmd(argc, argv) + int argc; + char **argv; +{ + showjobs(0); + return 0; +} + /* - * Read /etc/profile or .profile. Return on error. + * 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. */ static void -read_profile(name) - const char *name; +showjobs(change) + int change; { - int fd; - int xflag_set = 0; - int vflag_set = 0; + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - INTON; - if (fd < 0) - return; - /* -q turns off -x and -v just when executing init files */ - if (qflag) { - if (xflag) - xflag = 0, xflag_set = 1; - if (vflag) - vflag = 0, vflag_set = 1; - } - cmdloop(0); - if (qflag) { - if (xflag_set) - xflag = 1; - if (vflag_set) - vflag = 1; + 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) + snprintf(s, 64, "[%d] %ld ", jobno, + (long)ps->pid); + else + snprintf(s, 64, " %ld ", + (long)ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if (WIFEXITED(ps->status)) { + snprintf(s, 64, "Exit %d", + WEXITSTATUS(ps->status)); + } else { +#ifdef JOBS + if (WIFSTOPPED(ps->status)) + i = WSTOPSIG(ps->status); + else /* WIFSIGNALED(ps->status) */ +#endif + i = WTERMSIG(ps->status); + if ((i & 0x7F) < NSIG && sys_siglist[i & 0x7F]) + strcpy(s, sys_siglist[i & 0x7F]); + else + snprintf(s, 64, "Signal %d", i & 0x7F); + if (WCOREDUMP(ps->status)) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + printf( + "%*c%s\n", 30 - col >= 0 ? 30 - col : 0, ' ', + ps->cmd + ); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } } - popfile(); } - /* - * Read a file containing shell functions. + * Mark a job structure as unused. */ static void -readcmdfile(name) - char *name; +freejob(struct job *jp) { - int fd; + const struct procstat *ps; + int i; INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - else - error("Can't open %s", name); + 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; +#ifdef JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif 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(mybasename) - char *mybasename; -{ - char *fullname; - const char *path = pathval(); - struct stat statb; - - /* don't try this for absolute or relative paths */ - if (strchr(mybasename, '/')) - return mybasename; - - while ((fullname = padvance(&path, mybasename)) != NULL) { - if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { - /* - * Don't bother freeing here, since it will - * be freed by the caller. - */ - return fullname; - } - stunalloc(fullname); - } - - /* not found in the PATH */ - error("%s: not found", mybasename); - /* NOTREACHED */ -} - static int -dotcmd(argc, argv) +waitcmd(argc, argv) int argc; char **argv; { - struct strlist *sp; - exitstatus = 0; - - for (sp = cmdenviron; sp ; sp = sp->next) - setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); - - if (argc >= 2) { /* That's what SVR2 does */ - char *fullname; - struct stackmark smark; + struct job *job; + int status, retval; + struct job *jp; - setstackmark(&smark); - fullname = find_dot_file(argv[1]); - setinputfile(fullname, 1); - commandname = fullname; - cmdloop(0); - popfile(); - popstackmark(&smark); + if (--argc > 0) { +start: + job = getjob(*++argv); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if (! iflag) + freejob(job); + if (--argc) { + goto start; + } + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + retval = WSTOPSIG(status) + 128; +#endif + else { + /* XXX: limits number of signals */ + retval = WTERMSIG(status) + 128; + } + return retval; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + if (dowait(2, 0) < 0 && errno == EINTR) { + return 129; + } } - return exitstatus; } -static int -exitcmd(argc, argv) - int argc; - char **argv; -{ - if (stoppedjobs()) - return 0; - if (argc > 1) - exitstatus = number(argv[1]); - else - exitstatus = oexitstatus; - exitshell(exitstatus); - /* NOTREACHED */ -} -/* $NetBSD: memalloc.c,v 1.23 2000/11/01 19:56:01 christos Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - /* - * Parse trees for commands are allocated in lifo order, so we use a stack - * to make this more efficient, and also to avoid all sorts of exception - * handling code to handle interrupts in the middle of a parse. - * - * The size 504 was chosen because the Ultrix malloc handles that size - * well. + * Convert a job name to a job structure. */ -#define MINSIZE 504 /* minimum size of a block */ - - -struct stack_block { - struct stack_block *prev; - char space[MINSIZE]; -}; - -struct stack_block stackbase; -struct stack_block *stackp = &stackbase; -struct stackmark *markp; -static char *stacknxt = stackbase.space; -static int stacknleft = MINSIZE; -static int sstrnleft; -static int herefd = -1; - - - -pointer -stalloc(nbytes) - int nbytes; +static struct job * +getjob(const char *name) { - char *p; - - nbytes = ALIGN(nbytes); - if (nbytes > stacknleft) { - int blocksize; - struct stack_block *sp; - - blocksize = nbytes; - if (blocksize < MINSIZE) - blocksize = MINSIZE; - INTOFF; - sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); - sp->prev = stackp; - stacknxt = sp->space; - stacknleft = blocksize; - stackp = sp; - INTON; - } - p = stacknxt; - stacknxt += nbytes; - stacknleft -= nbytes; - return p; -} - + int jobno; + struct job *jp; + int pid; + int i; -static void -stunalloc(p) - pointer p; - { -#ifdef DEBUG - if (p == NULL) { /*DEBUG */ - write(2, "stunalloc\n", 10); - abort(); - } + if (name == NULL) { +#ifdef 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 - if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) { - p = stackp->space; + } 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]; +#ifdef JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + 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)) { + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } } - stacknleft += stacknxt - (char *)p; - stacknxt = p; + error("No such job: %s", name); + /* NOTREACHED */ } -static void -setstackmark(mark) - struct stackmark *mark; - { - mark->stackp = stackp; - mark->stacknxt = stacknxt; - mark->stacknleft = stacknleft; - mark->marknext = markp; - markp = mark; -} - +/* + * Return a new job structure, + */ -static void -popstackmark(mark) - struct stackmark *mark; - { - struct stack_block *sp; +static struct job * +makejob(const 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]); + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } INTOFF; - markp = mark->marknext; - while (stackp != mark->stackp) { - sp = stackp; - stackp = sp->prev; - ckfree(sp); + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#ifdef JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; } - stacknxt = mark->stacknxt; - stacknleft = mark->stacknleft; INTON; + TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, + jp - jobtab + 1)); + return jp; } /* - * When the parser reads in a string, it wants to stick the string on the - * stack and only adjust the stack pointer when it knows how big the - * string is. Stackblock (defined in stack.h) returns a pointer to a block - * of space on top of the stack and stackblocklen returns the length of - * this block. Growstackblock will grow this space by at least one byte, - * possibly moving it (like realloc). Grabstackblock actually allocates the - * part of the block that has been used. + * 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). */ -static void -growstackblock() { - char *p; - int newlen = ALIGN(stacknleft * 2 + 100); - char *oldspace = stacknxt; - int oldlen = stacknleft; - struct stack_block *sp; - struct stack_block *oldstackp; - - if (stacknxt == stackp->space && stackp != &stackbase) { - INTOFF; - oldstackp = stackp; - sp = stackp; - stackp = sp->prev; - sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); - sp->prev = stackp; - stackp = sp; - stacknxt = sp->space; - stacknleft = newlen; - { - /* Stack marks pointing to the start of the old block - * must be relocated to point to the new block - */ - struct stackmark *xmark; - xmark = markp; - while (xmark != NULL && xmark->stackp == oldstackp) { - xmark->stackp = stackp; - xmark->stacknxt = stacknxt; - xmark->stacknleft = stacknleft; - xmark = xmark->marknext; - } - } - INTON; - } else { - p = stalloc(newlen); - memcpy(p, oldspace, oldlen); - stacknxt = p; /* free the space */ - stacknleft += newlen; /* we just allocated */ - } -} - -static void -grabstackblock(len) - int len; +static int +forkshell(struct job *jp, const union node *n, int mode) { - len = ALIGN(len); - stacknxt += len; - stacknleft -= len; -} - - + int pid; +#ifdef JOBS + int pgrp; +#endif + const char *devnull = _PATH_DEVNULL; + const char *nullerr = "Can't open %s"; -/* - * The following routines are somewhat easier to use that the above. - * The user declares a variable of type STACKSTR, which may be declared - * to be a register. The macro STARTSTACKSTR initializes things. Then - * the user uses the macro STPUTC to add characters to the string. In - * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is - * grown as necessary. When the user is done, she can just leave the - * string there and refer to it using stackblock(). Or she can allocate - * the space for it using grabstackstr(). If it is necessary to allow - * someone else to use the stack temporarily and then continue to grow - * the string, the user should use grabstack to allocate the space, and - * then call ungrabstr(p) to return to the previous mode of operation. - * - * USTPUTC is like STPUTC except that it doesn't check for overflow. - * CHECKSTACKSPACE can be called before USTPUTC to ensure that there - * is space for at least one character. - */ - - -static char * -growstackstr() { - int len = stackblocksize(); - if (herefd >= 0 && len >= 1024) { - xwrite(herefd, stackblock(), len); - sstrnleft = len - 1; - return stackblock(); + 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"); } - growstackblock(); - sstrnleft = stackblocksize() - len - 1; - return stackblock() + len; -} - - -/* - * Called from CHECKSTRSPACE. - */ + if (pid == 0) { + struct job *p; + int wasroot; + int i; -static char * -makestrspace(size_t newlen) { - int len = stackblocksize() - sstrnleft; - do { - growstackblock(); - sstrnleft = stackblocksize() - len; - } while (sstrnleft < newlen); - return stackblock() + len; + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + closescript(); + INTON; + clear_traps(); +#ifdef 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 ***/ +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d", errno); +#else + if (tcsetpgrp(2, pgrp) < 0) + error("tcsetpgrp failed, errno=%d", errno); +#endif + } + 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(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(devnull, O_RDONLY) != 0) + error(nullerr, devnull); + } + } +#endif + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } +#ifdef JOBS + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgid(pid, pgrp); + } +#endif + 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; } -static void -ungrabstackstr(s, p) - char *s; - char *p; - { - stacknleft += stacknxt - s; - stacknxt = s; - sstrnleft = stacknleft - (p - s); -} -/* $NetBSD: miscbltin.c,v 1.30 2001/02/04 19:52:06 christos Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - /* - * Miscelaneous builtins. + * 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. */ +static int +waitforjob(struct job *jp) +{ +#ifdef JOBS + int mypgrp = getpgrp(); +#endif + int status; + int st; + struct sigaction act, oact; -#undef rflag - -#ifdef __GLIBC__ -mode_t getmode(const void *, mode_t); -static void *setmode(const char *); - -#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 -typedef enum __rlimit_resource rlim_t; + INTOFF; + intreceived = 0; +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { #endif + sigaction(SIGINT, 0, &act); + act.sa_handler = waitonint; + sigaction(SIGINT, &act, &oact); + } + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0) { + dowait(1, jp); + } +#ifdef JOBS + if (!jobctl) { +#else + if (!iflag) { +#endif + sigaction(SIGINT, &oact, 0); + if (intreceived && trap[SIGINT]) kill(getpid(), SIGINT); + } +#ifdef JOBS + if (jp->jobctl) { +#ifdef OLD_TTY_DRIVER + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); +#else + if (tcsetpgrp(2, mypgrp) < 0) + error("tcsetpgrp failed, errno=%d\n", errno); +#endif + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#ifdef JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; +#ifdef JOBS + if (jp->jobctl) { + /* + * This is truly gross. + * If we're doing job control, then we did a TIOCSPGRP which + * caused us (the shell) to no longer be in the controlling + * session -- so we wouldn't have seen any ^C/SIGINT. So, we + * intuit from the subprocess exit status whether a SIGINT + * occured, and if so interrupt ourselves. Yuck. - mycroft + */ + if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + raise(SIGINT); + } + if (jp->state == JOBDONE) + #endif + freejob(jp); + INTON; + return st; +} /* - * The read builtin. The -e option causes backslashes to escape the - * following character. - * - * This uses unbuffered input, which may be avoidable in some cases. + * Wait for a process to terminate. */ -static int -readcmd(argc, argv) - int argc; - char **argv; -{ - char **ap; - int backslash; - char c; - int rflag; - char *prompt; - const char *ifs; - char *p; - int startword; +/* + * 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. + * + */ + +static inline int +waitproc(int block, int *status) +{ + int flags; + + flags = 0; +#ifdef JOBS + if (jobctl) + flags |= WUNTRACED; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3(status, flags, (struct rusage *)NULL); +} + +static int +dowait(int block, struct job *job) +{ + int pid; int status; - int i; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + int sig; - rflag = 0; - prompt = NULL; - while ((i = nextopt("p:r")) != '\0') { - if (i == 'p') - prompt = optionarg; - else - rflag = 1; - } - if (prompt && isatty(0)) { - putprompt(prompt); - flushall(); - } - if (*(ap = argptr) == NULL) - error("arg count"); - if ((ifs = bltinlookup("IFS")) == NULL) - ifs = defifs; - status = 0; - startword = 1; - backslash = 0; - STARTSTACKSTR(p); - for (;;) { - if (read(0, &c, 1) != 1) { - status = 1; - break; - } - if (c == '\0') - continue; - if (backslash) { - backslash = 0; - if (c != '\n') - STPUTC(c, p); - continue; - } - if (!rflag && c == '\\') { - backslash++; - continue; - } - if (c == '\n') - break; - if (startword && *ifs == ' ' && strchr(ifs, c)) { - continue; - } - startword = 0; - if (backslash && c == '\\') { - if (read(0, &c, 1) != 1) { - status = 1; - break; + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d\n", pid, status)); + } while (!(block & 2) && 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(("Changing 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 (WIFSTOPPED(sp->status)) + done = 0; } - STPUTC(c, p); - } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { - STACKSTRNUL(p); - setvar(*ap, stackblock(), 0); - ap++; - startword = 1; - STARTSTACKSTR(p); + 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; +#ifdef JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { + core = WCOREDUMP(status); +#ifdef JOBS + if (WIFSTOPPED(status)) sig = WSTOPSIG(status); + else +#endif + if (WIFEXITED(status)) sig = 0; + else sig = WTERMSIG(status); + + if (sig != 0 && sig != SIGINT && sig != SIGPIPE) { + if (thisjob != job) + out2fmt("%d: ", pid); +#ifdef JOBS + if (sig == SIGTSTP && rootshell && iflag) + out2fmt("%%%ld ", + (long)(job - jobtab + 1)); +#endif + if (sig < NSIG && sys_siglist[sig]) + out2str(sys_siglist[sig]); + else + out2fmt("Signal %d", sig); + if (core) + out2str(" - core dumped"); + out2c('\n'); } else { - STPUTC(c, p); + TRACE(("Not printing status: status=%d, sig=%d\n", + status, sig)); } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; } - STACKSTRNUL(p); - /* Remove trailing blanks */ - while (stackblock() <= --p && strchr(ifs, *p) != NULL) - *p = '\0'; - setvar(*ap, stackblock(), 0); - while (*++ap != NULL) - setvar(*ap, nullstr, 0); - return status; + return pid; } + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ static int -umaskcmd(argc, argv) - int argc; - char **argv; +stoppedjobs(void) { - char *ap; - int mask; - int i; - int symbolic_mode = 0; + int jobno; + struct job *jp; - while ((i = nextopt("S")) != '\0') { - symbolic_mode = 1; + 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); + } } - INTOFF; - mask = umask(0); - umask(mask); - INTON; - - if ((ap = *argptr) == NULL) { - if (symbolic_mode) { - char u[4], g[4], o[4]; + return (0); +} - i = 0; - if ((mask & S_IRUSR) == 0) - u[i++] = 'r'; - if ((mask & S_IWUSR) == 0) - u[i++] = 'w'; - if ((mask & S_IXUSR) == 0) - u[i++] = 'x'; - u[i] = '\0'; +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ - i = 0; - if ((mask & S_IRGRP) == 0) - g[i++] = 'r'; - if ((mask & S_IWGRP) == 0) - g[i++] = 'w'; - if ((mask & S_IXGRP) == 0) - g[i++] = 'x'; - g[i] = '\0'; +static char *cmdnextc; +static int cmdnleft; +#define MAXCMDTEXT 200 - i = 0; - if ((mask & S_IROTH) == 0) - o[i++] = 'r'; - if ((mask & S_IWOTH) == 0) - o[i++] = 'w'; - if ((mask & S_IXOTH) == 0) - o[i++] = 'x'; - o[i] = '\0'; +static void +cmdputs(const char *s) +{ + const char *p; + char *q; + char c; + int subtype = 0; - out1fmt("u=%s,g=%s,o=%s\n", u, g, o); - } else { - out1fmt("%.4o\n", mask); - } - } else { - if (isdigit((unsigned char)*ap)) { - mask = 0; - do { - if (*ap >= '8' || *ap < '0') - error("Illegal number: %s", argv[1]); - mask = (mask << 3) + (*ap - '0'); - } while (*++ap != '\0'); - umask(mask); - } else { - void *set; + 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; +} - INTOFF; - if ((set = setmode(ap)) != 0) { - mask = getmode(set, ~mask & 0777); - ckfree(set); - } - INTON; - if (!set) - error("Illegal mode: %s", ap); - umask(~mask & 0777); +static void +cmdtxt(const union node *n) +{ + union node *np; + struct nodelist *lp; + const 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(spcstr); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(spcstr); + 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 NTOOV: + p = ">|"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; + case NFROMTO: + 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; } - return 0; } + +static char * +commandtext(const union node *n) +{ + char *name; + + cmdnextc = name = ckmalloc(MAXCMDTEXT); + cmdnleft = MAXCMDTEXT - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +static void waitonint(int sig) { + intreceived = 1; + return; +} /* - * ulimit builtin - * - * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and - * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with - * ash by J.T. Conklin. - * - * Public domain. + * Routines to check for mail. (Perhaps make part of main.c?) */ -struct limits { - const char *name; - int cmd; - int factor; /* multiply by to get rlim_{cur,max} values */ - char option; -}; - -static const struct limits limits[] = { -#ifdef RLIMIT_CPU - { "time(seconds)", RLIMIT_CPU, 1, 't' }, -#endif -#ifdef RLIMIT_FSIZE - { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, -#endif -#ifdef RLIMIT_DATA - { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, -#endif -#ifdef RLIMIT_STACK - { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, -#endif -#ifdef RLIMIT_CORE - { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, -#endif -#ifdef RLIMIT_RSS - { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, -#endif -#ifdef RLIMIT_MEMLOCK - { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, -#endif -#ifdef RLIMIT_NPROC - { "process(processes)", RLIMIT_NPROC, 1, 'p' }, -#endif -#ifdef RLIMIT_NOFILE - { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, -#endif -#ifdef RLIMIT_VMEM - { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, -#endif -#ifdef RLIMIT_SWAP - { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, -#endif - { (char *) 0, 0, 0, '\0' } -}; -static int -ulimitcmd(argc, argv) - int argc; - char **argv; -{ - int c; - rlim_t val = 0; - enum { SOFT = 0x1, HARD = 0x2 } - how = SOFT | HARD; - const struct limits *l; - int set, all = 0; - int optc, what; - struct rlimit limit; +#define MAXMBOXES 10 - what = 'f'; - while ((optc = nextopt("HSatfdsmcnpl")) != '\0') - switch (optc) { - case 'H': - how = HARD; - break; - case 'S': - how = SOFT; - break; - case 'a': - all = 1; - break; - default: - what = optc; - } - for (l = limits; l->name && l->option != what; l++) - ; - if (!l->name) - error("internal error (%c)", what); +static int nmboxes; /* number of mailboxes */ +static time_t mailtime[MAXMBOXES]; /* times of mailboxes */ - set = *argptr ? 1 : 0; - if (set) { - char *p = *argptr; - if (all || argptr[1]) - error("too many arguments"); - if (strcmp(p, "unlimited") == 0) - val = RLIM_INFINITY; - else { - val = (rlim_t) 0; - while ((c = *p++) >= '0' && c <= '9') - { - val = (val * 10) + (long)(c - '0'); - if (val < (rlim_t) 0) - break; - } - if (c) - error("bad number"); - val *= l->factor; - } - } - if (all) { - for (l = limits; l->name; l++) { - getrlimit(l->cmd, &limit); - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; +/* + * 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. + */ - out1fmt("%-20s ", l->name); - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); +static void +chkmail(int silent) +{ + int i; + const char *mpath; + char *p; + 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++); +#ifdef DEBUG + if (q[-1] != '/') + abort(); #endif - } + q[-1] = '\0'; /* delete trailing '/' */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2fmt(snlfmt, + pathopt? pathopt : "you have mail"); } - return 0; + mailtime[i] = statb.st_size; } + nmboxes = i; + popstackmark(&smark); +} - getrlimit(l->cmd, &limit); - if (set) { - if (how & HARD) - limit.rlim_max = val; - if (how & SOFT) - limit.rlim_cur = val; - if (setrlimit(l->cmd, &limit) < 0) - error("error setting limit (%s)", strerror(errno)); - } else { - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; +#define PROFILE 0 - if (val == RLIM_INFINITY) - out1fmt("unlimited\n"); - else - { - val /= l->factor; -#ifdef BSD4_4 - out1fmt("%lld\n", (long long) val); -#else - out1fmt("%ld\n", (long) val); +#if PROFILE +static short profile_buf[16384]; +extern int etext(); #endif - } - } - return 0; -} -/* $NetBSD: mystring.c,v 1.14 1999/07/09 03:05:50 christos Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +static void read_profile (const char *); +static void cmdloop (int); +static void options (int); +static void setoption (int, int); +static void procargs (int, char **); + /* - * String functions. - * - * equal(s1, s2) Return true if strings are equal. - * scopy(from, to) Copy a string. - * scopyn(from, to, n) Like scopy, but checks for overflow. - * number(s) Convert a string of digits to an integer. - * is_number(s) Return true if s is a string of digits. + * 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. */ -static char nullstr[1]; /* zero length string */ -static const char spcstr[] = " "; -static const char snlfmt[] = "%s\n"; +int +shell_main(argc, argv) + int argc; + char **argv; +{ + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + const char *shinit; -/* - * equal - #defined in mystring.h - */ + BLTINCMD = find_builtin("builtin"); + EXECCMD = find_builtin("exec"); + EVALCMD = find_builtin("eval"); -/* - * scopy - #defined in mystring.h - */ +#ifndef BB_FEATURE_SH_FANCY_PROMPT + unsetenv("PS1"); + unsetenv("PS2"); +#endif + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif +#if defined(linux) || defined(__GNU__) + signal(SIGCHLD, SIG_DFL); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + INTOFF; + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + switch (exception) { + case EXSHELLPROC: + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + break; + case EXEXEC: + exitstatus = exerrno; + break; -#if 0 -/* - * scopyn - copy a string from "from" to "to", truncating the string - * if necessary. "To" is always nul terminated, even if - * truncation is performed. "Size" is the size of "to". - */ + case EXERROR: + exitstatus = 2; + break; -static void -scopyn(from, to, size) - char const *from; - char *to; - int size; - { + default: + break; + } - while (--size > 0) { - if ((*to++ = *from++) == '\0') - return; + if (exception != EXSHELLPROC) { + if (state == 0 || iflag == 0 || ! rootshell) + exitshell(exitstatus); + } + reset(); + if (exception == EXINT) { + out2c('\n'); + } + 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; } - *to = '\0'; -} + 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; +#ifndef linux + if (getuid() == geteuid() && getgid() == getegid()) { +#endif + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + state = 3; + read_profile(shinit); + } +#ifndef linux + } +#endif +state3: + state = 4; + if (sflag == 0 || minusc) { + static int sigs[] = { + SIGINT, SIGQUIT, SIGHUP, +#ifdef SIGTSTP + SIGTSTP, #endif + SIGPIPE + }; +#define SIGSSIZE (sizeof(sigs)/sizeof(sigs[0])) + int i; + for (i = 0; i < SIGSSIZE; i++) + setsignal(sigs[i]); + } -/* - * prefix -- see if pfx is a prefix of string. - */ + if (minusc) + evalstring(minusc, 0); -static int -prefix(pfx, string) - char const *pfx; - char const *string; - { - while (*pfx) { - if (*pfx++ != *string++) - return 0; + if (sflag || minusc == NULL) { +state4: /* XXX ??? - why isn't this before the "if" statement */ + cmdloop(1); } - return 1; +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); + /* NOTREACHED */ } /* - * Convert a string of digits to an integer, printing an error message on - * failure. + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. */ -static int -number(s) - const char *s; - { +static void +cmdloop(int top) +{ + union node *n; + struct stackmark smark; + int inter; + int numeof = 0; - if (! is_number(s)) - error("Illegal number: %s", s); - return atoi(s); + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushall(); + } + 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); + setstackmark(&smark); + if (evalskip == SKIPFILE) { + evalskip = 0; + break; + } + } + popstackmark(&smark); } /* - * Check for a valid number. This should be elsewhere. + * Read /etc/profile or .profile. Return on error. */ -static int -is_number(p) - const char *p; - { - do { - if (! is_digit(*p)) - return 0; - } while (*++p != '\0'); - return 1; +static void +read_profile(name) + const char *name; +{ + int fd; + int xflag_set = 0; + int vflag_set = 0; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + /* -q turns off -x and -v just when executing init files */ + if (qflag) { + if (xflag) + xflag = 0, xflag_set = 1; + if (vflag) + vflag = 0, vflag_set = 1; + } + cmdloop(0); + if (qflag) { + if (xflag_set) + xflag = 1; + if (vflag_set) + vflag = 1; + } + popfile(); } + /* - * Produce a possibly single quoted string suitable as input to the shell. - * The return string is allocated on the stack. + * Read a file containing shell functions. */ -static char * -single_quote(const char *s) { - char *p; +static void +readcmdfile(const char *name) +{ + int fd; - STARTSTACKSTR(p); + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} - do { - char *q = p; - size_t len1, len1p, len2, len2p; - len1 = strcspn(s, "'"); - len2 = strspn(s + len1, "'"); - len1p = len1 ? len1 + 2 : len1; - switch (len2) { - case 0: - len2p = 0; - break; - case 1: - len2p = 2; - break; - default: - len2p = len2 + 2; - } +/* + * Take commands from a file. To be compatable we should do a path + * search for the file, which is necessary to find sub-commands. + */ - CHECKSTRSPACE(len1p + len2p + 1, p); - if (len1) { - *p = '\''; -#ifdef _GNU_SOURCE - q = mempcpy(p + 1, s, len1); -#else - q = p + 1 + len1; - memcpy(p + 1, s, len1); -#endif - *q++ = '\''; - s += len1; - } +static inline char * +find_dot_file(mybasename) + char *mybasename; +{ + char *fullname; + const char *path = pathval(); + struct stat statb; - switch (len2) { - case 0: - break; - case 1: - *q++ = '\\'; - *q = '\''; - s++; - break; - default: - *q = '"'; -#ifdef _GNU_SOURCE - *(char *) mempcpy(q + 1, s, len2) = '"'; -#else - q += 1 + len2; - memcpy(q + 1, s, len2); - *q = '"'; -#endif - s += len2; + /* don't try this for absolute or relative paths */ + if (strchr(mybasename, '/')) + return mybasename; + + while ((fullname = padvance(&path, mybasename)) != NULL) { + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; } + stunalloc(fullname); + } - STADJUST(len1p + len2p, p); - } while (*s); + /* not found in the PATH */ + error("%s: not found", mybasename); + /* NOTREACHED */ +} - USTPUTC(0, p); +static int +dotcmd(argc, argv) + int argc; + char **argv; +{ + struct strlist *sp; + exitstatus = 0; - return grabstackstr(p); -} + for (sp = cmdenviron; sp ; sp = sp->next) + setvareq(savestr(sp->text), VSTRFIXED|VTEXTFIXED); -/* - * Like strdup but works with the ash stack. - */ + if (argc >= 2) { /* That's what SVR2 does */ + char *fullname; + struct stackmark smark; -static char * -sstrdup(const char *p) -{ - size_t len = strlen(p) + 1; - return memcpy(stalloc(len), p, len); + setstackmark(&smark); + fullname = find_dot_file(argv[1]); + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + popstackmark(&smark); + } + return exitstatus; } -/* - * Wrapper around strcmp for qsort/bsearch/... - */ + static int -pstrcmp(const void *a, const void *b) +exitcmd(argc, argv) + int argc; + char **argv; { - return strcmp(*(const char *const *) a, *(const char *const *) b); + if (stoppedjobs()) + return 0; + if (argc > 1) + exitstatus = number(argv[1]); + else + exitstatus = oexitstatus; + exitshell(exitstatus); + /* NOTREACHED */ } -/* - * Find a string is in a sorted array. - */ -static const char *const * -findstring(const char *s, const char *const *array, size_t nmemb) +static pointer +stalloc(int nbytes) { - return bsearch(&s, array, nmemb, sizeof(const char *), pstrcmp); + char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; } -/* - * This file was generated by the mknodes program. - */ -/* $NetBSD: nodes.c.pat,v 1.8 1997/04/11 23:03:09 christos Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 - */ - -/* - * Routine for dealing with parsed shell commands. - */ +static void +stunalloc(pointer p) +{ +#ifdef DEBUG + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } +#endif + if (!(stacknxt >= (char *)p && (char *)p >= stackp->space)) { + p = stackp->space; + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} -static int funcblocksize; /* size of structures in function */ -static int funcstringsize; /* size of strings in node */ -pointer funcblock; /* block to allocate function from */ -static char *funcstring; /* block to allocate strings from */ -static const short nodesize[26] = { - ALIGN(sizeof (struct nbinary)), - ALIGN(sizeof (struct ncmd)), - ALIGN(sizeof (struct npipe)), - ALIGN(sizeof (struct nredir)), - ALIGN(sizeof (struct nredir)), - ALIGN(sizeof (struct nredir)), - ALIGN(sizeof (struct nbinary)), - ALIGN(sizeof (struct nbinary)), - ALIGN(sizeof (struct nif)), - ALIGN(sizeof (struct nbinary)), - ALIGN(sizeof (struct nbinary)), - ALIGN(sizeof (struct nfor)), - ALIGN(sizeof (struct ncase)), - ALIGN(sizeof (struct nclist)), - ALIGN(sizeof (struct narg)), - ALIGN(sizeof (struct narg)), - ALIGN(sizeof (struct nfile)), - ALIGN(sizeof (struct nfile)), - ALIGN(sizeof (struct nfile)), - ALIGN(sizeof (struct nfile)), - ALIGN(sizeof (struct nfile)), - ALIGN(sizeof (struct ndup)), - ALIGN(sizeof (struct ndup)), - ALIGN(sizeof (struct nhere)), - ALIGN(sizeof (struct nhere)), - ALIGN(sizeof (struct nnot)), -}; +static void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + mark->marknext = markp; + markp = mark; +} -static void calcsize __P((union node *)); -static void sizenodelist __P((struct nodelist *)); -static union node *copynode __P((union node *)); -static struct nodelist *copynodelist __P((struct nodelist *)); -static char *nodesavestr __P((char *)); +static void +popstackmark(struct stackmark *mark) +{ + struct stack_block *sp; + INTOFF; + markp = mark->marknext; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + INTON; +} /* - * Make a copy of a parse tree. + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. */ -union node * -copyfunc(n) - union node *n; -{ - if (n == NULL) - return NULL; - funcblocksize = 0; - funcstringsize = 0; - calcsize(n); - funcblock = ckmalloc(funcblocksize + funcstringsize); - funcstring = (char *) funcblock + funcblocksize; - return copynode(n); -} - - - static void -calcsize(n) - union node *n; -{ - if (n == NULL) - return; - funcblocksize += nodesize[n->type]; - switch (n->type) { - case NSEMI: - case NAND: - case NOR: - case NWHILE: - case NUNTIL: - calcsize(n->nbinary.ch2); - calcsize(n->nbinary.ch1); - break; - case NCMD: - calcsize(n->ncmd.redirect); - calcsize(n->ncmd.args); - calcsize(n->ncmd.assign); - break; - case NPIPE: - sizenodelist(n->npipe.cmdlist); - break; - case NREDIR: - case NBACKGND: - case NSUBSHELL: - calcsize(n->nredir.redirect); - calcsize(n->nredir.n); - break; - case NIF: - calcsize(n->nif.elsepart); - calcsize(n->nif.ifpart); - calcsize(n->nif.test); - break; - case NFOR: - funcstringsize += strlen(n->nfor.var) + 1; - calcsize(n->nfor.body); - calcsize(n->nfor.args); - break; - case NCASE: - calcsize(n->ncase.cases); - calcsize(n->ncase.expr); - break; - case NCLIST: - calcsize(n->nclist.body); - calcsize(n->nclist.pattern); - calcsize(n->nclist.next); - break; - case NDEFUN: - case NARG: - sizenodelist(n->narg.backquote); - funcstringsize += strlen(n->narg.text) + 1; - calcsize(n->narg.next); - break; - case NTO: - case NFROM: - case NFROMTO: - case NAPPEND: - case NTOOV: - calcsize(n->nfile.fname); - calcsize(n->nfile.next); - break; - case NTOFD: - case NFROMFD: - calcsize(n->ndup.vname); - calcsize(n->ndup.next); - break; - case NHERE: - case NXHERE: - calcsize(n->nhere.doc); - calcsize(n->nhere.next); - break; - case NNOT: - calcsize(n->nnot.com); - break; - }; +growstackblock(void) { + char *p; + int newlen = ALIGN(stacknleft * 2 + 100); + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + struct stack_block *oldstackp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + oldstackp = stackp; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + { + /* Stack marks pointing to the start of the old block + * must be relocated to point to the new block + */ + struct stackmark *xmark; + xmark = markp; + while (xmark != NULL && xmark->stackp == oldstackp) { + xmark->stackp = stackp; + xmark->stacknxt = stacknxt; + xmark->stacknleft = stacknleft; + xmark = xmark->marknext; + } + } + INTON; + } else { + p = stalloc(newlen); + memcpy(p, oldspace, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } } -static void -sizenodelist(lp) - struct nodelist *lp; +static inline void +grabstackblock(int len) { - while (lp) { - funcblocksize += ALIGN(sizeof(struct nodelist)); - calcsize(lp->n); - lp = lp->next; - } + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; } -static union node * -copynode(n) - union node *n; -{ - union node *new; - - if (n == NULL) - return NULL; - new = funcblock; - funcblock = (char *) funcblock + nodesize[n->type]; - switch (n->type) { - case NSEMI: - case NAND: - case NOR: - case NWHILE: - case NUNTIL: - new->nbinary.ch2 = copynode(n->nbinary.ch2); - new->nbinary.ch1 = copynode(n->nbinary.ch1); - break; - case NCMD: - new->ncmd.redirect = copynode(n->ncmd.redirect); - new->ncmd.args = copynode(n->ncmd.args); - new->ncmd.assign = copynode(n->ncmd.assign); - new->ncmd.backgnd = n->ncmd.backgnd; - break; - case NPIPE: - new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); - new->npipe.backgnd = n->npipe.backgnd; - break; - case NREDIR: - case NBACKGND: - case NSUBSHELL: - new->nredir.redirect = copynode(n->nredir.redirect); - new->nredir.n = copynode(n->nredir.n); - break; - case NIF: - new->nif.elsepart = copynode(n->nif.elsepart); - new->nif.ifpart = copynode(n->nif.ifpart); - new->nif.test = copynode(n->nif.test); - break; - case NFOR: - new->nfor.var = nodesavestr(n->nfor.var); - new->nfor.body = copynode(n->nfor.body); - new->nfor.args = copynode(n->nfor.args); - break; - case NCASE: - new->ncase.cases = copynode(n->ncase.cases); - new->ncase.expr = copynode(n->ncase.expr); - break; - case NCLIST: - new->nclist.body = copynode(n->nclist.body); - new->nclist.pattern = copynode(n->nclist.pattern); - new->nclist.next = copynode(n->nclist.next); - break; - case NDEFUN: - case NARG: - new->narg.backquote = copynodelist(n->narg.backquote); - new->narg.text = nodesavestr(n->narg.text); - new->narg.next = copynode(n->narg.next); - break; - case NTO: - case NFROM: - case NFROMTO: - case NAPPEND: - case NTOOV: - new->nfile.fname = copynode(n->nfile.fname); - new->nfile.fd = n->nfile.fd; - new->nfile.next = copynode(n->nfile.next); - break; - case NTOFD: - case NFROMFD: - new->ndup.vname = copynode(n->ndup.vname); - new->ndup.dupfd = n->ndup.dupfd; - new->ndup.fd = n->ndup.fd; - new->ndup.next = copynode(n->ndup.next); - break; - case NHERE: - case NXHERE: - new->nhere.doc = copynode(n->nhere.doc); - new->nhere.fd = n->nhere.fd; - new->nhere.next = copynode(n->nhere.next); - break; - case NNOT: - new->nnot.com = copynode(n->nnot.com); - break; - }; - new->type = n->type; - return new; -} +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ -static struct nodelist * -copynodelist(lp) - struct nodelist *lp; -{ - struct nodelist *start; - struct nodelist **lpp; - - lpp = &start; - while (lp) { - *lpp = funcblock; - funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist)); - (*lpp)->n = copynode(lp->n); - lp = lp->next; - lpp = &(*lpp)->next; +static char * +growstackstr(void) { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); } - *lpp = NULL; - return start; + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; } +/* + * Called from CHECKSTRSPACE. + */ static char * -nodesavestr(s) - char *s; -{ -#ifdef _GNU_SOURCE - char *rtn = funcstring; - - funcstring = stpcpy(funcstring, s) + 1; - return rtn; -#else - register char *p = s; - register char *q = funcstring; - char *rtn = funcstring; - - while ((*q++ = *p++) != '\0') - continue; - funcstring = q; - return rtn; -#endif +makestrspace(size_t newlen) { + int len = stackblocksize() - sstrnleft; + do { + growstackblock(); + sstrnleft = stackblocksize() - len; + } while (sstrnleft < newlen); + return stackblock() + len; } -/* - * Free a parse tree. - */ - static void -freefunc(n) - union node *n; +ungrabstackstr(char *s, char *p) { - if (n) - ckfree(n); + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); } -/* $NetBSD: options.c,v 1.31 2001/02/26 13:06:43 wiz Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. +/* + * Miscelaneous builtins. */ -struct optent optlist[NOPTS] = { - { "errexit", 'e', 0 }, - { "noglob", 'f', 0 }, - { "ignoreeof", 'I', 0 }, - { "interactive",'i', 0 }, - { "monitor", 'm', 0 }, - { "noexec", 'n', 0 }, - { "stdin", 's', 0 }, - { "xtrace", 'x', 0 }, - { "verbose", 'v', 0 }, - { "vi", 'V', 0 }, - { "emacs", 'E', 0 }, - { "noclobber", 'C', 0 }, - { "allexport", 'a', 0 }, - { "notify", 'b', 0 }, - { "nounset", 'u', 0 }, - { "quietprofile", 'q', 0 }, -}; -static char *arg0; /* value of $0 */ -struct shparam shellparam; /* current positional parameters */ -static char **argptr; /* argument list for builtin commands */ -static char *optionarg; /* set by nextopt (like getopt) */ -static char *optptr; /* used by nextopt */ - -static char *minusc; /* argument to -c option */ +#undef rflag +//#ifdef __GLIBC__ +static mode_t getmode(const void *, mode_t); +static void *setmode(const char *); +//#endif -static void options __P((int)); -static void minus_o __P((char *, int)); -static void setoption __P((int, int)); -#ifdef ASH_GETOPTS -static int getopts __P((char *, char *, char **, int *, int *)); +#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 +typedef long rlim_t; #endif + /* - * Process the shell command line arguments. + * The read builtin. The -e option causes backslashes to escape the + * following character. + * + * This uses unbuffered input, which may be avoidable in some cases. */ -static void -procargs(argc, argv) +static int +readcmd(argc, argv) int argc; char **argv; { + char **ap; + int backslash; + char c; + int rflag; + char *prompt; + const char *ifs; + char *p; + int startword; + int status; int i; - argptr = argv; - if (argc > 0) - argptr++; - for (i = 0; i < NOPTS; i++) - optlist[i].val = 2; - options(1); - if (*argptr == NULL && minusc == NULL) - sflag = 1; - if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) - iflag = 1; - if (mflag == 2) - mflag = iflag; - for (i = 0; i < NOPTS; i++) - if (optlist[i].val == 2) - optlist[i].val = 0; - arg0 = argv[0]; - if (sflag == 0 && minusc == NULL) { - commandname = argv[0]; - arg0 = *argptr++; - setinputfile(arg0, 0); - commandname = arg0; + rflag = 0; + prompt = NULL; + while ((i = nextopt("p:r")) != '\0') { + if (i == 'p') + prompt = optionarg; + else + rflag = 1; } - /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ - if (argptr && minusc && *argptr) - arg0 = *argptr++; - - shellparam.p = argptr; - shellparam.optind = 1; - shellparam.optoff = -1; - /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ - while (*argptr) { - shellparam.nparam++; - argptr++; + if (prompt && isatty(0)) { + putprompt(prompt); + flushall(); } - optschanged(); -} - - -static void -optschanged() -{ - setinteractive(iflag); - setjobctl(mflag); -} - -/* - * Process shell options. The global variable argptr contains a pointer - * to the argument list; we advance it past the options. - */ - -static void -options(cmdline) - int cmdline; -{ - char *p; - int val; - int c; - - if (cmdline) - minusc = NULL; - while ((p = *argptr) != NULL) { - argptr++; - if ((c = *p++) == '-') { - val = 1; - if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { - if (!cmdline) { - /* "-" means turn off -x and -v */ - if (p[0] == '\0') - xflag = vflag = 0; - /* "--" means reset params */ - else if (*argptr == NULL) - setparam(argptr); - } - break; /* "-" or "--" terminates options */ - } - } else if (c == '+') { - val = 0; - } else { - argptr--; + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS")) == NULL) + ifs = defifs; + status = 0; + startword = 1; + backslash = 0; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; break; } - while ((c = *p++) != '\0') { - if (c == 'c' && cmdline) { - char *q; -#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ - if (*p == '\0') -#endif - q = *argptr++; - if (q == NULL || minusc != NULL) - error("Bad -c option"); - minusc = q; -#ifdef NOHACK + if (c == '\0') + continue; + if (backslash) { + backslash = 0; + if (c != '\n') + STPUTC(c, p); + continue; + } + if (!rflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; break; -#endif - } else if (c == 'o') { - minus_o(*argptr, val); - if (*argptr) - argptr++; - } else { - setoption(c, val); } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); } } + STACKSTRNUL(p); + /* Remove trailing blanks */ + while (stackblock() <= --p && strchr(ifs, *p) != NULL) + *p = '\0'; + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; } -static void -minus_o(name, val) - char *name; - int val; -{ - int i; - - if (name == NULL) { - out1str("Current option settings\n"); - for (i = 0; i < NOPTS; i++) - out1fmt("%-16s%s\n", optlist[i].name, - optlist[i].val ? "on" : "off"); - } else { - for (i = 0; i < NOPTS; i++) - if (equal(name, optlist[i].name)) { - setoption(optlist[i].letter, val); - return; - } - error("Illegal option -o %s", name); - } -} -static void -setoption(flag, val) - char flag; - int val; - { +static int +umaskcmd(argc, argv) + int argc; + char **argv; +{ + char *ap; + int mask; int i; + int symbolic_mode = 0; - for (i = 0; i < NOPTS; i++) - if (optlist[i].letter == flag) { - optlist[i].val = val; - if (val) { - /* #%$ hack for ksh semantics */ - if (flag == 'V') - Eflag = 0; - else if (flag == 'E') - Vflag = 0; - } - return; - } - error("Illegal option -%c", flag); - /* NOTREACHED */ -} - + while (nextopt("S") != '\0') { + symbolic_mode = 1; + } + INTOFF; + mask = umask(0); + umask(mask); + INTON; -#ifdef mkinit -SHELLPROC { - int i; + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; - for (i = 0; i < NOPTS; i++) - optlist[i].val = 0; - optschanged(); + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; -} -#endif + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; -/* - * Set the shell parameters. - */ + printf("u=%s,g=%s,o=%s\n", u, g, o); + } else { + printf("%.4o\n", mask); + } + } else { + if (is_digit((unsigned char)*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; -static void -setparam(argv) - char **argv; - { - char **newparam; - char **ap; - int nparam; + INTOFF; + if ((set = setmode(ap)) != 0) { + mask = getmode(set, ~mask & 0777); + ckfree(set); + } + INTON; + if (!set) + error("Illegal mode: %s", ap); - for (nparam = 0 ; argv[nparam] ; nparam++); - ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); - while (*argv) { - *ap++ = savestr(*argv++); + umask(~mask & 0777); + } } - *ap = NULL; - freeparam(&shellparam); - shellparam.malloc = 1; - shellparam.nparam = nparam; - shellparam.p = newparam; - shellparam.optind = 1; - shellparam.optoff = -1; + return 0; } - /* - * Free the list of positional parameters. + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. */ -static void -freeparam(param) - volatile struct shparam *param; - { - char **ap; - - if (param->malloc) { - for (ap = param->p ; *ap ; ap++) - ckfree(*ap); - ckfree(param->p); - } -} - - +struct limits { + const char *name; + int cmd; + int factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; -/* - * The shift builtin command. - */ +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "time(seconds)", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file(blocks)", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data(kbytes)", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack(kbytes)", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "coredump(blocks)", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "memory(kbytes)", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "process(processes)", RLIMIT_NPROC, 1, 'p' }, +#endif +#ifdef RLIMIT_NOFILE + { "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "vmemory(kbytes)", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap(kbytes)", RLIMIT_SWAP, 1024, 'w' }, +#endif + { (char *) 0, 0, 0, '\0' } +}; static int -shiftcmd(argc, argv) +ulimitcmd(argc, argv) int argc; char **argv; { - int n; - char **ap1, **ap2; + int c; + rlim_t val = 0; + enum { SOFT = 0x1, HARD = 0x2 } + how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; - n = 1; - if (argc > 1) - n = number(argv[1]); - if (n > shellparam.nparam) - error("can't shift that many"); - INTOFF; - shellparam.nparam -= n; - for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { - if (shellparam.malloc) - ckfree(*ap1); + what = 'f'; + while ((optc = nextopt("HSatfdsmcnpl")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strcmp(p, "unlimited") == 0) + val = RLIM_INFINITY; + else { + val = (rlim_t) 0; + + while ((c = *p++) >= '0' && c <= '9') + { + val = (val * 10) + (long)(c - '0'); + if (val < (rlim_t) 0) + break; + } + if (c) + error("bad number"); + val *= l->factor; + } } - ap2 = shellparam.p; - while ((*ap2++ = *ap1++) != NULL); - shellparam.optind = 1; - shellparam.optoff = -1; - INTON; - return 0; -} + if (all) { + for (l = limits; l->name; l++) { + getrlimit(l->cmd, &limit); + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + printf("%-20s ", l->name); + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + return 0; + } + getrlimit(l->cmd, &limit); + if (set) { + if (how & HARD) + limit.rlim_max = val; + if (how & SOFT) + limit.rlim_cur = val; + if (setrlimit(l->cmd, &limit) < 0) + error("error setting limit (%m)"); + } else { + if (how & SOFT) + val = limit.rlim_cur; + else if (how & HARD) + val = limit.rlim_max; + if (val == RLIM_INFINITY) + printf("unlimited\n"); + else + { + val /= l->factor; + printf("%lld\n", (long long) val); + } + } + return 0; +} /* - * The set command builtin. + * prefix -- see if pfx is a prefix of string. */ static int -setcmd(argc, argv) - int argc; - char **argv; +prefix(char const *pfx, char const *string) { - if (argc == 1) - return showvarscmd(argc, argv); - INTOFF; - options(0); - optschanged(); - if (*argptr != NULL) { - setparam(argptr); + while (*pfx) { + if (*pfx++ != *string++) + return 0; } - INTON; - return 0; + return 1; } +/* + * Return true if s is a string of digits, and save munber in intptr + * nagative is bad + */ -static void -getoptsreset(value) - const char *value; +static int +is_number(const char *p, int *intptr) { - shellparam.optind = number(value); - shellparam.optoff = -1; + int ret = 0; + + do { + if (! is_digit(*p)) + return 0; + ret *= 10; + ret += digit_val(*p); + p++; + } while (*p != '\0'); + + *intptr = ret; + return 1; } -#ifdef ASH_GETOPTS /* - * The getopts builtin. Shellparam.optnext points to the next argument - * to be processed. Shellparam.optptr points to the next character to - * be processed in the current argument. If shellparam.optnext is NULL, - * then it's the first time getopts has been called. + * Convert a string of digits to an integer, printing an error message on + * failure. */ static int -getoptscmd(argc, argv) - int argc; - char **argv; +number(const char *s) { - char **optbase; + int i; + if (! is_number(s, &i)) + error("Illegal number: %s", s); + return i; +} - if (argc < 3) - error("Usage: getopts optstring var [arg]"); - else if (argc == 3) { - optbase = shellparam.p; - if (shellparam.optind > shellparam.nparam + 1) { - shellparam.optind = 1; - shellparam.optoff = -1; +/* + * Produce a possibly single quoted string suitable as input to the shell. + * The return string is allocated on the stack. + */ + +static char * +single_quote(const char *s) { + char *p; + + STARTSTACKSTR(p); + + do { + char *q = p; + size_t len1, len1p, len2, len2p; + + len1 = strcspn(s, "'"); + len2 = strspn(s + len1, "'"); + + len1p = len1 ? len1 + 2 : len1; + switch (len2) { + case 0: + len2p = 0; + break; + case 1: + len2p = 2; + break; + default: + len2p = len2 + 2; } - } - else { - optbase = &argv[3]; - if (shellparam.optind > argc - 2) { - shellparam.optind = 1; - shellparam.optoff = -1; + + CHECKSTRSPACE(len1p + len2p + 1, p); + + if (len1) { + *p = '\''; + q = p + 1 + len1; + memcpy(p + 1, s, len1); + *q++ = '\''; + s += len1; } - } - return getopts(argv[1], argv[2], optbase, &shellparam.optind, - &shellparam.optoff); + switch (len2) { + case 0: + break; + case 1: + *q++ = '\\'; + *q = '\''; + s++; + break; + default: + *q = '"'; + q += 1 + len2; + memcpy(q + 1, s, len2); + *q = '"'; + s += len2; + } + + STADJUST(len1p + len2p, p); + } while (*s); + + USTPUTC(0, p); + + return grabstackstr(p); } /* - * Safe version of setvar, returns 1 on success 0 on failure. + * Like strdup but works with the ash stack. */ -static int -setvarsafe(name, val, flags) - const char *name, *val; - int flags; +static char * +sstrdup(const char *p) { - struct jmploc jmploc; - struct jmploc *volatile savehandler = handler; - int err = 0; -#ifdef __GNUC__ - (void) &err; -#endif - - if (setjmp(jmploc.loc)) - err = 1; - else { - handler = &jmploc; - setvar(name, val, flags); - } - handler = savehandler; - return err; + size_t len = strlen(p) + 1; + return memcpy(stalloc(len), p, len); } -static int -getopts(optstr, optvar, optfirst, myoptind, optoff) - char *optstr; - char *optvar; - char **optfirst; - int *myoptind; - int *optoff; -{ - char *p, *q; - char c = '?'; - int done = 0; - int err = 0; - char s[10]; - char **optnext = optfirst + *myoptind - 1; - - if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) || - strlen(*(optnext - 1)) < *optoff) - p = NULL; - else - p = *(optnext - 1) + *optoff; - if (p == NULL || *p == '\0') { - /* Current word is done, advance */ - if (optnext == NULL) - return 1; - p = *optnext; - if (p == NULL || *p != '-' || *++p == '\0') { -atend: - *myoptind = optnext - optfirst + 1; - p = NULL; - done = 1; - goto out; - } - optnext++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - goto atend; - } - - c = *p++; - for (q = optstr; *q != c; ) { - if (*q == '\0') { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - } - else { - outfmt(&errout, "Illegal option -%c\n", c); - (void) unsetvar("OPTARG"); - } - c = '?'; - goto bad; - } - if (*++q == ':') - q++; - } - - if (*++q == ':') { - if (*p == '\0' && (p = *optnext) == NULL) { - if (optstr[0] == ':') { - s[0] = c; - s[1] = '\0'; - err |= setvarsafe("OPTARG", s, 0); - c = ':'; - } - else { - outfmt(&errout, "No arg for -%c option\n", c); - (void) unsetvar("OPTARG"); - c = '?'; - } - goto bad; - } - - if (p == *optnext) - optnext++; - setvarsafe("OPTARG", p, 0); - p = NULL; - } - else - setvarsafe("OPTARG", "", 0); - *myoptind = optnext - optfirst + 1; - goto out; - -bad: - *myoptind = 1; - p = NULL; -out: - *optoff = p ? p - *(optnext - 1) : -1; - fmtstr(s, sizeof(s), "%d", *myoptind); - err |= setvarsafe("OPTIND", s, VNOFUNC); - s[0] = c; - s[1] = '\0'; - err |= setvarsafe(optvar, s, 0); - if (err) { - *myoptind = 1; - *optoff = -1; - flushall(); - exraise(EXERROR); - } - return done; -} -#endif - -/* - * XXX - should get rid of. have all builtins use getopt(3). the - * library getopt must have the BSD extension static variable "optreset" - * otherwise it can't be used within the shell safely. - * - * Standard option processing (a la getopt) for builtin routines. The - * only argument that is passed to nextopt is the option string; the - * other arguments are unnecessary. It return the character, or '\0' on - * end of input. - */ - -static int -nextopt(optstring) - const char *optstring; - { - char *p; - const char *q; - char c; - - if ((p = optptr) == NULL || *p == '\0') { - p = *argptr; - if (p == NULL || *p != '-' || *++p == '\0') - return '\0'; - argptr++; - if (p[0] == '-' && p[1] == '\0') /* check for "--" */ - return '\0'; - } - c = *p++; - for (q = optstring ; *q != c ; ) { - if (*q == '\0') - error("Illegal option -%c", c); - if (*++q == ':') - q++; - } - if (*++q == ':') { - if (*p == '\0' && (p = *argptr++) == NULL) - error("No arg for -%c option", c); - optionarg = p; - p = NULL; - } - optptr = p; - return c; -} - - -/* $NetBSD: output.c,v 1.23 2001/01/07 23:39:07 lukem Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ /* - * Shell output routines. We use our own output routines because: - * When a builtin command is interrupted we have to discard - * any pending output. - * When a builtin command appears in back quotes, we want to - * save the output of the command in a region obtained - * via malloc, rather than doing a fork and reading the - * output of the command via a pipe. - * Our output routines may be smaller than the stdio routines. + * Routine for dealing with parsed shell commands. */ -#define OUTBUFSIZ BUFSIZ -#define MEM_OUT -3 /* output to dynamically allocated memory */ - - -#ifdef USE_GLIBC_STDIO -struct output output = {NULL, NULL, 0, NULL, 0, 1, 0}; -struct output errout = {NULL, NULL, 0, NULL, 0, 2, 0}; -struct output memout = {NULL, NULL, 0, NULL, 0, MEM_OUT, 0}; -#else -struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; -struct output errout = {NULL, 0, NULL, 0, 2, 0}; -struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; -#endif -struct output *out1 = &output; -struct output *out2 = &errout; - - -#ifndef USE_GLIBC_STDIO -static void __outstr __P((const char *, size_t, struct output*)); -#endif - - -#ifdef mkinit - -INCLUDE "output.h" -INCLUDE "memalloc.h" +static void sizenodelist (const struct nodelist *); +static struct nodelist *copynodelist (const struct nodelist *); +static char *nodesavestr (const char *); -INIT { -#ifdef USE_GLIBC_STDIO - initstreams(); -#endif +static void +calcsize(const union node *n) +{ + if (n == NULL) + return; + funcblocksize += nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + calcsize(n->nbinary.ch2); + calcsize(n->nbinary.ch1); + break; + case NCMD: + calcsize(n->ncmd.redirect); + calcsize(n->ncmd.args); + calcsize(n->ncmd.assign); + break; + case NPIPE: + sizenodelist(n->npipe.cmdlist); + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + calcsize(n->nredir.redirect); + calcsize(n->nredir.n); + break; + case NIF: + calcsize(n->nif.elsepart); + calcsize(n->nif.ifpart); + calcsize(n->nif.test); + break; + case NFOR: + funcstringsize += strlen(n->nfor.var) + 1; + calcsize(n->nfor.body); + calcsize(n->nfor.args); + break; + case NCASE: + calcsize(n->ncase.cases); + calcsize(n->ncase.expr); + break; + case NCLIST: + calcsize(n->nclist.body); + calcsize(n->nclist.pattern); + calcsize(n->nclist.next); + break; + case NDEFUN: + case NARG: + sizenodelist(n->narg.backquote); + funcstringsize += strlen(n->narg.text) + 1; + calcsize(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + calcsize(n->nfile.fname); + calcsize(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + calcsize(n->ndup.vname); + calcsize(n->ndup.next); + break; + case NHERE: + case NXHERE: + calcsize(n->nhere.doc); + calcsize(n->nhere.next); + break; + case NNOT: + calcsize(n->nnot.com); + break; + }; } -RESET { - out1 = &output; - out2 = &errout; -#ifdef USE_GLIBC_STDIO - if (memout.stream != NULL) - __closememout(); -#endif - if (memout.buf != NULL) { - ckfree(memout.buf); - memout.buf = NULL; +static void +sizenodelist(const struct nodelist *lp) +{ + while (lp) { + funcblocksize += ALIGN(sizeof(struct nodelist)); + calcsize(lp->n); + lp = lp->next; } } -#endif - -#ifndef USE_GLIBC_STDIO -static void -__outstr(const char *p, size_t len, struct output *dest) { - if (!dest->bufsize) { - dest->nleft = 0; - } else if (dest->buf == NULL) { - if (len > dest->bufsize && dest->fd == MEM_OUT) { - dest->bufsize = len; - } - INTOFF; - dest->buf = ckmalloc(dest->bufsize); - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; - INTON; - } else if (dest->fd == MEM_OUT) { - int offset; +static union node * +copynode(const union node *n) +{ + union node *new; - offset = dest->bufsize; - INTOFF; - if (dest->bufsize >= len) { - dest->bufsize <<= 1; - } else { - dest->bufsize += len; - } - dest->buf = ckrealloc(dest->buf, dest->bufsize); - dest->nleft = dest->bufsize - offset; - dest->nextc = dest->buf + offset; - INTON; - } else { - flushout(dest); - } + if (n == NULL) + return NULL; + new = funcblock; + funcblock = (char *) funcblock + nodesize[n->type]; + switch (n->type) { + case NSEMI: + case NAND: + case NOR: + case NWHILE: + case NUNTIL: + new->nbinary.ch2 = copynode(n->nbinary.ch2); + new->nbinary.ch1 = copynode(n->nbinary.ch1); + break; + case NCMD: + new->ncmd.redirect = copynode(n->ncmd.redirect); + new->ncmd.args = copynode(n->ncmd.args); + new->ncmd.assign = copynode(n->ncmd.assign); + new->ncmd.backgnd = n->ncmd.backgnd; + break; + case NPIPE: + new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); + new->npipe.backgnd = n->npipe.backgnd; + break; + case NREDIR: + case NBACKGND: + case NSUBSHELL: + new->nredir.redirect = copynode(n->nredir.redirect); + new->nredir.n = copynode(n->nredir.n); + break; + case NIF: + new->nif.elsepart = copynode(n->nif.elsepart); + new->nif.ifpart = copynode(n->nif.ifpart); + new->nif.test = copynode(n->nif.test); + break; + case NFOR: + new->nfor.var = nodesavestr(n->nfor.var); + new->nfor.body = copynode(n->nfor.body); + new->nfor.args = copynode(n->nfor.args); + break; + case NCASE: + new->ncase.cases = copynode(n->ncase.cases); + new->ncase.expr = copynode(n->ncase.expr); + break; + case NCLIST: + new->nclist.body = copynode(n->nclist.body); + new->nclist.pattern = copynode(n->nclist.pattern); + new->nclist.next = copynode(n->nclist.next); + break; + case NDEFUN: + case NARG: + new->narg.backquote = copynodelist(n->narg.backquote); + new->narg.text = nodesavestr(n->narg.text); + new->narg.next = copynode(n->narg.next); + break; + case NTO: + case NFROM: + case NFROMTO: + case NAPPEND: + case NTOOV: + new->nfile.fname = copynode(n->nfile.fname); + new->nfile.fd = n->nfile.fd; + new->nfile.next = copynode(n->nfile.next); + break; + case NTOFD: + case NFROMFD: + new->ndup.vname = copynode(n->ndup.vname); + new->ndup.dupfd = n->ndup.dupfd; + new->ndup.fd = n->ndup.fd; + new->ndup.next = copynode(n->ndup.next); + break; + case NHERE: + case NXHERE: + new->nhere.doc = copynode(n->nhere.doc); + new->nhere.fd = n->nhere.fd; + new->nhere.next = copynode(n->nhere.next); + break; + case NNOT: + new->nnot.com = copynode(n->nnot.com); + break; + }; + new->type = n->type; + return new; +} - if (len < dest->nleft) { - dest->nleft -= len; - memcpy(dest->nextc, p, len); - dest->nextc += len; - return; - } - if (xwrite(dest->fd, p, len) < len) - dest->flags |= OUTPUT_ERR; +static struct nodelist * +copynodelist(const struct nodelist *lp) +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + funcblock = (char *) funcblock + ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; } -#endif -static void -outstr(p, file) - const char *p; - struct output *file; - { -#ifdef USE_GLIBC_STDIO - INTOFF; - fputs(p, file->stream); - INTON; +static char * +nodesavestr(const char *s) +{ +#ifdef _GNU_SOURCE + char *rtn = funcstring; + + funcstring = stpcpy(funcstring, s) + 1; + return rtn; #else - size_t len; + const char *p = s; + char *q = funcstring; + char *rtn = funcstring; - if (!*p) { - return; - } - len = strlen(p); - if ((file->nleft -= len) > 0) { - memcpy(file->nextc, p, len); - file->nextc += len; - return; - } - __outstr(p, len, file); + while ((*q++ = *p++) != '\0') + continue; + funcstring = q; + return rtn; #endif } +#ifdef ASH_GETOPTS +static int getopts (char *, char *, char **, int *, int *); +#endif -#ifndef USE_GLIBC_STDIO +/* + * Process the shell command line arguments. + */ static void -outcslow(c, dest) - char c; - struct output *dest; - { - __outstr(&c, 1, dest); -} -#endif +procargs(argc, argv) + int argc; + char **argv; +{ + int i; + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optent_val(i) = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag == 2 && sflag == 1 && isatty(0) && isatty(1)) + iflag = 1; + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optent_val(i) == 2) + optent_val(i) = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + commandname = argv[0]; + arg0 = *argptr++; + setinputfile(arg0, 0); + commandname = arg0; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (argptr && minusc && *argptr) + arg0 = *argptr++; -static void -flushall() { - flushout(&output); -#ifdef FLUSHERR - flushout(&errout); -#endif + shellparam.p = argptr; + shellparam.optind = 1; + shellparam.optoff = -1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); } -static void -flushout(dest) - struct output *dest; - { -#ifdef USE_GLIBC_STDIO - INTOFF; - fflush(dest->stream); - INTON; -#else - size_t len; - len = dest->nextc - dest->buf; - if (dest->buf == NULL || !len || dest->fd < 0) - return; - dest->nextc = dest->buf; - dest->nleft = dest->bufsize; - if (xwrite(dest->fd, dest->buf, len) < len) - dest->flags |= OUTPUT_ERR; -#endif -} +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ +static inline void +minus_o(const char *name, int val) +{ + int i; -static void -freestdout() { - if (output.buf) { - INTOFF; - ckfree(output.buf); - output.buf = NULL; - output.nleft = 0; - INTON; + if (name == NULL) { + out1str("Current option settings\n"); + for (i = 0; i < NOPTS; i++) + printf("%-16s%s\n", optent_name(optlist[i]), + optent_val(i) ? "on" : "off"); + } else { + for (i = 0; i < NOPTS; i++) + if (equal(name, optent_name(optlist[i]))) { + setoption(optent_letter(optlist[i]), val); + return; + } + error("Illegal option -o %s", name); } - output.flags = 0; } static void -#ifdef __STDC__ -outfmt(struct output *file, const char *fmt, ...) -#else -static void -outfmt(va_alist) - va_dcl -#endif +options(int cmdline) { - va_list ap; -#ifndef __STDC__ - struct output *file; - const char *fmt; + char *p; + int val; + int c; - va_start(ap); - file = va_arg(ap, struct output *); - fmt = va_arg(ap, const char *); -#else - va_start(ap, fmt); + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || (p[0] == '-' && p[1] == '\0')) { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') #endif - doformat(file, fmt, ap); - va_end(ap); + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else { + setoption(c, val); + } + } + } } static void -#ifdef __STDC__ -out1fmt(const char *fmt, ...) -#else -out1fmt(va_alist) - va_dcl -#endif +setoption(int flag, int val) { - va_list ap; -#ifndef __STDC__ - const char *fmt; + int i; - va_start(ap); - fmt = va_arg(ap, const char *); -#else - va_start(ap, fmt); -#endif - doformat(out1, fmt, ap); - va_end(ap); + for (i = 0; i < NOPTS; i++) + if (optent_letter(optlist[i]) == flag) { + optent_val(i) = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (flag == 'V') + Eflag = 0; + else if (flag == 'E') + Vflag = 0; + } + return; + } + error("Illegal option -%c", flag); + /* NOTREACHED */ } -static void -#ifdef __STDC__ -fmtstr(char *outbuf, size_t length, const char *fmt, ...) -#else -fmtstr(va_alist) - va_dcl -#endif -{ - va_list ap; -#ifndef __STDC__ - char *outbuf; - size_t length; - const char *fmt; - va_start(ap); - outbuf = va_arg(ap, char *); - length = va_arg(ap, size_t); - fmt = va_arg(ap, const char *); -#else - va_start(ap, fmt); -#endif - INTOFF; - vsnprintf(outbuf, length, fmt, ap); - INTON; -} -#ifndef USE_GLIBC_STDIO /* - * Formatted output. This routine handles a subset of the printf formats: - * - Formats supported: d, u, o, p, X, s, and c. - * - The x format is also accepted but is treated like X. - * - The l, ll and q modifiers are accepted. - * - The - and # flags are accepted; # only works with the o format. - * - Width and precision may be specified with any format except c. - * - An * may be given for the width or precision. - * - The obsolete practice of preceding the width with a zero to get - * zero padding is not supported; use the precision field. - * - A % may be printed by writing %% in the format string. + * Set the shell parameters. */ -#define TEMPSIZE 24 +static void +setparam(char **argv) +{ + char **newparam; + char **ap; + int nparam; -#ifdef BSD4_4 -#define HAVE_VASPRINTF 1 -#endif + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optind = 1; + shellparam.optoff = -1; +} -#if !HAVE_VASPRINTF -static const char digit[] = "0123456789ABCDEF"; -#endif +/* + * Free the list of positional parameters. + */ static void -doformat(dest, f, ap) - struct output *dest; - const char *f; /* format string */ - va_list ap; +freeparam(volatile struct shparam *param) { -#if HAVE_VASPRINTF - char *s, *t; - int len; - - INTOFF; - len = vasprintf(&t, f, ap); - if (len < 0) { - return; - } - s = stalloc(++len); - memcpy(s, t, len); - free(t); - INTON; - outstr(s, dest); - stunalloc(s); -#else /* !HAVE_VASPRINTF */ - char c; - char temp[TEMPSIZE]; - int flushleft; - int sharp; - int width; - int prec; - int islong; - int isquad; - char *p; - int sign; -#ifdef BSD4_4 - quad_t l; - u_quad_t num; -#else - long l; - u_long num; -#endif - unsigned base; - int len; - int size; - int pad; + char **ap; - while ((c = *f++) != '\0') { - if (c != '%') { - outc(c, dest); - continue; - } - flushleft = 0; - sharp = 0; - width = 0; - prec = -1; - islong = 0; - isquad = 0; - for (;;) { - if (*f == '-') - flushleft++; - else if (*f == '#') - sharp++; - else - break; - f++; - } - if (*f == '*') { - width = va_arg(ap, int); - f++; - } else { - while (is_digit(*f)) { - width = 10 * width + digit_val(*f++); - } - } - if (*f == '.') { - if (*++f == '*') { - prec = va_arg(ap, int); - f++; - } else { - prec = 0; - while (is_digit(*f)) { - prec = 10 * prec + digit_val(*f++); - } - } - } - if (*f == 'l') { - f++; - if (*f == 'l') { - isquad++; - f++; - } else - islong++; - } else if (*f == 'q') { - isquad++; - f++; - } - switch (*f) { - case 'd': -#ifdef BSD4_4 - if (isquad) - l = va_arg(ap, quad_t); - else -#endif - if (islong) - l = va_arg(ap, long); - else - l = va_arg(ap, int); - sign = 0; - num = l; - if (l < 0) { - num = -l; - sign = 1; - } - base = 10; - goto number; - case 'u': - base = 10; - goto uns_number; - case 'o': - base = 8; - goto uns_number; - case 'p': - outc('0', dest); - outc('x', dest); - /*FALLTHROUGH*/ - case 'x': - /* we don't implement 'x'; treat like 'X' */ - case 'X': - base = 16; -uns_number: /* an unsigned number */ - sign = 0; -#ifdef BSD4_4 - if (isquad) - num = va_arg(ap, u_quad_t); - else -#endif - if (islong) - num = va_arg(ap, unsigned long); - else - num = va_arg(ap, unsigned int); -number: /* process a number */ - p = temp + TEMPSIZE - 1; - *p = '\0'; - while (num) { - *--p = digit[num % base]; - num /= base; - } - len = (temp + TEMPSIZE - 1) - p; - if (prec < 0) - prec = 1; - if (sharp && *f == 'o' && prec <= len) - prec = len + 1; - pad = 0; - if (width) { - size = len; - if (size < prec) - size = prec; - size += sign; - pad = width - size; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - if (sign) - outc('-', dest); - prec -= len; - while (--prec >= 0) - outc('0', dest); - while (*p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 's': - p = va_arg(ap, char *); - pad = 0; - if (width) { - len = strlen(p); - if (prec >= 0 && len > prec) - len = prec; - pad = width - len; - if (flushleft == 0) { - while (--pad >= 0) - outc(' ', dest); - } - } - prec++; - while (--prec != 0 && *p) - outc(*p++, dest); - while (--pad >= 0) - outc(' ', dest); - break; - case 'c': - c = va_arg(ap, int); - outc(c, dest); - break; - default: - outc(*f, dest); - break; - } - f++; + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); } -#endif /* !HAVE_VASPRINTF */ } -#endif /* - * Version of write which resumes after a signal is caught. + * The shift builtin command. */ static int -xwrite(fd, buf, nbytes) - int fd; - const char *buf; - int nbytes; - { - int ntry; - int i; +shiftcmd(argc, argv) + int argc; + char **argv; +{ int n; + char **ap1, **ap2; - n = nbytes; - ntry = 0; - for (;;) { - i = write(fd, buf, n); - if (i > 0) { - if ((n -= i) <= 0) - return nbytes; - buf += i; - ntry = 0; - } else if (i == 0) { - if (++ntry > 10) - return nbytes - n; - } else if (errno != EINTR) { - return -1; - } + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + error("can't shift that many"); + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optind = 1; + shellparam.optoff = -1; + INTON; + return 0; } -#ifdef notdef + /* - * Version of ioctl that retries after a signal is caught. - * XXX unused function + * The set command builtin. */ static int -xioctl(fd, request, arg) - int fd; - unsigned long request; - char * arg; +setcmd(argc, argv) + int argc; + char **argv; { - int i; - - while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); - return i; -} -#endif - - -#ifdef USE_GLIBC_STDIO -static void initstreams() { - output.stream = stdout; - errout.stream = stderr; + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; } static void -openmemout() { - INTOFF; - memout.stream = open_memstream(&memout.buf, &memout.bufsize); - INTON; +getoptsreset(const char *value) +{ + shellparam.optind = number(value); + shellparam.optoff = -1; } - -static int -__closememout() { - int error; - error = fclose(memout.stream); - memout.stream = NULL; - return error; +#ifdef BB_LOCALE_SUPPORT +static void change_lc_all(const char *value) +{ + if(value != 0 && *value != 0) + setlocale(LC_ALL, value); } -#endif -/* $NetBSD: parser.c,v 1.46 2001/02/04 19:52:06 christos Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ +static void change_lc_ctype(const char *value) +{ + if(value != 0 && *value != 0) + setlocale(LC_CTYPE, value); +} +#endif +#ifdef ASH_GETOPTS /* - * Shell command parser. + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. */ -#define EOFMARKLEN 79 - +static int +getoptscmd(argc, argv) + int argc; + char **argv; +{ + char **optbase; + if (argc < 3) + error("Usage: getopts optstring var [arg]"); + else if (argc == 3) { + optbase = shellparam.p; + if (shellparam.optind > shellparam.nparam + 1) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } + else { + optbase = &argv[3]; + if (shellparam.optind > argc - 2) { + shellparam.optind = 1; + shellparam.optoff = -1; + } + } -struct heredoc { - struct heredoc *next; /* next here document in list */ - union node *here; /* redirection node */ - char *eofmark; /* string indicating end of input */ - int striptabs; /* if set, strip leading tabs */ -}; + return getopts(argv[1], argv[2], optbase, &shellparam.optind, + &shellparam.optoff); +} +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ +static int +setvarsafe(name, val, flags) + const char *name, *val; + int flags; +{ + struct jmploc jmploc; + struct jmploc *volatile savehandler = handler; + int err = 0; +#ifdef __GNUC__ + (void) &err; +#endif -struct heredoc *heredoclist; /* list of here documents to read */ -static int parsebackquote; /* nonzero if we are inside backquotes */ -static int doprompt; /* if set, prompt the user */ -static int needprompt; /* true if interactive and at start of line */ -static int lasttoken; /* last token read */ -static int tokpushback; /* last token pushed back */ -static char *wordtext; /* text of last word returned by readtoken */ -static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ -/* 1 == check for aliases, 2 == also check for assignments */ -static int checkalias; -struct nodelist *backquotelist; -union node *redirnode; + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + return err; +} + +static int +getopts(optstr, optvar, optfirst, myoptind, optoff) + char *optstr; + char *optvar; + char **optfirst; + int *myoptind; + int *optoff; +{ + char *p, *q; + char c = '?'; + int done = 0; + int err = 0; + char s[10]; + char **optnext = optfirst + *myoptind - 1; + + if (*myoptind <= 1 || *optoff < 0 || !(*(optnext - 1)) || + strlen(*(optnext - 1)) < *optoff) + p = NULL; + else + p = *(optnext - 1) + *optoff; + if (p == NULL || *p == '\0') { + /* Current word is done, advance */ + if (optnext == NULL) + return 1; + p = *optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + *myoptind = optnext - optfirst + 1; + p = NULL; + done = 1; + goto out; + } + optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + } + else { + out2fmt("Illegal option -%c\n", c); + (void) unsetvar("OPTARG"); + } + c = '?'; + goto bad; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = *optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + err |= setvarsafe("OPTARG", s, 0); + c = ':'; + } + else { + out2fmt("No arg for -%c option\n", c); + (void) unsetvar("OPTARG"); + c = '?'; + } + goto bad; + } + + if (p == *optnext) + optnext++; + setvarsafe("OPTARG", p, 0); + p = NULL; + } + else + setvarsafe("OPTARG", "", 0); + *myoptind = optnext - optfirst + 1; + goto out; + +bad: + *myoptind = 1; + p = NULL; +out: + *optoff = p ? p - *(optnext - 1) : -1; + snprintf(s, sizeof(s), "%d", *myoptind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *myoptind = 1; + *optoff = -1; + exraise(EXERROR); + } + return done; +} +#endif + +/* + * XXX - should get rid of. have all builtins use getopt(3). the + * library getopt must have the BSD extension static variable "optreset" + * otherwise it can't be used within the shell safely. + * + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +static int +nextopt(const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optionarg = p; + p = NULL; + } + optptr = p; + return c; +} + +static void +flushall() { + INTOFF; + fflush(stdout); + INTON; +} + + +static void +out2fmt(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +/* + * Version of write which resumes after a signal is caught. + */ + +static int +xwrite(int fd, const char *buf, int nbytes) +{ + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + +static struct heredoc *heredoclist; /* list of here documents to read */ +static int parsebackquote; /* nonzero if we are inside backquotes */ +static int doprompt; /* if set, prompt the user */ +static int needprompt; /* true if interactive and at start of line */ +static int lasttoken; /* last token read */ + +static char *wordtext; /* text of last word returned by readtoken */ + +static struct nodelist *backquotelist; +static union node *redirnode; struct heredoc *heredoc; -static int quoteflag; /* set if (part of) last token was quoted */ -static int startlinno; /* line # where last token started */ +static int quoteflag; /* set if (part of) last token was quoted */ +static int startlinno; /* line # where last token started */ -static union node *list __P((int)); -static union node *andor __P((void)); -static union node *pipeline __P((void)); -static union node *command __P((void)); -static union node *simplecmd __P((void)); -static union node *makename __P((void)); -static void parsefname __P((void)); -static void parseheredoc __P((void)); -static int peektoken __P((void)); -static int readtoken __P((void)); -static int xxreadtoken __P((void)); -static int readtoken1 __P((int, char const *, char *, int)); -static int noexpand __P((char *)); -static void synexpect __P((int)) __attribute__((noreturn)); -static void synerror __P((const char *)) __attribute__((noreturn)); -static void setprompt __P((int)); +static union node *list (int); +static union node *andor (void); +static union node *pipeline (void); +static union node *command (void); +static union node *simplecmd (void); +static void parsefname (void); +static void parseheredoc (void); +static int peektoken (void); +static int readtoken (void); +static int xxreadtoken (void); +static int readtoken1 (int, char const *, char *, int); +static int noexpand (char *); +static void synexpect (int) __attribute__((noreturn)); +static void synerror (const char *) __attribute__((noreturn)); +static void setprompt (int); /* @@ -9974,7 +9472,7 @@ static void setprompt __P((int)); * valid parse tree indicating a blank line.) */ -union node * +static union node * parsecmd(int interact) { int t; @@ -10054,7 +9552,7 @@ list(nlflag) if (heredoclist) parseheredoc(); else - pungetc(); /* push back EOF on input */ + pungetc(); /* push back EOF on input */ return n1; default: if (nlflag) @@ -10384,7 +9882,7 @@ simplecmd() { case TREDIR: *rpp = n = redirnode; rpp = &n->nfile.next; - parsefname(); /* read name of redirection file */ + parsefname(); /* read name of redirection file */ break; case TLP: if ( @@ -10394,10 +9892,6 @@ simplecmd() { /* We have a function */ if (readtoken() != TRP) synexpect(TRP); -#ifdef notdef - if (! goodname(n->narg.text)) - synerror("Bad function name"); -#endif n->type = NDEFUN; checkkwd = 2; n->narg.next = command(); @@ -10423,7 +9917,7 @@ out: } static union node * -makename() { +makename(void) { union node *n; n = (union node *)stalloc(sizeof (struct narg)); @@ -10435,7 +9929,7 @@ makename() { } static void fixredir(union node *n, const char *text, int err) - { +{ TRACE(("Fix redir %s %d\n", text, err)); if (!err) n->ndup.vname = NULL; @@ -10455,7 +9949,7 @@ static void fixredir(union node *n, const char *text, int err) static void -parsefname() { +parsefname(void) { union node *n = redirnode; if (readtoken() != TWORD) @@ -10530,16 +10024,26 @@ peektoken() { static int readtoken() { int t; - int savecheckkwd = checkkwd; + +#ifdef ASH_ALIAS int savecheckalias = checkalias; + int savecheckkwd = checkkwd; struct alias *ap; +#endif + #ifdef DEBUG int alreadyseen = tokpushback; #endif +#ifdef ASH_ALIAS top: +#endif + t = xxreadtoken(); + +#ifdef ASH_ALIAS checkalias = savecheckalias; +#endif if (checkkwd) { /* @@ -10568,12 +10072,14 @@ top: } } + if (t != TWORD) { if (t != TREDIR) { checkalias = 0; } } else if (checkalias == 2 && isassignment(wordtext)) { lasttoken = t = TASSIGN; +#ifdef ASH_ALIAS } else if (checkalias) { if (!quoteflag && (ap = lookupalias(wordtext, 1)) != NULL) { if (*ap->val) { @@ -10583,6 +10089,7 @@ top: goto top; } checkalias = 0; +#endif } out: #ifdef DEBUG @@ -10598,12 +10105,12 @@ out: /* * Read the next input token. * If the token is a word, we set backquotelist to the list of cmds in - * backquotes. We set quoteflag to true if any part of the word was - * quoted. + * backquotes. We set quoteflag to true if any part of the word was + * quoted. * If the token is TREDIR, then we set redirnode to a structure containing - * the redirection. + * the redirection. * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. + * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the @@ -10613,7 +10120,7 @@ out: * have parseword (readtoken1?) handle both words and redirection.] */ -#define RETURN(token) return lasttoken = token +#define RETURN(token) return lasttoken = token static int xxreadtoken() { @@ -10628,11 +10135,13 @@ xxreadtoken() { needprompt = 0; } startlinno = plinno; - for (;;) { /* until token or start of word found */ + for (;;) { /* until token or start of word found */ c = pgetc_macro(); switch (c) { case ' ': case '\t': +#ifdef ASH_ALIAS case PEOA: +#endif continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF); @@ -10697,12 +10206,12 @@ breakloop: * will run code that appears at the end of readtoken1. */ -#define CHECKEND() {goto checkend; checkend_return:;} -#define PARSEREDIR() {goto parseredir; parseredir_return:;} -#define PARSESUB() {goto parsesub; parsesub_return:;} -#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} -#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} -#define PARSEARITH() {goto parsearith; parsearith_return:;} +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} static int readtoken1(firstc, syntax, eofmark, striptabs) @@ -10718,12 +10227,12 @@ readtoken1(firstc, syntax, eofmark, striptabs) struct nodelist *bqlist; int quotef; int dblquote; - int varnest; /* levels of variables expansion */ - int arinest; /* levels of arithmetic expansion */ - int parenlevel; /* levels of parens in arithmetic */ - int dqvarnest; /* levels of variables expansion within double quotes */ + int varnest; /* levels of variables expansion */ + int arinest; /* levels of arithmetic expansion */ + int parenlevel; /* levels of parens in arithmetic */ + int dqvarnest; /* levels of variables expansion within double quotes */ int oldstyle; - char const *prevsyntax; /* syntax before arithmetic */ + char const *prevsyntax; /* syntax before arithmetic */ #if __GNUC__ /* Avoid longjmp clobbering */ (void) &out; @@ -10750,24 +10259,14 @@ readtoken1(firstc, syntax, eofmark, striptabs) dqvarnest = 0; STARTSTACKSTR(out); - loop: { /* for each line, until end of word */ -#if ATTY - if (c == '\034' && doprompt - && attyset() && ! equal(termval(), "emacs")) { - attyline(); - if (syntax == BASESYNTAX) - return readtoken(); - c = pgetc(); - goto loop; - } -#endif - CHECKEND(); /* set c to PEOF if at end of here document */ - for (;;) { /* until end of line or end of word */ - CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + loop: { /* for each line, until end of word */ + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ switch(syntax[c]) { - case CNL: /* '\n' */ + case CNL: /* '\n' */ if (syntax == BASESYNTAX) - goto endword; /* exit outer loop */ + goto endword; /* exit outer loop */ USTPUTC(c, out); plinno++; if (doprompt) @@ -10775,7 +10274,7 @@ readtoken1(firstc, syntax, eofmark, striptabs) else setprompt(0); c = pgetc(); - goto loop; /* continue outer loop */ + goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); break; @@ -10785,7 +10284,7 @@ readtoken1(firstc, syntax, eofmark, striptabs) USTPUTC(CTLESC, out); USTPUTC(c, out); break; - case CBACK: /* backslash */ + case CBACK: /* backslash */ c = pgetc2(); if (c == PEOF) { USTPUTC('\\', out); @@ -10834,10 +10333,10 @@ readtoken1(firstc, syntax, eofmark, striptabs) quotef++; } break; - case CVAR: /* '$' */ - PARSESUB(); /* parse substitution */ + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ break; - case CENDVAR: /* '}' */ + case CENDVAR: /* '}' */ if (varnest > 0) { varnest--; if (dqvarnest > 0) { @@ -10849,11 +10348,11 @@ readtoken1(firstc, syntax, eofmark, striptabs) } break; #ifdef ASH_MATH_SUPPORT - case CLP: /* '(' in arithmetic */ + case CLP: /* '(' in arithmetic */ parenlevel++; USTPUTC(c, out); break; - case CRP: /* ')' in arithmetic */ + case CRP: /* ')' in arithmetic */ if (parenlevel > 0) { USTPUTC(c, out); --parenlevel; @@ -10879,19 +10378,21 @@ readtoken1(firstc, syntax, eofmark, striptabs) } break; #endif - case CBQUOTE: /* '`' */ + case CBQUOTE: /* '`' */ PARSEBACKQOLD(); break; - case CEOF: - goto endword; /* exit outer loop */ + case CENDFILE: + goto endword; /* exit outer loop */ case CIGN: break; default: if (varnest == 0) - goto endword; /* exit outer loop */ - if (c != PEOA) { + goto endword; /* exit outer loop */ +#ifdef ASH_ALIAS + if (c != PEOA) +#endif USTPUTC(c, out); - } + } c = pgetc_macro(); } @@ -10936,9 +10437,11 @@ endword: checkend: { if (eofmark) { +#ifdef ASH_ALIAS if (c == PEOA) { c = pgetc2(); } +#endif if (striptabs) { while (c == '\t') { c = pgetc2(); @@ -10988,7 +10491,7 @@ parseredir: { np->type = NTO; pungetc(); } - } else { /* c == '<' */ + } else { /* c == '<' */ np->nfile.fd = 0; switch (c = pgetc()) { case '<': @@ -11047,7 +10550,7 @@ parsesub: { ) { USTPUTC('$', out); pungetc(); - } else if (c == '(') { /* $(command) or $((arith)) */ + } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { PARSEARITH(); } else { @@ -11086,7 +10589,7 @@ parsesub: { c = pgetc(); } else -badsub: synerror("Bad substitution"); +badsub: synerror("Bad substitution"); STPUTC('=', out); flags = 0; @@ -11171,17 +10674,17 @@ parsebackq: { savehandler = handler; handler = &jmploc; INTON; - if (oldstyle) { - /* We must read until the closing backquote, giving special - treatment to some slashes, and then push the string and - reread it as input, interpreting it normally. */ - char *pout; - int pc; - int psavelen; - char *pstr; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *pout; + int pc; + int psavelen; + char *pstr; - STARTSTACKSTR(pout); + STARTSTACKSTR(pout); for (;;) { if (needprompt) { setprompt(2); @@ -11192,7 +10695,7 @@ parsebackq: { goto done; case '\\': - if ((pc = pgetc()) == '\n') { + if ((pc = pgetc()) == '\n') { plinno++; if (doprompt) setprompt(2); @@ -11206,17 +10709,19 @@ parsebackq: { */ continue; } - if (pc != '\\' && pc != '`' && pc != '$' - && (!dblquote || pc != '"')) - STPUTC('\\', pout); + if (pc != '\\' && pc != '`' && pc != '$' + && (!dblquote || pc != '"')) + STPUTC('\\', pout); if (pc > PEOA) { break; } /* fall through */ case PEOF: +#ifdef ASH_ALIAS case PEOA: - startlinno = plinno; +#endif + startlinno = plinno; synerror("EOF in backquote substitution"); case '\n': @@ -11228,15 +10733,15 @@ parsebackq: { break; } STPUTC(pc, pout); - } + } done: - STPUTC('\0', pout); - psavelen = pout - stackblock(); - if (psavelen > 0) { + STPUTC('\0', pout); + psavelen = pout - stackblock(); + if (psavelen > 0) { pstr = grabstackstr(pout); setinputstring(pstr); - } - } + } + } nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; @@ -11259,12 +10764,12 @@ done: } (*nlpp)->n = n; - if (oldstyle) { + if (oldstyle) { /* * Start reading from old file again, ignoring any pushed back * tokens left from the backquote parsing */ - popfile(); + popfile(); tokpushback = 0; } while (stackblocksize() <= savelen) @@ -11316,16 +10821,6 @@ parsearith: { } /* end of readtoken */ - -#ifdef mkinit -INCLUDE "parser.h" -RESET { - tokpushback = 0; - checkkwd = 0; - checkalias = 0; -} -#endif - /* * Returns true if the text contains nothing to expand (no dollar signs * or backquotes). @@ -11357,10 +10852,10 @@ noexpand(text) */ static int -goodname(char *name) - { - char *p; - +goodname(const char *name) +{ + const char *p; + p = name; if (! is_name(*p)) return 0; @@ -11385,10 +10880,10 @@ synexpect(token) char msg[64]; if (token >= 0) { - fmtstr(msg, 64, "%s unexpected (expecting %s)", + snprintf(msg, 64, "%s unexpected (expecting %s)", tokname[lasttoken], tokname[token]); } else { - fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + snprintf(msg, 64, "%s unexpected", tokname[lasttoken]); } synerror(msg); /* NOTREACHED */ @@ -11396,220 +10891,153 @@ synexpect(token) static void -synerror(msg) - const char *msg; - { +synerror(const char *msg) +{ if (commandname) - outfmt(&errout, "%s: %d: ", commandname, startlinno); - outfmt(&errout, "Syntax error: %s\n", msg); + out2fmt("%s: %d: ", commandname, startlinno); + out2fmt("Syntax error: %s\n", msg); error((char *)NULL); /* NOTREACHED */ } -static void -setprompt(int which) -{ - whichprompt = which; - putprompt(getprompt(NULL)); -} /* * called by editline -- any expansions to the prompt * should be added here. */ -static const char * -getprompt(void *unused) - { - switch (whichprompt) { - case 0: - return ""; +static void +setprompt(int whichprompt) +{ + char *prompt; + switch (whichprompt) { case 1: - return ps1val(); + prompt = ps1val(); + break; case 2: - return ps2val(); - default: - return ""; - } -} - -static int -isassignment(const char *word) { - if (!is_name(*word)) { - return 0; - } - do { - word++; - } while (is_in_name(*word)); - return *word == '='; -} - -static const char *const * -findkwd(const char *s) { - return findstring( - s, parsekwd, sizeof(parsekwd) / sizeof(const char *) - ); + prompt = ps2val(); + break; + default: /* 0 */ + prompt = ""; + } + putprompt(prompt); } -/* $NetBSD: redir.c,v 1.22 2000/05/22 10:18:47 elric Exp $ */ -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ /* * Code for dealing with input/output redirection. */ -#define EMPTY -2 /* marks an unused slot in redirtab */ +#define EMPTY -2 /* marks an unused slot in redirtab */ #ifndef PIPE_BUF -# define PIPESIZE 4096 /* amount of buffering in a pipe */ +# define PIPESIZE 4096 /* amount of buffering in a pipe */ #else # define PIPESIZE PIPE_BUF #endif -struct redirtab *redirlist; - -/* - * We keep track of whether or not fd0 has been redirected. This is for - * background commands, where we want to redirect fd0 to /dev/null only - * if it hasn't already been redirected. -*/ -static int fd0_redirected = 0; - /* - * We also keep track of where fileno2 goes. + * Open a file in noclobber mode. + * The code was copied from bash. */ -static int fileno2 = 2; +static inline int +noclobberopen(const char *fname) +{ + int r, fd; + struct stat finfo, finfo2; + + /* + * If the file exists and is a regular file, return an error + * immediately. + */ + r = stat(fname, &finfo); + if (r == 0 && S_ISREG(finfo.st_mode)) { + errno = EEXIST; + return -1; + } + + /* + * If the file was not present (r != 0), make sure we open it + * exclusively so that if it is created before we open it, our open + * will fail. Make sure that we do not truncate an existing file. + * Note that we don't turn on O_EXCL unless the stat failed -- if the + * file was not a regular file, we leave O_EXCL off. + */ + if (r != 0) + return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); + fd = open(fname, O_WRONLY|O_CREAT, 0666); + + /* If the open failed, return the file descriptor right away. */ + if (fd < 0) + return fd; + + /* + * OK, the open succeeded, but the file may have been changed from a + * non-regular file to a regular file between the stat and the open. + * We are assuming that the O_EXCL open handles the case where FILENAME + * did not exist and is symlinked to an existing file between the stat + * and open. + */ -static int openredirect __P((union node *)); -static void dupredirect __P((union node *, int, char[10 ])); -static int openhere __P((union node *)); -static int noclobberopen __P((const char *)); + /* + * If we can open it and fstat the file descriptor, and neither check + * revealed that it was a regular file, and the file has not been + * replaced, return the file descriptor. + */ + if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && + finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) + return fd; + /* The file has been replaced. badness. */ + close(fd); + errno = EEXIST; + return -1; +} /* - * Process a list of redirection commands. If the REDIR_PUSH flag is set, - * old file descriptors are stashed away so that the redirection can be - * undone by calling popredir. If the REDIR_BACKQ flag is set, then the - * standard output, and the standard error if it becomes a duplicate of - * stdout, is saved in memory. + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. */ -static void -redirect(redir, flags) - union node *redir; - int flags; - { - union node *n; - struct redirtab *sv = NULL; - int i; - int fd; - int newfd; - int try; - char memory[10]; /* file descriptors to write to memory */ - - for (i = 10 ; --i >= 0 ; ) - memory[i] = 0; - memory[1] = flags & REDIR_BACKQ; - if (flags & REDIR_PUSH) { - sv = ckmalloc(sizeof (struct redirtab)); - for (i = 0 ; i < 10 ; i++) - sv->renamed[i] = EMPTY; - sv->next = redirlist; - redirlist = sv; - } - for (n = redir ; n ; n = n->nfile.next) { - fd = n->nfile.fd; - try = 0; - if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && - n->ndup.dupfd == fd) - continue; /* redirect from/to same file descriptor */ +static inline int +openhere(const union node *redir) +{ + int pip[2]; + int len = 0; - INTOFF; - newfd = openredirect(n); - if (((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) || - (fd == fileno2)) { - if (newfd == fd) { - try++; - } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { - switch (errno) { - case EBADF: - if (!try) { - dupredirect(n, newfd, memory); - try++; - break; - } - /* FALLTHROUGH*/ - default: - if (newfd >= 0) { - close(newfd); - } - INTON; - error("%d: %s", fd, strerror(errno)); - /* NOTREACHED */ - } - } - if (!try) { - close(fd); - if (flags & REDIR_PUSH) { - sv->renamed[fd] = i; - } - if (fd == fileno2) { - fileno2 = i; - } - } - } else if (fd != newfd) { - close(fd); + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; } - if (fd == 0) - fd0_redirected++; - if (!try) - dupredirect(n, newfd, memory); - INTON; } - if (memory[1]) - out1 = &memout; - if (memory[2]) - out2 = &memout; + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; } -static int -openredirect(redir) - union node *redir; - { +static inline int +openredirect(const union node *redir) +{ char *fname; int f; @@ -11677,73 +11105,100 @@ eopen: } -static void -dupredirect(redir, f, memory) - union node *redir; - int f; - char memory[10]; - { - int fd = redir->nfile.fd; - - memory[fd] = 0; - if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { - if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ - if (memory[redir->ndup.dupfd]) - memory[fd] = 1; - else - dup_as_newfd(redir->ndup.dupfd, fd); - } - return; - } - - if (f != fd) { - dup_as_newfd(f, fd); - close(f); - } - return; -} - - /* - * Handle here documents. Normally we fork off a process to write the - * data to a pipe. If the document is short, we can stuff the data in - * the pipe without forking. + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout. */ -static int -openhere(redir) - union node *redir; - { - int pip[2]; - int len = 0; +static void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + int newfd; + int try; + int fd1dup = flags & REDIR_BACKQ;; /* stdout `cmd` redir to pipe */ - if (pipe(pip) < 0) - error("Pipe call failed"); - if (redir->type == NHERE) { - len = strlen(redir->nhere.doc->narg.text); - if (len <= PIPESIZE) { - xwrite(pip[1], redir->nhere.doc->narg.text, len); - goto out; + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + try = 0; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + INTOFF; + newfd = openredirect(n); + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + if (newfd == fd) { + try++; + } else if ((i = fcntl(fd, F_DUPFD, 10)) == -1) { + switch (errno) { + case EBADF: + if (!try) { + dupredirect(n, newfd, fd1dup); + try++; + break; + } + /* FALLTHROUGH*/ + default: + if (newfd >= 0) { + close(newfd); + } + INTON; + error("%d: %m", fd); + /* NOTREACHED */ + } + } + if (!try) { + close(fd); + if (flags & REDIR_PUSH) { + sv->renamed[fd] = i; + } + } + } else if (fd != newfd) { + close(fd); } + if (fd == 0) + fd0_redirected++; + if (!try) + dupredirect(n, newfd, fd1dup); + INTON; } - if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { - close(pip[0]); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif - signal(SIGPIPE, SIG_DFL); - if (redir->type == NHERE) - xwrite(pip[1], redir->nhere.doc->narg.text, len); - else - expandhere(redir->nhere.doc, pip[1]); - _exit(0); +} + + +static void +dupredirect(const union node *redir, int f, int fd1dup) +{ + int fd = redir->nfile.fd; + + if(fd==1) + fd1dup = 0; + if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) { + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (redir->ndup.dupfd!=1 || fd1dup!=1) + dup_as_newfd(redir->ndup.dupfd, fd); + } + return; } -out: - close(pip[1]); - return pip[0]; + + if (f != fd) { + dup_as_newfd(f, fd); + close(f); + } + return; } @@ -11753,23 +11208,21 @@ out: */ static void -popredir() { +popredir(void) +{ struct redirtab *rp = redirlist; int i; INTOFF; for (i = 0 ; i < 10 ; i++) { if (rp->renamed[i] != EMPTY) { - if (i == 0) - fd0_redirected--; + if (i == 0) + fd0_redirected--; close(i); if (rp->renamed[i] >= 0) { dup_as_newfd(rp->renamed[i], i); close(rp->renamed[i]); } - if (rp->renamed[i] == fileno2) { - fileno2 = i; - } } } redirlist = rp->next; @@ -11777,37 +11230,12 @@ popredir() { INTON; } -/* - * Undo all redirections. Called on error or interrupt. - */ - -#ifdef mkinit - -INCLUDE "redir.h" - -RESET { - while (redirlist) - popredir(); -} - -SHELLPROC { - clearredir(); -} - -#endif - -/* Return true if fd 0 has already been redirected at least once. */ -static int -fd0_redirected_p () { - return fd0_redirected != 0; -} - /* * Discard all saved file descriptors. */ static void -clearredir() { +clearredir(void) { struct redirtab *rp; int i; @@ -11815,21 +11243,13 @@ clearredir() { for (i = 0 ; i < 10 ; i++) { if (rp->renamed[i] >= 0) { close(rp->renamed[i]); - if (rp->renamed[i] == fileno2) { - fileno2 = -1; - } } rp->renamed[i] = EMPTY; } } - if (fileno2 != 2 && fileno2 >= 0) { - close(fileno2); - fileno2 = -1; - } } - /* * Copy a file descriptor to be >= to. Returns -1 * if the source file descriptor is closed, EMPTY if there are no unused @@ -11848,135 +11268,43 @@ dup_as_newfd(from, to) if (errno == EMFILE) return EMPTY; else - error("%d: %s", from, strerror(errno)); + error("%d: %m", from); } return newfd; } -/* - * Open a file in noclobber mode. - * The code was copied from bash. - */ -static int -noclobberopen(fname) - const char *fname; -{ - int r, fd; - struct stat finfo, finfo2; - - /* - * If the file exists and is a regular file, return an error - * immediately. - */ - r = stat(fname, &finfo); - if (r == 0 && S_ISREG(finfo.st_mode)) { - errno = EEXIST; - return -1; - } - - /* - * If the file was not present (r != 0), make sure we open it - * exclusively so that if it is created before we open it, our open - * will fail. Make sure that we do not truncate an existing file. - * Note that we don't turn on O_EXCL unless the stat failed -- if the - * file was not a regular file, we leave O_EXCL off. - */ - if (r != 0) - return open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666); - fd = open(fname, O_WRONLY|O_CREAT, 0666); - - /* If the open failed, return the file descriptor right away. */ - if (fd < 0) - return fd; - - /* - * OK, the open succeeded, but the file may have been changed from a - * non-regular file to a regular file between the stat and the open. - * We are assuming that the O_EXCL open handles the case where FILENAME - * did not exist and is symlinked to an existing file between the stat - * and open. - */ - - /* - * If we can open it and fstat the file descriptor, and neither check - * revealed that it was a regular file, and the file has not been - * replaced, return the file descriptor. - */ - if (fstat(fd, &finfo2) == 0 && !S_ISREG(finfo2.st_mode) && - finfo.st_dev == finfo2.st_dev && finfo.st_ino == finfo2.st_ino) - return fd; - - /* The file has been replaced. badness. */ - close(fd); - errno = EEXIST; - return -1; -} -/* $NetBSD: setmode.c,v 1.28 2000/01/25 15:43:43 enami Exp $ */ - -/* - * Copyright (c) 1989, 1993, 1994 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Dave Borman at Cray Research, Inc. - * - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef __weak_alias +/*#ifdef __weak_alias __weak_alias(getmode,_getmode) __weak_alias(setmode,_setmode) -#endif +#endif*/ -#ifdef __GLIBC__ +#ifndef S_ISTXT +#if defined(__GLIBC__) && __GLIBC__ >= 2 #define S_ISTXT __S_ISVTX +#else +#define S_ISTXT S_ISVTX +#endif #endif -#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ -#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ +#define SET_LEN 6 /* initial # of bitcmd struct to malloc */ +#define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */ typedef struct bitcmd { - char cmd; - char cmd2; - mode_t bits; + char cmd; + char cmd2; + mode_t bits; } BITCMD; -#define CMD2_CLR 0x01 -#define CMD2_SET 0x02 -#define CMD2_GBITS 0x04 -#define CMD2_OBITS 0x08 -#define CMD2_UBITS 0x10 +#define CMD2_CLR 0x01 +#define CMD2_SET 0x02 +#define CMD2_GBITS 0x04 +#define CMD2_OBITS 0x08 +#define CMD2_UBITS 0x10 -static BITCMD *addcmd __P((BITCMD *, int, int, int, u_int)); -static void compress_mode __P((BITCMD *)); +static BITCMD *addcmd (BITCMD *, int, int, int, u_int); +static void compress_mode (BITCMD *); #ifdef SETMODE_DEBUG -static void dumpmode __P((BITCMD *)); +static void dumpmode (BITCMD *); #endif /* @@ -11985,7 +11313,7 @@ static void dumpmode __P((BITCMD *)); * Note that there is no '=' command; a strict assignment is just a '-' (clear * bits) followed by a '+' (set bits). */ -mode_t +static mode_t getmode(bbox, omode) const void *bbox; mode_t omode; @@ -12015,7 +11343,7 @@ getmode(bbox, omode) case 'o': value = newmode & S_IRWXO; -common: if (set->cmd2 & CMD2_CLR) { +common: if (set->cmd2 & CMD2_CLR) { clrval = (set->cmd2 & CMD2_SET) ? S_IRWXO : value; if (set->cmd2 & CMD2_UBITS) @@ -12057,23 +11385,23 @@ common: if (set->cmd2 & CMD2_CLR) { } } -#define ADDCMD(a, b, c, d) do { \ - if (set >= endset) { \ - BITCMD *newset; \ - setlen += SET_LEN_INCR; \ - newset = realloc(saveset, sizeof(BITCMD) * setlen); \ - if (newset == NULL) { \ - free(saveset); \ - return (NULL); \ - } \ - set = newset + (set - saveset); \ - saveset = newset; \ - endset = newset + (setlen - 2); \ - } \ - set = addcmd(set, (a), (b), (c), (d)); \ +#define ADDCMD(a, b, c, d) do { \ + if (set >= endset) { \ + BITCMD *newset; \ + setlen += SET_LEN_INCR; \ + newset = realloc(saveset, sizeof(BITCMD) * setlen); \ + if (newset == NULL) { \ + free(saveset); \ + return (NULL); \ + } \ + set = newset + (set - saveset); \ + saveset = newset; \ + endset = newset + (setlen - 2); \ + } \ + set = addcmd(set, (a), (b), (c), (d)); \ } while (/*CONSTCOND*/0) -#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) +#define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) static void * setmode(p) @@ -12084,7 +11412,7 @@ setmode(p) BITCMD *set, *saveset, *endset; sigset_t mysigset, sigoset; mode_t mask; - int equalopdone = 0; /* pacify gcc */ + int equalopdone = 0; /* pacify gcc */ int permXbits, setlen; if (!*p) @@ -12103,7 +11431,7 @@ setmode(p) (void)sigprocmask(SIG_SETMASK, &sigoset, NULL); setlen = SET_LEN + 2; - + if ((set = malloc((u_int)(sizeof(BITCMD) * setlen))) == NULL) return (NULL); saveset = set; @@ -12113,7 +11441,7 @@ setmode(p) * If an absolute number, get it and return; disallow non-octal digits * or illegal bits. */ - if (isdigit((unsigned char)*p)) { + if (is_digit((unsigned char)*p)) { perm = (mode_t)strtol(p, &ep, 8); if (*ep || perm & ~(STANDARD_BITS|S_ISTXT)) { free(saveset); @@ -12149,7 +11477,7 @@ setmode(p) } } -getop: if ((op = *p++) != '+' && op != '-' && op != '=') { +getop: if ((op = *p++) != '+' && op != '-' && op != '=') { free(saveset); return (NULL); } @@ -12164,16 +11492,16 @@ getop: if ((op = *p++) != '+' && op != '-' && op != '=') { break; case 's': /* - * If specific bits where requested and - * only "other" bits ignore set-id. + * If specific bits where requested and + * only "other" bits ignore set-id. */ if (who == 0 || (who & ~S_IRWXO)) perm |= S_ISUID|S_ISGID; break; case 't': /* - * If specific bits where requested and - * only "other" bits ignore set-id. + * If specific bits where requested and + * only "other" bits ignore set-id. */ if (who == 0 || (who & ~S_IRWXO)) { who |= S_ISTXT; @@ -12229,7 +11557,7 @@ getop: if ((op = *p++) != '+' && op != '-' && op != '=') { } } -apply: if (!*p) +apply: if (!*p) break; if (*p != ',') goto getop; @@ -12286,7 +11614,7 @@ addcmd(set, op, who, oparg, mask) set->cmd2 = CMD2_UBITS | CMD2_GBITS | CMD2_OBITS; set->bits = mask; } - + if (oparg == '+') set->cmd2 |= CMD2_SET; else if (oparg == '-') @@ -12320,7 +11648,7 @@ dumpmode(set) /* * Given an array of bitcmd structures, compress by compacting consecutive * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u', - * 'g' and 'o' commands continue to be separate. They could probably be + * 'g' and 'o' commands continue to be separate. They could probably be * compacted, but it's not worth the effort. */ static void @@ -12374,51 +11702,12 @@ compress_mode(set) } } } -/* $NetBSD: show.c,v 1.18 1999/10/08 21:10:44 pk Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - #ifdef DEBUG -static void shtree __P((union node *, int, char *, FILE*)); -static void shcmd __P((union node *, FILE *)); -static void sharg __P((union node *, FILE *)); -static void indent __P((int, char *, FILE *)); -static void trstring __P((char *)); +static void shtree (union node *, int, char *, FILE*); +static void shcmd (union node *, FILE *); +static void sharg (union node *, FILE *); +static void indent (int, char *, FILE *); +static void trstring (char *); static void @@ -12506,14 +11795,14 @@ shcmd(cmd, fp) if (! first) putchar(' '); switch (np->nfile.type) { - case NTO: s = ">"; dftfd = 1; break; - case NAPPEND: s = ">>"; dftfd = 1; break; - case NTOFD: s = ">&"; dftfd = 1; break; - case NTOOV: s = ">|"; dftfd = 1; break; - case NFROM: s = "<"; dftfd = 0; break; - case NFROMFD: s = "<&"; dftfd = 0; break; - case NFROMTO: s = "<>"; dftfd = 0; break; - default: s = "*error*"; dftfd = 0; break; + case NTO: s = ">"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NTOOV: s = ">|"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; + default: s = "*error*"; dftfd = 0; break; } if (np->nfile.fd != dftfd) fprintf(fp, "%d", np->nfile.fd); @@ -12714,7 +12003,7 @@ trstring(s) case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; case CTLBACKQ: c = 'q'; goto backslash; case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; -backslash: putc('\\', tracefile); +backslash: putc('\\', tracefile); putc(c, tracefile); break; default: @@ -12743,442 +12032,50 @@ trargs(ap) trstring(*ap++); if (*ap) putc(' ', tracefile); - else - putc('\n', tracefile); - } - fflush(tracefile); -} - - -static void -opentrace() { - char s[100]; -#ifdef O_APPEND - int flags; -#endif - - if (!debug) - return; -#ifdef not_this_way - { - char *p; - if ((p = getenv("HOME")) == NULL) { - if (geteuid() == 0) - p = "/"; - else - p = "/tmp"; - } - scopy(p, s); - strcat(s, "/trace"); - } -#else - scopy("./trace", s); -#endif /* not_this_way */ - if ((tracefile = fopen(s, "a")) == NULL) { - fprintf(stderr, "Can't open %s\n", s); - return; - } -#ifdef O_APPEND - if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) - fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); -#endif - fputs("\nTracing started.\n", tracefile); - fflush(tracefile); -} -#endif /* DEBUG */ - - -/* - * This file was generated by the mksyntax program. - */ - -/* syntax table used when not in quotes */ -static const char basesyntax[257] = { - CEOF, CSPCL, CWORD, CCTL, - CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CCTL, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CSPCL, - CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CSPCL, CWORD, - CDQUOTE, CWORD, CVAR, CWORD, - CSPCL, CSQUOTE, CSPCL, CSPCL, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CSPCL, CSPCL, CWORD, - CSPCL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CBACK, CWORD, - CWORD, CWORD, CBQUOTE, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CSPCL, CENDVAR, - CWORD -}; - -/* syntax table used when in double quotes */ -static const char dqsyntax[257] = { - CEOF, CIGN, CWORD, CCTL, - CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CCTL, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CCTL, - CENDQUOTE,CWORD, CVAR, CWORD, - CWORD, CWORD, CWORD, CWORD, - CCTL, CWORD, CWORD, CCTL, - CWORD, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CCTL, CWORD, CWORD, CCTL, - CWORD, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CCTL, CBACK, CCTL, - CWORD, CWORD, CBQUOTE, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CENDVAR, - CCTL -}; - -/* syntax table used when in single quotes */ -static const char sqsyntax[257] = { - CEOF, CIGN, CWORD, CCTL, - CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CCTL, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CCTL, - CWORD, CWORD, CWORD, CWORD, - CWORD, CENDQUOTE,CWORD, CWORD, - CCTL, CWORD, CWORD, CCTL, - CWORD, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CCTL, CWORD, CWORD, CCTL, - CWORD, CCTL, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CCTL, CCTL, CCTL, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CCTL -}; - -/* syntax table used when in arithmetic */ -static const char arisyntax[257] = { - CEOF, CIGN, CWORD, CCTL, - CCTL, CCTL, CCTL, CCTL, - CCTL, CCTL, CCTL, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CNL, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CDQUOTE, CWORD, CVAR, CWORD, - CWORD, CSQUOTE, CLP, CRP, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CBACK, CWORD, - CWORD, CWORD, CBQUOTE, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CWORD, - CWORD, CWORD, CWORD, CENDVAR, - CWORD -}; - -/* character classification table */ -static const char is_type[257] = { - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, ISSPECL, - 0, ISSPECL, ISSPECL, 0, - 0, 0, 0, 0, - ISSPECL, 0, 0, ISSPECL, - 0, 0, ISDIGIT, ISDIGIT, - ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, - ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT, - 0, 0, 0, 0, - 0, ISSPECL, ISSPECL, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, ISUPPER, ISUPPER, ISUPPER, - ISUPPER, 0, 0, 0, - 0, ISUNDER, 0, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, ISLOWER, ISLOWER, ISLOWER, - ISLOWER, 0, 0, 0, - 0 -}; -/* $NetBSD: trap.c,v 1.25 2001/02/04 19:52:07 christos Exp $ */ + else + putc('\n', tracefile); + } + fflush(tracefile); +} -/*- - * 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. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* - * Sigmode records the current value of the signal handlers for the various - * modes. A value of zero means that the current handler is not known. - * S_HARD_IGN indicates that the signal was ignored on entry to the shell, - */ +static void +opentrace() { + char s[100]; +#ifdef O_APPEND + int flags; +#endif + + if (!debug) + return; +#ifdef not_this_way + { + char *p; + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + strcpy(s, p); + strcat(s, "/trace"); + } +#else + strcpy(s, "./trace"); +#endif /* not_this_way */ + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +} +#endif /* DEBUG */ + /* * The trap builtin. @@ -13199,7 +12096,7 @@ trapcmd(argc, argv) char *p; p = single_quote(trap[signo]); - out1fmt("trap -- %s %s\n", p, + printf("trap -- %s %s\n", p, signal_names[signo] + (signo ? 3 : 0) ); stunalloc(p); @@ -13235,25 +12132,6 @@ trapcmd(argc, argv) -/* - * Clear traps on a fork. - */ - -static void -clear_traps() { - char **tp; - - for (tp = trap ; tp < &trap[NSIG] ; tp++) { - if (*tp && **tp) { /* trap not NULL or SIG_IGN */ - INTOFF; - ckfree(*tp); - *tp = NULL; - if (tp != &trap[0]) - setsignal(tp - trap); - INTON; - } - } -} @@ -13263,8 +12141,7 @@ clear_traps() { */ static void -setsignal(signo) - int signo; +setsignal(int signo) { int action; char *t; @@ -13285,7 +12162,6 @@ setsignal(signo) case SIGQUIT: #ifdef DEBUG { - extern int debug; if (debug) break; @@ -13296,7 +12172,7 @@ setsignal(signo) if (iflag) action = S_IGN; break; -#if JOBS +#ifdef JOBS case SIGTSTP: case SIGTTOU: if (mflag) @@ -13322,11 +12198,11 @@ setsignal(signo) if (act.sa_handler == SIG_IGN) { if (mflag && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)) { - *t = S_IGN; /* don't hard ignore these */ + *t = S_IGN; /* don't hard ignore these */ } else *t = S_HARD_IGN; } else { - *t = S_RESET; /* force to be set */ + *t = S_RESET; /* force to be set */ } } if (*t == S_HARD_IGN || *t == action) @@ -13362,30 +12238,12 @@ ignoresig(signo) } -#ifdef mkinit -INCLUDE -INCLUDE "trap.h" - -SHELLPROC { - char *sm; - - clear_traps(); - for (sm = sigmode ; sm < sigmode + NSIG - 1; sm++) { - if (*sm == S_IGN) - *sm = S_HARD_IGN; - } -} -#endif - - - /* * Signal handler. */ static void -onsig(signo) - int signo; +onsig(int signo) { if (signo == SIGINT && trap[SIGINT] == NULL) { onint(); @@ -13396,14 +12254,14 @@ onsig(signo) } - /* * Called to execute a trap. Perhaps we should avoid entering new trap * handlers while we are executing a trap handler. */ static void -dotrap() { +dotrap(void) +{ int i; int savestatus; @@ -13423,37 +12281,12 @@ done: pendingsigs = 0; } - - -/* - * Controls whether the shell is interactive or not. - */ - - -static void -setinteractive(on) - int on; -{ - static int is_interactive; - - if (on == is_interactive) - return; - setsignal(SIGINT); - setsignal(SIGQUIT); - setsignal(SIGTERM); - chkmail(1); - is_interactive = on; -} - - - /* * Called to exit the shell. */ static void -exitshell(status) - int status; +exitshell(int status) { struct jmploc loc1, loc2; char *p; @@ -13470,9 +12303,9 @@ exitshell(status) trap[0] = NULL; evalstring(p, 0); } -l1: handler = &loc2; /* probably unnecessary */ +l1: handler = &loc2; /* probably unnecessary */ flushall(); -#if JOBS +#ifdef JOBS setjobctl(0); #endif l2: _exit(status); @@ -13483,8 +12316,7 @@ static int decode_signal(const char *string, int minsig) { int signo; - if (is_number(string)) { - signo = atoi(string); + if (is_number(string, &signo)) { if (signo >= NSIG) { return -1; } @@ -13507,137 +12339,14 @@ zero: return -1; } -/* $NetBSD: var.c,v 1.27 2001/02/04 19:52:07 christos Exp $ */ - -/*- - * Copyright (c) 1991, 1993 - * The Regents of the University of California. All rights reserved. - * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -#define VTABSIZE 39 - - -struct varinit { - struct var *var; - int flags; - const char *text; - void (*func) __P((const char *)); -}; - -struct localvar *localvars; - -#if ATTY -struct var vatty; -#endif -struct var vifs; -struct var vmail; -struct var vmpath; -struct var vpath; -struct var vps1; -struct var vps2; -struct var vvers; -struct var voptind; - -static const char defpathvar[] = - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"; -#ifdef IFS_BROKEN -static const char defifsvar[] = "IFS= \t\n"; -#else -static const char defifs[] = " \t\n"; -#endif - -static const struct varinit varinit[] = { -#if ATTY - { &vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY=", - NULL }, -#endif -#ifdef IFS_BROKEN - { &vifs, VSTRFIXED|VTEXTFIXED, defifsvar, -#else - { &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=", -#endif - NULL }, - { &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=", - NULL }, - { &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=", - NULL }, - { &vpath, VSTRFIXED|VTEXTFIXED, defpathvar, - changepath }, - /* - * vps1 depends on uid - */ - { &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ", - NULL }, - { &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1", - getoptsreset }, - { NULL, 0, NULL, - NULL } -}; - -struct var *vartab[VTABSIZE]; - -static struct var **hashvar __P((const char *)); -static void showvars __P((const char *, int, int)); -static struct var **findvar __P((struct var **, const char *)); +static struct var **hashvar (const char *); +static void showvars (const char *, int, int); +static struct var **findvar (struct var **, const char *); /* * Initialize the varable symbol tables and import the environment */ -#ifdef mkinit -INCLUDE -INCLUDE "output.h" -INCLUDE "var.h" -static char **environ; -INIT { - char **envp; - char ppid[32]; - - initvar(); - for (envp = environ ; *envp ; envp++) { - if (strchr(*envp, '=')) { - setvareq(*envp, VEXPORT|VTEXTFIXED); - } - } - - fmtstr(ppid, sizeof(ppid), "%d", (int) getppid()); - setvar("PPID", ppid, 0); -} -#endif - - /* * This routine initializes the builtin variables. It is called when the * shell is initialized and again when a shell procedure is spawned. @@ -13704,7 +12413,7 @@ setvar(name, val, flags) namelen = p - name; if (isbad) error("%.*s: bad variable name", namelen, name); - len = namelen + 2; /* 2 is space for '=' and '\0' */ + len = namelen + 2; /* 2 is space for '=' and '\0' */ if (val == NULL) { flags |= VUNSET; } else { @@ -13801,7 +12510,7 @@ listsetvar(mylist) * Find the value of a variable. Returns NULL if not set. */ -static char * +static const char * lookupvar(name) const char *name; { @@ -13819,11 +12528,10 @@ lookupvar(name) * Search the environment of a builtin command. */ -static char * -bltinlookup(name) - const char *name; +static const char * +bltinlookup(const char *name) { - struct strlist *sp; + const struct strlist *sp; for (sp = cmdenviron ; sp ; sp = sp->next) { if (varequal(sp->text, name)) @@ -13870,16 +12578,8 @@ environment() { * VSTACK set since these are currently allocated on the stack. */ -#ifdef mkinit -static void shprocvar __P((void)); - -SHELLPROC { - shprocvar(); -} -#endif - static void -shprocvar() { +shprocvar(void) { struct var **vpp; struct var *vp, **prev; @@ -13963,6 +12663,8 @@ found:; * The "local" command. */ +/* funcnest nonzero if we are currently evaluating a function */ + static int localcmd(argc, argv) int argc; @@ -13970,7 +12672,7 @@ localcmd(argc, argv) { char *name; - if (! in_function()) + if (! funcnest) error("Not in a function"); while ((name = *argptr++) != NULL) { mklocal(name); @@ -13998,8 +12700,8 @@ mklocal(name) lvp = ckmalloc(sizeof (struct localvar)); if (name[0] == '-' && name[1] == '\0') { char *p; - p = ckmalloc(sizeof optlist); - lvp->text = memcpy(p, optlist, sizeof optlist); + p = ckmalloc(sizeof optet_vals); + lvp->text = memcpy(p, optet_vals, sizeof optet_vals); vp = NULL; } else { vpp = hashvar(name); @@ -14009,7 +12711,7 @@ mklocal(name) setvareq(savestr(name), VSTRFIXED); else setvar(name, NULL, VSTRFIXED); - vp = *vpp; /* the new variable */ + vp = *vpp; /* the new variable */ lvp->text = NULL; lvp->flags = VUNSET; } else { @@ -14039,8 +12741,8 @@ poplocalvars() { while ((lvp = localvars) != NULL) { localvars = lvp->next; vp = lvp->vp; - if (vp == NULL) { /* $- saved */ - memcpy(optlist, lvp->text, sizeof optlist); + if (vp == NULL) { /* $- saved */ + memcpy(optet_vals, lvp->text, sizeof optet_vals); ckfree(lvp->text); } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { (void)unsetvar(vp->text); @@ -14111,9 +12813,8 @@ unsetcmd(argc, argv) */ static int -unsetvar(s) - const char *s; - { +unsetvar(const char *s) +{ struct var **vpp; struct var *vp; @@ -14147,9 +12848,8 @@ unsetvar(s) */ static struct var ** -hashvar(p) - const char *p; - { +hashvar(const char *p) +{ unsigned int hashval; hashval = ((unsigned char) *p) << 4; @@ -14167,9 +12867,8 @@ hashvar(p) */ static int -varequal(p, q) - const char *p, *q; - { +varequal(const char *p, const char *q) +{ while (*p == *q++) { if (*p++ == '=') return 1; @@ -14196,10 +12895,8 @@ showvars(const char *myprefix, int mask, int xor) len = p - vp->text; p = single_quote(p); - out1fmt( - "%s%s%.*s%s\n", myprefix, sep, len, - vp->text, p - ); + printf("%s%s%.*s%s\n", myprefix, sep, len, + vp->text, p); stunalloc(p); } } @@ -14220,7 +12917,7 @@ findvar(struct var **vpp, const char *name) /* * Copyright (c) 1999 Herbert Xu * This file contains code for the times builtin. - * $Id: ash.c,v 1.1 2001/06/28 07:25:15 andersen Exp $ + * $Id: ash.c,v 1.10 2001/07/12 20:26:31 andersen Exp $ */ static int timescmd (int argc, char **argv) { @@ -14240,3 +12937,39 @@ static int timescmd (int argc, char **argv) return 0; } + +/*- + * Copyright (c) 1989, 1991, 1993, 1994 + * 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. + * + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */