| /* 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. |
| * |
| * 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 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 <andersee@debian.org> and |
| * Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox |
| * |
| * |
| * Original copyright notice is retained at the end of this file. |
| */ |
| |
| |
| /* 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 adds a bit over 2k an x86 system. */ |
| //#undef ASH_MATH_SUPPORT |
| #define 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 |
| |
| #include <assert.h> |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <paths.h> |
| #include <pwd.h> |
| #include <setjmp.h> |
| #include <signal.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sysexits.h> |
| #include <unistd.h> |
| #include <sys/stat.h> |
| #include <sys/cdefs.h> |
| #include <sys/ioctl.h> |
| #include <sys/param.h> |
| #include <sys/resource.h> |
| #include <sys/time.h> |
| #include <sys/times.h> |
| #include <sys/types.h> |
| #include <sys/wait.h> |
| |
| |
| #if !defined(FNMATCH_BROKEN) |
| #include <fnmatch.h> |
| #endif |
| #if !defined(GLOB_BROKEN) |
| #include <glob.h> |
| #endif |
| |
| #ifdef JOBS |
| #include <termios.h> |
| #endif |
| |
| #include "busybox.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 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 */ |
| |
| |
| /* 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 */ |
| |
| /* 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 */ |
| |
| /* |
| * BSD setjmp saves the signal mask, which violates ANSI C and takes time, |
| * so we use _setjmp instead. |
| */ |
| |
| #if defined(BSD) |
| #define setjmp(jmploc) _setjmp(jmploc) |
| #define longjmp(jmploc, val) _longjmp(jmploc, val) |
| #endif |
| |
| /* |
| * Most machines require the value returned from malloc to be aligned |
| * in some way. The following macro will get this right on many machines. |
| */ |
| |
| #ifndef ALIGN |
| union align { |
| int i; |
| char *cp; |
| }; |
| |
| #define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1)) |
| #endif |
| |
| #ifdef BB_LOCALE_SUPPORT |
| #include <locale.h> |
| 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. :-)) |
| */ |
| |
| static void onint (void); |
| static volatile int suppressint; |
| static volatile int intpending; |
| |
| #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 |
| |
| #define CLEAR_PENDING_INT intpending = 0 |
| #define int_pending() intpending |
| |
| |
| typedef void *pointer; |
| #ifndef NULL |
| #define NULL (void *)0 |
| #endif |
| |
| 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 *); |
| |
| /* |
| * 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. |
| */ |
| |
| #define MINSIZE 504 /* minimum size of a block */ |
| |
| |
| struct stack_block { |
| struct stack_block *prev; |
| char space[MINSIZE]; |
| }; |
| |
| static struct stack_block stackbase; |
| static struct stack_block *stackp = &stackbase; |
| static struct stackmark *markp; |
| static char *stacknxt = stackbase.space; |
| static int stacknleft = MINSIZE; |
| |
| |
| #define equal(s1, s2) (strcmp(s1, s2) == 0) |
| |
| #define stackblock() stacknxt |
| #define stackblocksize() stacknleft |
| #define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() |
| |
| #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')) |
| |
| |
| #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) |
| |
| #define ckfree(p) free((pointer)(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" |
| }; |
| |
| #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; |
| }; |
| |
| |
| struct ncmd { |
| int type; |
| int backgnd; |
| union node *assign; |
| union node *args; |
| union node *redirect; |
| }; |
| |
| |
| struct npipe { |
| int type; |
| int backgnd; |
| struct nodelist *cmdlist; |
| }; |
| |
| |
| struct nredir { |
| int type; |
| union node *n; |
| union node *redirect; |
| }; |
| |
| |
| struct nif { |
| int type; |
| union node *test; |
| union node *ifpart; |
| union node *elsepart; |
| }; |
| |
| |
| struct nfor { |
| int type; |
| union node *args; |
| union node *body; |
| char *var; |
| }; |
| |
| |
| struct ncase { |
| int type; |
| union node *expr; |
| union node *cases; |
| }; |
| |
| |
| struct nclist { |
| int type; |
| union node *next; |
| union node *pattern; |
| union node *body; |
| }; |
| |
| |
| struct narg { |
| int type; |
| union node *next; |
| char *text; |
| struct nodelist *backquote; |
| }; |
| |
| |
| struct nfile { |
| int type; |
| union node *next; |
| int fd; |
| union node *fname; |
| char *expfname; |
| }; |
| |
| |
| struct ndup { |
| int type; |
| union node *next; |
| int fd; |
| int dupfd; |
| union node *vname; |
| }; |
| |
| |
| struct nhere { |
| int type; |
| union node *next; |
| int fd; |
| union node *doc; |
| }; |
| |
| |
| struct nnot { |
| int type; |
| union node *com; |
| }; |
| |
| |
| 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; |
| }; |
| |
| |
| 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 */ |
| }; |
| |
| /* |
| * 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 */ |
| |
| |
| |
| 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 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); |
| |
| 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); } |
| |
| #ifndef ASH_OPTIMIZE_FOR_SIZE |
| #define out2c(c) putc((c), stderr) |
| #else |
| static void out2c(int c) { putc(c, stderr); } |
| #endif |
| |
| /* 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 |
| }; |
| |
| /* 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 |
| }; |
| |
| /* 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 */ |
| |
| /* |
| * 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 int tokpushback; /* last token pushed back */ |
| #define NEOF ((union node *)&tokpushback) |
| static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ |
| |
| |
| 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 rmescapes (char *); |
| #endif |
| |
| static int casematch (union node *, const char *); |
| static void clearredir(void); |
| static void popstring(void); |
| static void readcmdfile (const char *); |
| |
| 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); |
| |
| |
| 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"; |
| |
| static int sstrnleft; |
| static int herefd = -1; |
| |
| 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 |
| |
| 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 |
| |
| static const struct varinit varinit[] = { |
| #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 }, |
| #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 |
| { NULL, 0, NULL, |
| NULL } |
| }; |
| |
| #define VTABSIZE 39 |
| |
| static struct var *vartab[VTABSIZE]; |
| |
| /* |
| * 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. |
| */ |
| |
| #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) |
| |
| 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 *); |
| |
| |
| 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 */ |
| |
| |
| #ifdef ASH_ALIAS |
| |
| #define ALIASINUSE 1 |
| #define ALIASDEAD 2 |
| |
| #define ATABSIZE 39 |
| |
| struct alias { |
| struct alias *next; |
| char *name; |
| char *val; |
| int flag; |
| }; |
| |
| 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; |
| |
| 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 |
| unalias(char *name) |
| { |
| struct alias **app; |
| |
| app = __lookupalias(name); |
| |
| if (*app) { |
| INTOFF; |
| *app = freealias(*app); |
| INTON; |
| return (0); |
| } |
| |
| return (1); |
| } |
| |
| static void |
| rmaliases(void) |
| { |
| struct alias *ap, **app; |
| int i; |
| |
| 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; |
| } |
| |
| 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); |
| } |
| |
| |
| /* |
| * TODO - sort output |
| */ |
| static int |
| aliascmd(int argc, char **argv) |
| { |
| char *n, *v; |
| int ret = 0; |
| struct alias *ap; |
| |
| if (argc == 1) { |
| int i; |
| |
| for (i = 0; i < ATABSIZE; i++) |
| for (ap = atab[i]; ap; ap = ap->next) { |
| printalias(ap); |
| } |
| return (0); |
| } |
| 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); |
| } |
| } |
| |
| return (ret); |
| } |
| |
| static int |
| unaliascmd(int argc, char **argv) |
| { |
| int i; |
| |
| while ((i = nextopt("a")) != '\0') { |
| if (i == 'a') { |
| rmaliases(); |
| return (0); |
| } |
| } |
| for (i = 0; *argptr; argptr++) { |
| if (unalias(*argptr)) { |
| out2fmt("%s: %s not found\n", "unalias", *argptr); |
| i = 1; |
| } |
| } |
| |
| 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 struct alias * |
| freealias(struct alias *ap) { |
| struct alias *next; |
| |
| if (ap->flag & ALIASINUSE) { |
| ap->flag |= ALIASDEAD; |
| return ap; |
| } |
| |
| 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); |
| |
| for (; *app; app = &(*app)->next) { |
| if (equal(name, (*app)->name)) { |
| break; |
| } |
| } |
| |
| return app; |
| } |
| #endif |
| |
| #ifdef ASH_MATH_SUPPORT |
| /* The generated file arith.c has been replaced with a custom hand |
| * written implementation written by Aaron Lehmann <aaronl@vitelus.com>. |
| * This is now part of libbb, so that it can be used by all the shells |
| * in busybox. */ |
| #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); |
| #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 */ |
| |
| /* |
| * This file was generated by the mkbuiltins program. |
| */ |
| |
| #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 letcmd (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 }, |
| { 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_REGULAR "let", letcmd }, |
| #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 */ |
| |
| /* |
| * A job structure contains information about a job. A job is either a |
| * single process or a set of processes contained in a pipeline. In the |
| * latter case, pidlist will be non-NULL, and will point to a -1 terminated |
| * array of pids. |
| */ |
| |
| struct procstat { |
| 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; |
| { |
| 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; |
| |
| } |
| } |
| error("can't cd to %s", dest); |
| /* NOTREACHED */ |
| } |
| |
| |
| /* |
| * 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; |
| |
| 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; |
| } |
| } |
| |
| INTOFF; |
| if (chdir(dest) < 0) { |
| INTON; |
| return -1; |
| } |
| updatepwd(badstat ? NULL : dest); |
| INTON; |
| if (print && iflag) |
| printf(snlfmt, curdir); |
| return 0; |
| } |
| |
| |
| /* |
| * 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; |
| |
| if ((p = cdcomppath) == NULL) |
| return NULL; |
| start = cdcomppath; |
| while (*p != '/' && *p != '\0') |
| p++; |
| if (*p == '\0') { |
| cdcomppath = NULL; |
| } else { |
| *p++ = '\0'; |
| cdcomppath = p; |
| } |
| return start; |
| } |
| |
| |
| |
| /* |
| * Update curdir (the name of the current directory) in response to a |
| * cd command. We also call hashcd to let the routines in exec.c know |
| * that the current directory has changed. |
| */ |
| |
| static void hashcd (void); |
| |
| static void |
| updatepwd(const 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); |
| } |
| |
| |
| #ifndef BB_PWD |
| static int |
| pwdcmd(argc, argv) |
| int argc; |
| char **argv; |
| { |
| printf(snlfmt, curdir); |
| return 0; |
| } |
| #endif |
| |
| /* |
| * 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 { |
| curdir = savestr(val); |
| } |
| INTON; |
| setvar("PWD", curdir, VEXPORT); |
| } |
| |
| /* |
| * Errors and exceptions. |
| */ |
| |
| /* |
| * Code to handle exceptions in C. |
| */ |
| |
| /* |
| * 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. |
| */ |
| |
| struct jmploc { |
| jmp_buf loc; |
| }; |
| |
| /* 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__)); |
| |
| /* |
| * 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 |
| exraise(int e) |
| { |
| #ifdef DEBUG |
| if (handler == NULL) |
| abort(); |
| #endif |
| flushall(); |
| 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.) |
| */ |
| |
| static void |
| onint(void) { |
| sigset_t mysigset; |
| |
| 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 */ |
| |
| /* |
| * 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 void |
| exverror(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) |
| out2fmt("%s: ", commandname); |
| vfprintf(stderr, msg, ap); |
| out2c('\n'); |
| } |
| exraise(cond); |
| /* NOTREACHED */ |
| } |
| |
| |
| static void |
| error(const char *msg, ...) |
| { |
| va_list ap; |
| va_start(ap, msg); |
| exverror(EXERROR, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| |
| static void |
| exerror(int cond, const char *msg, ...) |
| { |
| va_list ap; |
| va_start(ap, msg); |
| exverror(cond, msg, ap); |
| /* NOTREACHED */ |
| va_end(ap); |
| } |
| |
| |
| |
| /* |
| * Table of error messages. |
| */ |
| |
| struct errname { |
| short errcode; /* error number */ |
| char action; /* operation which encountered the error */ |
| }; |
| |
| /* |
| * Types of operations (passed to the errmsg routine). |
| */ |
| |
| #define E_OPEN 01 /* opening a file */ |
| #define E_CREAT 02 /* creating a file */ |
| #define E_EXEC 04 /* executing a program */ |
| |
| #define ALL (E_OPEN|E_CREAT|E_EXEC) |
| |
| 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 |
| #ifdef ELOOP |
| { ELOOP, ALL }, |
| #endif |
| { E2BIG, E_EXEC }, |
| #ifdef ELIBACC |
| { ELIBACC, E_EXEC }, |
| #endif |
| }; |
| |
| #define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname)) |
| |
| /* |
| * 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 const char * |
| errmsg(int e, int action) |
| { |
| struct errname const *ep; |
| static char buf[12]; |
| |
| for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) { |
| if (ep->errcode == e && (ep->action & action) != 0) |
| return strerror(e); |
| } |
| |
| snprintf(buf, sizeof buf, "error %d", e); |
| return buf; |
| } |
| |
| |
| #ifdef ASH_OPTIMIZE_FOR_SIZE |
| static void |
| __inton() { |
| if (--suppressint == 0 && intpending) { |
| onint(); |
| } |
| } |
| 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 */ |
| |
| 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. |
| */ |
| |
| /* |
| * The eval commmand. |
| */ |
| static void evalstring (char *, int); |
| |
| static int |
| evalcmd(argc, argv) |
| int argc; |
| char **argv; |
| { |
| char *p; |
| char *concat; |
| char **ap; |
| |
| if (argc > 1) { |
| p = argv[1]; |
| if (argc > 2) { |
| STARTSTACKSTR(concat); |
| ap = argv + 2; |
| for (;;) { |
| while (*p) |
| STPUTC(*p++, concat); |
| if ((p = *ap++) == NULL) |
| break; |
| STPUTC(' ', concat); |
| } |
| STPUTC('\0', concat); |
| p = grabstackstr(concat); |
| } |
| evalstring(p, EV_TESTED); |
| } |
| return exitstatus; |
| } |
| |
| /* |
| * 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 *); |
| |
| /* |
| * Make a copy of a parse tree. |
| */ |
| |
| 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 inline union node * |
| copyfunc(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); |
| } |
| |
| /* |
| * Free a parse tree. |
| */ |
| |
| static void |
| freefunc(union node *n) |
| { |
| if (n) |
| ckfree(n); |
| } |
| |
| |
| /* |
| * Add a new command entry, replacing any existing command entry for |
| * the same name. |
| */ |
| |
| static inline void |
| addcmdentry(char *name, struct cmdentry *entry) |
| { |
| struct tblentry *cmdp; |
| |
| INTOFF; |
| cmdp = cmdlookup(name, 1); |
| if (cmdp->cmdtype == CMDFUNCTION) { |
| freefunc(cmdp->param.func); |
| } |
| cmdp->cmdtype = entry->cmdtype; |
| cmdp->param = entry->u; |
| INTON; |
| } |
| |
| 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; |
| } |
| if (n->type == NWHILE) { |
| if (exitstatus != 0) |
| break; |
| } else { |
| if (exitstatus == 0) |
| break; |
| } |
| evaltree(n->nbinary.ch2, flags & EV_TESTED); |
| status = exitstatus; |
| if (evalskip) |
| goto skipping; |
| } |
| loopnest--; |
| exitstatus = status; |
| } |
| |
| static void |
| evalfor(const union node *n, int flags) |
| { |
| struct arglist arglist; |
| union node *argp; |
| struct strlist *sp; |
| struct stackmark smark; |
| |
| setstackmark(&smark); |
| arglist.lastp = &arglist.list; |
| for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { |
| oexitstatus = exitstatus; |
| expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | 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; |
| } |
| } |
| loopnest--; |
| out: |
| popstackmark(&smark); |
| } |
| |
| static inline void |
| evalcase(const union node *n, int flags) |
| { |
| union node *cp; |
| union node *patp; |
| struct arglist arglist; |
| struct stackmark smark; |
| |
| setstackmark(&smark); |
| arglist.lastp = &arglist.list; |
| oexitstatus = exitstatus; |
| expandarg(n->ncase.expr, &arglist, EXP_TILDE); |
| for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { |
| for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { |
| if (casematch(patp, arglist.list->text)) { |
| if (evalskip == 0) { |
| evaltree(cp->nclist.body, flags); |
| } |
| goto out; |
| } |
| } |
| } |
| out: |
| popstackmark(&smark); |
| } |
| |
| /* |
| * Evaluate a pipeline. All the processes in the pipeline are children |
| * of the process creating the pipeline. (This differs from some versions |
| * of the shell, which make the last process in a pipeline the parent |
| * of all the rest.) |
| */ |
| |
| static inline void |
| evalpipe(n) |
| union node *n; |
| { |
| struct job *jp; |
| struct nodelist *lp; |
| int pipelen; |
| int prevfd; |
| int pip[2]; |
| |
| TRACE(("evalpipe(0x%lx) called\n", (long)n)); |
| pipelen = 0; |
| for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) |
| pipelen++; |
| INTOFF; |
| jp = makejob(n, pipelen); |
| prevfd = -1; |
| for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { |
| prehash(lp->n); |
| pip[1] = -1; |
| if (lp->next) { |
| if (pipe(pip) < 0) { |
| close(prevfd); |
| error("Pipe call failed"); |
| } |
| } |
| if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { |
| INTON; |
| if (prevfd > 0) { |
| close(0); |
| 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); |
| } |
| if (prevfd >= 0) |
| close(prevfd); |
| prevfd = pip[0]; |
| close(pip[1]); |
| } |
| INTON; |
| if (n->npipe.backgnd == 0) { |
| INTOFF; |
| exitstatus = waitforjob(jp); |
| TRACE(("evalpipe: job done exit status %d\n", exitstatus)); |
| INTON; |
| } |
| } |
| |
| static void find_command (const char *, struct cmdentry *, int, const char *); |
| |
| static int |
| isassignment(const char *word) { |
| if (!is_name(*word)) { |
| return 0; |
| } |
| do { |
| word++; |
| } while (is_in_name(*word)); |
| return *word == '='; |
| } |
| |
| |
| static void |
| 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); |
| } |
| } |
| *arglist.lastp = NULL; |
| *varlist.lastp = NULL; |
| expredir(cmd->ncmd.redirect); |
| argc = 0; |
| for (sp = arglist.list ; sp ; sp = sp->next) |
| argc++; |
| argv = stalloc(sizeof (char *) * (argc + 1)); |
| |
| for (sp = arglist.list ; sp ; sp = sp->next) { |
| TRACE(("evalcommand arg: %s\n", sp->text)); |
| *argv++ = sp->text; |
| } |
| *argv = NULL; |
| lastarg = NULL; |
| if (iflag && funcnest == 0 && argc > 0) |
| lastarg = argv[-1]; |
| argv -= argc; |
| |
| /* Print the command if xflag is set. */ |
| if (xflag) { |
| out2c('+'); |
| eprintlist(varlist.list); |
| eprintlist(arglist.list); |
| out2c('\n'); |
| } |
| |
| /* 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; |
| |
| /* |
| * 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; |
| |
| 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; |
| } |
| } |
| |
| /* 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; |
| } |
| |
| /* 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; |
| 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); |
| } |
| |
| /* |
| * Evaluate a parse tree. The value is left in the global variable |
| * exitstatus. |
| */ |
| static void |
| evaltree(n, flags) |
| union node *n; |
| int flags; |
| { |
| 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; |
| } |
| entry.cmdtype = CMDFUNCTION; |
| entry.u.func = copyfunc(n->narg.next); |
| addcmdentry(n->narg.text, &entry); |
| exitstatus = 0; |
| break; |
| } |
| 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 |
| } |
| out: |
| if (pendingsigs) |
| dotrap(); |
| if ( |
| flags & EV_EXIT || |
| (checkexit && eflag && exitstatus && !(flags & EV_TESTED)) |
| ) |
| exitshell(exitstatus); |
| } |
| |
| /* |
| * Kick off a subshell to evaluate a tree. |
| */ |
| |
| static void |
| evalsubshell(const union node *n, int flags) |
| { |
| struct job *jp; |
| int backgnd = (n->type == NBACKGND); |
| |
| expredir(n->nredir.redirect); |
| jp = makejob(n, 1); |
|
|