diff options
Diffstat (limited to 'ash.c')
-rw-r--r-- | ash.c | 12825 |
1 files changed, 0 insertions, 12825 deletions
@@ -1,12825 +0,0 @@ -/* 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 -#define IFS_BROKEN - -#include <assert.h> -#include <stddef.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 */ - -#define SYNBASE 130 -#define PEOF -130 - -#define PEOA -129 - -#define TEOF 0 -#define TNL 1 -#define TREDIR 2 -#define TWORD 3 -#define TASSIGN 4 -#define TSEMI 5 -#define TBACKGND 6 -#define TAND 7 -#define TOR 8 -#define TPIPE 9 -#define TLP 10 -#define TRP 11 -#define TENDCASE 12 -#define TENDBQUOTE 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 - - - -/* 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_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)))) - -/* - * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise - * (assuming ascii char codes, as the original implementation did) - */ -#define is_special(c) \ - ( (((unsigned int)c) - 33 < 32) \ - && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1)) - -#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 inline 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 - - -#ifdef ASH_OPTIMIZE_FOR_SIZE -#define USE_SIT_FUNCTION -#endif - -/* number syntax index */ -#define BASESYNTAX 0 /* not in quotes */ -#define DQSYNTAX 1 /* in double quotes */ -#define SQSYNTAX 2 /* in single quotes */ -#define ARISYNTAX 3 /* in arithmetic */ - -static const char S_I_T[][4] = { - /* 0 */ { CSPCL, CIGN, CIGN, CIGN }, /* PEOA */ - /* 1 */ { CSPCL, CWORD, CWORD, CWORD }, /* ' ' */ - /* 2 */ { CNL, CNL, CNL, CNL }, /* \n */ - /* 3 */ { CWORD, CCTL, CCTL, CWORD }, /* !*-/:=?[]~ */ - /* 4 */ { CDQUOTE, CENDQUOTE, CWORD, CDQUOTE }, /* '"' */ - /* 5 */ { CVAR, CVAR, CWORD, CVAR }, /* $ */ - /* 6 */ { CSQUOTE, CWORD, CENDQUOTE, CSQUOTE }, /* "'" */ - /* 7 */ { CSPCL, CWORD, CWORD, CLP }, /* ( */ - /* 8 */ { CSPCL, CWORD, CWORD, CRP }, /* ) */ - /* 9 */ { CBACK, CBACK, CCTL, CBACK }, /* \ */ - /* 10 */ { CBQUOTE, CBQUOTE, CWORD, CBQUOTE }, /* ` */ - /* 11 */ { CENDVAR, CENDVAR, CWORD, CENDVAR }, /* } */ -#ifndef USE_SIT_FUNCTION - /* 12 */ { CENDFILE, CENDFILE, CENDFILE, CENDFILE }, /* PEOF */ - /* 13 */ { CWORD, CWORD, CWORD, CWORD }, /* 0-9A-Za-z */ - /* 14 */ { CCTL, CCTL, CCTL, CCTL } /* CTLESC ... */ -#endif -}; - -#ifdef USE_SIT_FUNCTION - -#define U_C(c) ((unsigned char)(c)) - -static int SIT(int c, int syntax) -{ - static const char spec_symbls[]="\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; - static const char syntax_index_table [] = { - 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ - 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ - 3, 1, 3, 3, 9, 3,10, 1, /* "=>?[\\]`|" */ - 11,3 }; /* "}~" */ - const char *s; - int indx; - - if(c==PEOF) /* 2^8+2 */ - return CENDFILE; - if(c==PEOA) /* 2^8+1 */ - indx = 0; - else if(U_C(c)>=U_C(CTLESC) && U_C(c)<=U_C(CTLQUOTEMARK)) - return CCTL; - else { - s = strchr(spec_symbls, c); - if(s==0) - return CWORD; - indx = syntax_index_table[(s-spec_symbls)]; - } - return S_I_T[indx][syntax]; -} - -#else /* USE_SIT_FUNCTION */ - -#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax] - -#define CSPCL_CIGN_CIGN_CIGN 0 -#define CSPCL_CWORD_CWORD_CWORD 1 -#define CNL_CNL_CNL_CNL 2 -#define CWORD_CCTL_CCTL_CWORD 3 -#define CDQUOTE_CENDQUOTE_CWORD_CDQUOTE 4 -#define CVAR_CVAR_CWORD_CVAR 5 -#define CSQUOTE_CWORD_CENDQUOTE_CSQUOTE 6 -#define CSPCL_CWORD_CWORD_CLP 7 -#define CSPCL_CWORD_CWORD_CRP 8 -#define CBACK_CBACK_CCTL_CBACK 9 -#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 10 -#define CENDVAR_CENDVAR_CWORD_CENDVAR 11 -#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 12 -#define CWORD_CWORD_CWORD_CWORD 13 -#define CCTL_CCTL_CCTL_CCTL 14 - -static const char syntax_index_table[258] = { - /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ - /* 0 -130 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, - /* 1 -129 PEOA */ CSPCL_CIGN_CIGN_CIGN, - /* 2 -128 0xff */ CWORD_CWORD_CWORD_CWORD, - /* 3 -127 */ CCTL_CCTL_CCTL_CCTL, /* CTLQUOTEMARK */ - /* 4 -126 */ CCTL_CCTL_CCTL_CCTL, - /* 5 -125 */ CCTL_CCTL_CCTL_CCTL, - /* 6 -124 */ CCTL_CCTL_CCTL_CCTL, - /* 7 -123 */ CCTL_CCTL_CCTL_CCTL, - /* 8 -122 */ CCTL_CCTL_CCTL_CCTL, - /* 9 -121 */ CCTL_CCTL_CCTL_CCTL, - /* 10 -120 */ CCTL_CCTL_CCTL_CCTL, /* CTLESC */ - /* 11 -119 */ CWORD_CWORD_CWORD_CWORD, - /* 12 -118 */ CWORD_CWORD_CWORD_CWORD, - /* 13 -117 */ CWORD_CWORD_CWORD_CWORD, - /* 14 -116 */ CWORD_CWORD_CWORD_CWORD, - /* 15 -115 */ CWORD_CWORD_CWORD_CWORD, - /* 16 -114 */ CWORD_CWORD_CWORD_CWORD, - /* 17 -113 */ CWORD_CWORD_CWORD_CWORD, - /* 18 -112 */ CWORD_CWORD_CWORD_CWORD, - /* 19 -111 */ CWORD_CWORD_CWORD_CWORD, - /* 20 -110 */ CWORD_CWORD_CWORD_CWORD, - /* 21 -109 */ CWORD_CWORD_CWORD_CWORD, - /* 22 -108 */ CWORD_CWORD_CWORD_CWORD, - /* 23 -107 */ CWORD_CWORD_CWORD_CWORD, - /* 24 -106 */ CWORD_CWORD_CWORD_CWORD, - /* 25 -105 */ CWORD_CWORD_CWORD_CWORD, - /* 26 -104 */ CWORD_CWORD_CWORD_CWORD, - /* 27 -103 */ CWORD_CWORD_CWORD_CWORD, - /* 28 -102 */ CWORD_CWORD_CWORD_CWORD, - /* 29 -101 */ CWORD_CWORD_CWORD_CWORD, - /* 30 -100 */ CWORD_CWORD_CWORD_CWORD, - /* 31 -99 */ CWORD_CWORD_CWORD_CWORD, - /* 32 -98 */ CWORD_CWORD_CWORD_CWORD, - /* 33 -97 */ CWORD_CWORD_CWORD_CWORD, - /* 34 -96 */ CWORD_CWORD_CWORD_CWORD, - /* 35 -95 */ CWORD_CWORD_CWORD_CWORD, - /* 36 -94 */ CWORD_CWORD_CWORD_CWORD, - /* 37 -93 */ CWORD_CWORD_CWORD_CWORD, - /* 38 -92 */ CWORD_CWORD_CWORD_CWORD, - /* 39 -91 */ CWORD_CWORD_CWORD_CWORD, - /* 40 -90 */ CWORD_CWORD_CWORD_CWORD, - /* 41 -89 */ CWORD_CWORD_CWORD_CWORD, - /* 42 -88 */ CWORD_CWORD_CWORD_CWORD, - /* 43 -87 */ CWORD_CWORD_CWORD_CWORD, - /* 44 -86 */ CWORD_CWORD_CWORD_CWORD, - /* 45 -85 */ CWORD_CWORD_CWORD_CWORD, - /* 46 -84 */ CWORD_CWORD_CWORD_CWORD, - /* 47 -83 */ CWORD_CWORD_CWORD_CWORD, - /* 48 -82 */ CWORD_CWORD_CWORD_CWORD, - /* 49 -81 */ CWORD_CWORD_CWORD_CWORD, - /* 50 -80 */ CWORD_CWORD_CWORD_CWORD, - /* 51 -79 */ CWORD_CWORD_CWORD_CWORD, - /* 52 -78 */ CWORD_CWORD_CWORD_CWORD, - /* 53 -77 */ CWORD_CWORD_CWORD_CWORD, - /* 54 -76 */ CWORD_CWORD_CWORD_CWORD, - /* 55 -75 */ CWORD_CWORD_CWORD_CWORD, - /* 56 -74 */ CWORD_CWORD_CWORD_CWORD, - /* 57 -73 */ CWORD_CWORD_CWORD_CWORD, - /* 58 -72 */ CWORD_CWORD_CWORD_CWORD, - /* 59 -71 */ CWORD_CWORD_CWORD_CWORD, - /* 60 -70 */ CWORD_CWORD_CWORD_CWORD, - /* 61 -69 */ CWORD_CWORD_CWORD_CWORD, - /* 62 -68 */ CWORD_CWORD_CWORD_CWORD, - /* 63 -67 */ CWORD_CWORD_CWORD_CWORD, - /* 64 -66 */ CWORD_CWORD_CWORD_CWORD, - /* 65 -65 */ CWORD_CWORD_CWORD_CWORD, - /* 66 -64 */ CWORD_CWORD_CWORD_CWORD, - /* 67 -63 */ CWORD_CWORD_CWORD_CWORD, - /* 68 -62 */ CWORD_CWORD_CWORD_CWORD, - /* 69 -61 */ CWORD_CWORD_CWORD_CWORD, - /* 70 -60 */ CWORD_CWORD_CWORD_CWORD, - /* 71 -59 */ CWORD_CWORD_CWORD_CWORD, - /* 72 -58 */ CWORD_CWORD_CWORD_CWORD, - /* 73 -57 */ CWORD_CWORD_CWORD_CWORD, - /* 74 -56 */ CWORD_CWORD_CWORD_CWORD, - /* 75 -55 */ CWORD_CWORD_CWORD_CWORD, - /* 76 -54 */ CWORD_CWORD_CWORD_CWORD, - /* 77 -53 */ CWORD_CWORD_CWORD_CWORD, - /* 78 -52 */ CWORD_CWORD_CWORD_CWORD, - /* 79 -51 */ CWORD_CWORD_CWORD_CWORD, - /* 80 -50 */ CWORD_CWORD_CWORD_CWORD, - /* 81 -49 */ CWORD_CWORD_CWORD_CWORD, - /* 82 -48 */ CWORD_CWORD_CWORD_CWORD, - /* 83 -47 */ CWORD_CWORD_CWORD_CWORD, - /* 84 -46 */ CWORD_CWORD_CWORD_CWORD, - /* 85 -45 */ CWORD_CWORD_CWORD_CWORD, - /* 86 -44 */ CWORD_CWORD_CWORD_CWORD, - /* 87 -43 */ CWORD_CWORD_CWORD_CWORD, - /* 88 -42 */ CWORD_CWORD_CWORD_CWORD, - /* 89 -41 */ CWORD_CWORD_CWORD_CWORD, - /* 90 -40 */ CWORD_CWORD_CWORD_CWORD, - /* 91 -39 */ CWORD_CWORD_CWORD_CWORD, - /* 92 -38 */ CWORD_CWORD_CWORD_CWORD, - /* 93 -37 */ CWORD_CWORD_CWORD_CWORD, - /* 94 -36 */ CWORD_CWORD_CWORD_CWORD, - /* 95 -35 */ CWORD_CWORD_CWORD_CWORD, - /* 96 -34 */ CWORD_CWORD_CWORD_CWORD, - /* 97 -33 */ CWORD_CWORD_CWORD_CWORD, - /* 98 -32 */ CWORD_CWORD_CWORD_CWORD, - /* 99 -31 */ CWORD_CWORD_CWORD_CWORD, - /* 100 -30 */ CWORD_CWORD_CWORD_CWORD, - /* 101 -29 */ CWORD_CWORD_CWORD_CWORD, - /* 102 -28 */ CWORD_CWORD_CWORD_CWORD, - /* 103 -27 */ CWORD_CWORD_CWORD_CWORD, - /* 104 -26 */ CWORD_CWORD_CWORD_CWORD, - /* 105 -25 */ CWORD_CWORD_CWORD_CWORD, - /* 106 -24 */ CWORD_CWORD_CWORD_CWORD, - /* 107 -23 */ CWORD_CWORD_CWORD_CWORD, - /* 108 -22 */ CWORD_CWORD_CWORD_CWORD, - /* 109 -21 */ CWORD_CWORD_CWORD_CWORD, - /* 110 -20 */ CWORD_CWORD_CWORD_CWORD, - /* 111 -19 */ CWORD_CWORD_CWORD_CWORD, - /* 112 -18 */ CWORD_CWORD_CWORD_CWORD, - /* 113 -17 */ CWORD_CWORD_CWORD_CWORD, - /* 114 -16 */ CWORD_CWORD_CWORD_CWORD, - /* 115 -15 */ CWORD_CWORD_CWORD_CWORD, - /* 116 -14 */ CWORD_CWORD_CWORD_CWORD, - /* 117 -13 */ CWORD_CWORD_CWORD_CWORD, - /* 118 -12 */ CWORD_CWORD_CWORD_CWORD, - /* 119 -11 */ CWORD_CWORD_CWORD_CWORD, - /* 120 -10 */ CWORD_CWORD_CWORD_CWORD, - /* 121 -9 */ CWORD_CWORD_CWORD_CWORD, - /* 122 -8 */ CWORD_CWORD_CWORD_CWORD, - /* 123 -7 */ CWORD_CWORD_CWORD_CWORD, - /* 124 -6 */ CWORD_CWORD_CWORD_CWORD, - /* 125 -5 */ CWORD_CWORD_CWORD_CWORD, - /* 126 -4 */ CWORD_CWORD_CWORD_CWORD, - /* 127 -3 */ CWORD_CWORD_CWORD_CWORD, - /* 128 -2 */ CWORD_CWORD_CWORD_CWORD, - /* 129 -1 */ CWORD_CWORD_CWORD_CWORD, - /* 130 0 */ CWORD_CWORD_CWORD_CWORD, - /* 131 1 */ CWORD_CWORD_CWORD_CWORD, - /* 132 2 */ CWORD_CWORD_CWORD_CWORD, - /* 133 3 */ CWORD_CWORD_CWORD_CWORD, - /* 134 4 */ CWORD_CWORD_CWORD_CWORD, - /* 135 5 */ CWORD_CWORD_CWORD_CWORD, - /* 136 6 */ CWORD_CWORD_CWORD_CWORD, - /* 137 7 */ CWORD_CWORD_CWORD_CWORD, - /* 138 8 */ CWORD_CWORD_CWORD_CWORD, - /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, - /* 140 10 "\n" */ CNL_CNL_CNL_CNL, - /* 141 11 */ CWORD_CWORD_CWORD_CWORD, - /* 142 12 */ CWORD_CWORD_CWORD_CWORD, - /* 143 13 */ CWORD_CWORD_CWORD_CWORD, - /* 144 14 */ CWORD_CWORD_CWORD_CWORD, - /* 145 15 */ CWORD_CWORD_CWORD_CWORD, - /* 146 16 */ CWORD_CWORD_CWORD_CWORD, - /* 147 17 */ CWORD_CWORD_CWORD_CWORD, - /* 148 18 */ CWORD_CWORD_CWORD_CWORD, - /* 149 19 */ CWORD_CWORD_CWORD_CWORD, - /* 150 20 */ CWORD_CWORD_CWORD_CWORD, - /* 151 21 */ CWORD_CWORD_CWORD_CWORD, - /* 152 22 */ CWORD_CWORD_CWORD_CWORD, - /* 153 23 */ CWORD_CWORD_CWORD_CWORD, - /* 154 24 */ CWORD_CWORD_CWORD_CWORD, - /* 155 25 */ CWORD_CWORD_CWORD_CWORD, - /* 156 26 */ CWORD_CWORD_CWORD_CWORD, - /* 157 27 */ CWORD_CWORD_CWORD_CWORD, - /* 158 28 */ CWORD_CWORD_CWORD_CWORD, - /* 159 29 */ CWORD_CWORD_CWORD_CWORD, - /* 160 30 */ CWORD_CWORD_CWORD_CWORD, - /* 161 31 */ CWORD_CWORD_CWORD_CWORD, - /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD, - /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD, - /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CDQUOTE, - /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD, - /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR, - /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD, - /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD, - /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CSQUOTE, - /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP, - /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP, - /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD, - /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD, - /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD, - /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD, - /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD, - /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD, - /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD, - /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD, - /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD, - /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD, - /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD, - /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD, - /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD, - /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD, - /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD, - /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD, - /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD, - /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD, - /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD, - /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD, - /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD, - /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD, - /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD, - /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD, - /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD, - /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD, - /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD, - /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD, - /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD, - /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD, - /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD, - /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD, - /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD, - /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD, - /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD, - /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD, - /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD, - /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD, - /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD, - /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD, - /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD, - /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD, - /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD, - /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD, - /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD, - /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD, - /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD, - /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD, - /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD, - /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD, - /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK, - /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD, - /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD, - /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD, - /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, - /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD, - /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD, - /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD, - /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD, - /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD, - /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD, - /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD, - /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD, - /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD, - /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD, - /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD, - /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD, - /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD, - /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD, - /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD, - /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD, - /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD, - /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD, - /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD, - /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD, - /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD, - /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD, - /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD, - /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD, - /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD, - /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD, - /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD, - /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD, - /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, - /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD, - /* 257 127 */ CWORD_CWORD_CWORD_CWORD, -}; - -#endif /* USE_SIT_FUNCTION */ - - -/* first char is indicating which tokens mark the end of a list */ -static const char *const tokname_array[] = { - "\1end of file", - "\0newline", - "\0redirection", - "\0word", - "\0assignment", - "\0;", - "\0&", - "\0&&", - "\0||", - "\0|", - "\0(", - "\1)", - "\1;;", - "\1`", -#define KWDOFFSET 14 - /* the following are keywords */ - "\0!", - "\0case", - "\1do", - "\1done", - "\1elif", - "\1else", - "\1esac", - "\1fi", - "\0for", - "\0if", - "\0in", - "\1then", - "\0until", - "\0while", - "\0{", - "\1}", -}; - -static const char *tokname(int tok) -{ - static char buf[16]; - - if(tok>=TSEMI) - buf[0] = '"'; - sprintf(buf+(tok>=TSEMI), "%s%c", - tokname_array[tok]+1, (tok>=TSEMI ? '"' : 0)); - return buf; -} - -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. */ -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) ) - -#define 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(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); - 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); - -static void -expredir(union node *n) -{ - union node *redir; - - 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; - } - } -} - - -/* - * 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 -evalbackcmd(union node *n, struct backcmd *result) -{ - int pip[2]; - struct job *jp; - struct stackmark smark; /* unnecessary */ - - setstackmark(&smark); - result->fd = -1; - result->buf = NULL; - result->nleft = 0; - result->jp = NULL; - if (n == NULL) { - exitstatus = 0; - goto out; - } - 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)); -} - - -/* - * 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 -prehash(n) - union node *n; -{ - struct cmdentry entry; - - if (n->type == NCMD && n->ncmd.args) - if (goodname(n->ncmd.args->narg.text)) - find_command(n->ncmd.args->narg.text, &entry, 0, - pathval()); -} - - -/* - * Builtin commands. Builtin commands whose functions are closely - * tied to evaluation are implemented here. - */ - -/* - * No command given, or a bltin command with no arguments. Set the - * specified variables. - */ - -int -bltincmd(argc, argv) - int argc; - char **argv; -{ - /* - * Preserve exitstatus of a previous possible redirection - * as POSIX mandates - */ - return exitstatus; -} - - -/* - * Handle break and continue commands. Break, continue, and return are - * all handled by setting the evalskip flag. The evaluation routines - * above all check this flag, and if it is set they start skipping - * commands rather than executing them. The variable skipcount is - * the number of loops to break/continue, or the number of function - * levels to return. (The latter is always 1.) It should probably - * be an error to break out of more loops than exist, but it isn't - * in the standard shell so we don't make it one here. - */ - -static int -breakcmd(argc, argv) - int argc; - char **argv; -{ - int n = argc > 1 ? number(argv[1]) : 1; - - 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; - } -} - - -#ifndef BB_TRUE_FALSE -static int -false_main(argc, argv) - int argc; - char **argv; -{ - return 1; -} - - -static int -true_main(argc, argv) - int argc; - char **argv; -{ - return 0; -} -#endif - -/* - * Controls whether the shell is interactive or not. - */ - -static void setsignal(int signo); -static void chkmail(int silent); - - -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 */ -#ifndef BB_FEATURE_SH_EXTRA_QUIET - printf( "\n\n" BB_BANNER " Built-in shell (ash)\n"); - printf( "Enter 'help' for a list of built-in commands.\n\n"); -#endif - do_banner=1; - } -} - -static void -optschanged(void) -{ - setinteractive(iflag); - setjobctl(mflag); -} - - -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; -} - -static void -eprintlist(struct strlist *sp) -{ - for (; sp; sp = sp->next) { - out2fmt(" %s",sp->text); - } -} - -/* - * Exec a program. Never returns. If you change this routine, you may - * have to change the find_command routine as well. - */ - -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 */ -} - -/* - * Clear traps on a fork. - */ -static void -clear_traps(void) { - 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; - } - } -} - - -static void -initshellproc(void) { - -#ifdef ASH_ALIAS - /* from alias.c: */ - { - rmaliases(); - } -#endif - /* from eval.c: */ - { - exitstatus = 0; - } - - /* from exec.c: */ - { - deletefuncs(); - } - - /* from jobs.c: */ - { - backgndpid = -1; -#ifdef JOBS - jobctl = 0; -#endif - } - - /* from options.c: */ - { - int i; - - for (i = 0; i < NOPTS; i++) - optent_val(i) = 0; - optschanged(); - - } - - /* 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); - -/* - * Read a character from the script, returning PEOF on end of file. - * Nul characters in the input are silently discarded. - */ - -#ifndef ASH_OPTIMIZE_FOR_SIZE -#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) -static int -pgetc(void) -{ - return pgetc_macro(); -} -#else -static int -pgetc_macro(void) -{ - return --parsenleft >= 0? *parsenextc++ : preadbuffer(); -} - -static inline int -pgetc(void) -{ - return pgetc_macro(); -} -#endif - - -/* - * Undo the last call to pgetc. Only one character may be pushed back. - * PEOF may be pushed back. - */ - -static void pungetc(void) -{ - parsenleft++; - parsenextc--; -} - - -static void -popfile(void) { - struct parsefile *pf = parsefile; - - INTOFF; - if (pf->fd >= 0) - close(pf->fd); - if (pf->buf) - ckfree(pf->buf); - while (pf->strpush) - popstring(); - parsefile = pf->prev; - ckfree(pf); - parsenleft = parsefile->nleft; - parselleft = parsefile->lleft; - parsenextc = parsefile->nextc; - plinno = parsefile->linno; - INTON; -} - - -/* - * Return to top level. - */ - -static void -popallfiles(void) { - while (parsefile != &basepf) - popfile(); -} - -/* - * Close the file(s) that the shell is reading commands from. Called - * after a fork is done. - */ - -static void closescript(void) -{ - popallfiles(); - if (parsefile->fd > 0) { - close(parsefile->fd); - parsefile->fd = 0; - } -} - - -/* - * Like setinputfile, but takes an open file descriptor. Call this with - * interrupts off. - */ - -static void setinputfd(int fd, int 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; -} - - -/* - * Set the input to take input from a file. If push is set, push the - * old input onto the stack first. - */ - -static void -setinputfile(const char *fname, int push) -{ - 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; - } - setinputfd(fd, push); - INTON; -} - - -static void -tryexec(char *cmd, char **argv, char **envp) -{ - int e; - -#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); - } - 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 void growstackblock(void); - - -static char * -padvance(const char **path, const char *name) -{ - const char *p; - char *q; - const char *start; - int len; - - if (*path == NULL) - return NULL; - start = *path; - for (p = start ; *p && *p != ':' && *p != '%' ; p++); - len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ - while (stackblocksize() < len) - growstackblock(); - q = stackblock(); - if (p != start) { - memcpy(q, start, p - start); - q += p - start; - *q++ = '/'; - } - strcpy(q, name); - pathopt = NULL; - if (*p == '%') { - pathopt = ++p; - while (*p && *p != ':') p++; - } - if (*p == ':') - *path = p + 1; - else - *path = NULL; - return stalloc(len); -} - -/* - * Wrapper around strcmp for qsort/bsearch/... - */ -static int -pstrcmp(const void *a, const void *b) -{ - return strcmp((const char *) a, (*(const char *const *) b) + 1); -} - -/* - * Find a keyword is in a sorted array. - */ - -static const char *const * -findkwd(const char *s) -{ - return bsearch(s, tokname_array + KWDOFFSET, - (sizeof(tokname_array)/sizeof(const char *)) - KWDOFFSET, - sizeof(const char *), pstrcmp); -} - - -/*** Command hashing code ***/ - - -static int -hashcmd(argc, argv) - int argc; - char **argv; -{ - struct tblentry **pp; - struct tblentry *cmdp; - int c; - int verbose; - struct cmdentry entry; - char *name; -#ifdef ASH_ALIAS - const struct alias *ap; -#endif - - verbose = 0; - while ((c = nextopt("rvV")) != '\0') { - if (c == 'r') { - clearcmdentry(0); - return 0; - } else if (c == 'v' || c == 'V') { - verbose = c; - } - } - 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; - } - - 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; -} - -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); -} - - - -/*** List the available builtins ***/ - - -static int helpcmd(int argc, char** argv) -{ - int col, i; - - 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; - - for (i=0; i < NUM_APPLETS; i++) { - - col += printf("%c%s", ((col == 0) ? '\t' : ' '), - applets[i].name); - if (col > 60) { - printf("\n"); - col = 0; - } - } - } -#endif - printf("\n\n"); - return EXIT_SUCCESS; -} - -/* - * Resolve a command name. If you change this routine, you may have to - * change the shellexec routine as well. - */ - -static int prefix (const char *, const char *); - -static void -find_command(const char *name, struct cmdentry *entry, int act, const char *path) -{ - 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 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; - } - - 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; - } - } - - bcmd = find_builtin(name); - regular = bcmd && IS_BUILTIN_REGULAR(bcmd); - - 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 - prev = cmdp->param.index; - } - - 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; - } - - /* 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; -} - - - -/* - * Search the table of builtin commands. - */ - -static int -bstrcmp(const void *name, const void *b) -{ - 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; - } - } -} - - - -/* - * 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(const char *newval) -{ - int firstchange; - int bltin; - - firstchange = path_change(newval, &bltin); - if (builtinloc < 0 && bltin >= 0) - builtinloc = bltin; /* zap builtins */ - clearcmdentry(firstchange); - builtinloc = bltin; -} - - -/* - * Clear out command entries. The argument specifies the first entry in - * PATH which has changed. - */ - -static void -clearcmdentry(firstchange) - int firstchange; -{ - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if ((cmdp->cmdtype == CMDNORMAL && - cmdp->param.index >= firstchange) - || (cmdp->cmdtype == CMDBUILTIN && - builtinloc >= firstchange)) { - *pp = cmdp->next; - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - -/* - * Delete all functions. - */ - -static void -deletefuncs(void) { - struct tblentry **tblp; - struct tblentry **pp; - struct tblentry *cmdp; - - INTOFF; - for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { - pp = tblp; - while ((cmdp = *pp) != NULL) { - if (cmdp->cmdtype == CMDFUNCTION) { - *pp = cmdp->next; - freefunc(cmdp->param.func); - ckfree(cmdp); - } else { - pp = &cmdp->next; - } - } - } - INTON; -} - - - -/* - * Locate a command in the command hash table. If "add" is nonzero, - * add the command to the table if it is not already present. The - * variable "lastcmdentry" is set to point to the address of the link - * pointing to the entry, so that delete_cmd_entry can delete the - * entry. - */ - -static struct tblentry **lastcmdentry; - -static struct tblentry * -cmdlookup(const char *name, int add) -{ - int hashval; - const char *p; - struct tblentry *cmdp; - struct tblentry **pp; - - p = name; - hashval = *p << 4; - while (*p) - hashval += *p++; - hashval &= 0x7FFF; - pp = &cmdtable[hashval % CMDTABLESIZE]; - for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { - if (equal(cmdp->cmdname, name)) - break; - pp = &cmdp->next; - } - if (add && cmdp == NULL) { - INTOFF; - cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB - + strlen(name) + 1); - cmdp->next = NULL; - cmdp->cmdtype = CMDUNKNOWN; - cmdp->rehash = 0; - strcpy(cmdp->cmdname, name); - INTON; - } - lastcmdentry = pp; - return cmdp; -} - -/* - * Delete the command entry returned on the last lookup. - */ - -static void -delete_cmd_entry() { - struct tblentry *cmdp; - - INTOFF; - cmdp = *lastcmdentry; - *lastcmdentry = cmdp->next; - ckfree(cmdp); - INTON; -} - - - - - -static const unsigned char 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)), -}; - - - -/* - * Delete a function if it exists. - */ - -static void -unsetfunc(char *name) -{ - struct tblentry *cmdp; - - if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { - freefunc(cmdp->param.func); - delete_cmd_entry(); - } -} - - -/* - * Locate and print what a word is... - */ - -static int -typecmd(int argc, char **argv) -{ - int i; - int err = 0; - char *argv_a[2]; - - argv_a[1] = 0; - - for (i = 1; i < argc; i++) { - argv_a[0] = argv[i]; - argptr = argv_a; - optptr = "v"; - err |= hashcmd(argc, argv); - } - return err; -} - -#ifdef ASH_CMDCMD -static int -commandcmd(argc, argv) - int argc; - char **argv; -{ - int c; - int default_path = 0; - int verify_only = 0; - int verbose_verify_only = 0; - - 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; - } - - 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]; - - argv_a[1] = 0; - argv_a[0] = *argptr; - argptr = argv_a; - optptr = verbose_verify_only ? "v" : "V"; /* reverse special */ - return hashcmd(argc, argv); - } - - return 0; -} -#endif - -static int -path_change(newval, bltin) - const char *newval; - int *bltin; -{ - const char *old, *new; - int idx; - int firstchange; - - 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++; - } - 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 *, int, 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()); -} - - -/* - * 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. - */ - -static void -expandarg(arg, arglist, flag) - union node *arg; - struct arglist *arglist; - int flag; -{ - struct strlist *sp; - char *p; - - argbackq = arg->narg.backquote; - STARTSTACKSTR(expdest); - ifsfirst.next = NULL; - ifslastp = NULL; - argstr(arg->narg.text, flag); - if (arglist == NULL) { - return; /* here document expanded */ - } - STPUTC('\0', expdest); - p = grabstackstr(expdest); - exparg.lastp = &exparg.list; - /* - * TODO - EXP_REDIR - */ - if (flag & EXP_FULL) { - ifsbreakup(p, &exparg); - *exparg.lastp = NULL; - exparg.lastp = &exparg.list; - expandmeta(exparg.list, flag); - } else { - if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ - rmescapes(p); - sp = (struct strlist *)stalloc(sizeof (struct strlist)); - sp->text = p; - *exparg.lastp = sp; - exparg.lastp = &sp->next; - } - ifsfree(); - *exparg.lastp = NULL; - if (exparg.list) { - *arglist->lastp = exparg.list; - arglist->lastp = exparg.lastp; - } -} - - -/* - * Expand a variable, and return a pointer to the next character in the - * input string. - */ - -static inline char * evalvar(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); - - 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 - ); - } - } - } - - if (subtype == VSPLUS) - set = ! set; - - easy = ((varflags & VSQUOTE) == 0 || - (*var == '@' && shellparam.nparam != 1)); - - - switch (subtype) { - case VSLENGTH: - expdest = cvtnum(varlen, expdest); - goto record; - - case VSNORMAL: - if (!easy) - break; -record: - recordregion(startloc, expdest - stackblock(), - varflags & VSQUOTE); - break; - - case VSPLUS: - case VSMINUS: - if (!set) { - argstr(p, flag); - break; - } - if (easy) - goto record; - break; - - case VSTRIMLEFT: - case VSTRIMLEFTMAX: - case VSTRIMRIGHT: - case VSTRIMRIGHTMAX: - if (!set) - break; - /* - * Terminate the string and start recording the pattern - * right after it - */ - STPUTC('\0', expdest); - 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; - -#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; -} - - -/* - * Perform variable and command substitution. If EXP_FULL is set, output CTLESC - * characters to allow for further processing. Otherwise treat - * $@ like $* since no splitting will be performed. - */ - -static void -argstr(p, flag) - 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); -} - - -static void -removerecordregions(int endoff) -{ - if (ifslastp == NULL) - return; - - 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 -/* - * Expand arithmetic expression. Backup to start of expression, - * evaluate, place result in (backed up) result, adjust string position. - */ -static void -expari(int flag) -{ - char *p, *start; - int errcode; - int result; - int begoff; - int quotes = flag & (EXP_FULL | EXP_CASE); - int quoted; - - /* ifsfree(); */ - - /* - * 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++; - - if (p[1] == '"') - quoted=1; - else - quoted=0; - begoff = p - start; - removerecordregions(begoff); - if (quotes) - rmescapes(p+2); - result = arith(p+2, &errcode); - if (errcode < 0) { - if(errcode == -2) - error("divide by zero"); - else - error("syntax error: \"%s\"\n", 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 - -/* - * Expand stuff in backwards quotes. - */ - -static void -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(); - int 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; - - 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; - } - - 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 && SIT(lastc, syntax) == CCTL) - STPUTC(CTLESC, dest); - STPUTC(lastc, dest); - } - } - - /* Eat all trailing newlines */ - for (; dest > stackblock() && dest[-1] == '\n';) - STUNPUTC(dest); - -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; - - 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; - - switch (subtype) { - case VSASSIGN: - setvar(str, startp, 0); - amount = startp - expdest; - STADJUST(amount, expdest); - varflags &= ~VSNUL; - if (c != 0) - *loc = c; - return 1; - - 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 */ - - 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; - - 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; - - 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; - -#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; -} - - -/* - * Test whether a specialized variable is set. - */ - -static int -varisset(name, nulok) - char *name; - int nulok; -{ - if (*name == '!') - return backgndpid != -1; - else if (*name == '@' || *name == '*') { - if (*shellparam.p == NULL) - return 0; - - if (nulok) { - char **av; - - for (av = shellparam.p; *av; av++) - if (**av != '\0') - return 1; - return 0; - } - } else if (is_digit(*name)) { - char *ap; - int num = atoi(name); - - if (num > shellparam.nparam) - return 0; - - if (num == 0) - ap = arg0; - else - ap = shellparam.p[num - 1]; - - if (nulok && (ap == NULL || *ap == '\0')) - return 0; - } - return 1; -} - -/* - * Put a string on the stack. - */ - -static void -strtodest(const char *p, int syntax, int quotes) -{ - while (*p) { - if (quotes && SIT(*p,syntax) == CCTL) - STPUTC(CTLESC, expdest); - STPUTC(*p++, expdest); - } -} - -/* - * Add the value of a specialized variable to the stack string. - */ - -static void -varvalue(char *name, int quoted, int flags) -{ - int num; - char *p; - int i; - int sep; - int sepq = 0; - char **ap; - int syntax; - int allow_split = flags & EXP_FULL; - int quotes = flags & (EXP_FULL | EXP_CASE); - - 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); - } - break; - case '@': - if (allow_split && quoted) { - sep = 1 << CHAR_BIT; - goto param; - } - /* fall through */ - case '*': - sep = ifsset() ? ifsval()[0] : ' '; - if (quotes) { - sepq = SIT(sep,syntax) == 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; - } -} - - -/* - * Record the fact that we have to scan this region of the - * string for IFS characters. - */ - -static void -recordregion(start, end, nulonly) - int start; - int end; - int nulonly; -{ - 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; -} - - - -/* - * Break the argument string into pieces based upon IFS and add the - * strings to the argument list. The regions of the string to be - * searched for IFS characters have been stored by recordregion. - */ -static void -ifsbreakup(string, arglist) - char *string; - struct arglist *arglist; - { - struct ifsregion *ifsp; - struct strlist *sp; - char *start; - char *p; - char *q; - const char *ifs, *realifs; - int ifsspc; - int nulonly; - - - 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++; - } - } 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; -} - -/* - * Add a file name to the list. - */ - -static void -addfname(const 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; -} - -/* - * 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 -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, 0, 0, &pglob)) { - case 0: - if(pglob.gl_pathv[1]==0 && !strcmp(p, pglob.gl_pathv[0])) - 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; - } -} - - -/* - * Add the result of glob(3) to the list. - */ - -static void -addglob(pglob) - const glob_t *pglob; -{ - char **p = pglob->gl_pathv; - - do { - addfname(*p); - } while (*++p); -} - - -#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 */ - } - - expmeta(expdir, str->text); - ckfree(expdir); - expdir = NULL; - INTON; - if (exparg.lastp == savelastp) { - /* - * no matches - */ -nometa: - *exparg.lastp = str; - rmescapes(str->text); - exparg.lastp = &str->next; - } else { - *exparg.lastp = NULL; - *savelastp = sp = expsort(*savelastp); - while (sp->next != NULL) - sp = sp->next; - exparg.lastp = &sp->next; - } - str = str->next; - } -} - - -/* - * Do metacharacter (i.e. *, ?, [...]) expansion. - */ - -static void -expmeta(enddir, name) - char *enddir; - char *name; - { - 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; - - 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) { - 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 /* defined(__GLIBC__) && !defined(FNMATCH_BROKEN) && !defined(GLOB_BROKEN) */ - - - -#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); -} - - -static struct strlist * -msort(list, len) - struct strlist *list; - int len; -{ - struct strlist *p, *q = NULL; - struct strlist **lpp; - int half; - int n; - - if (len <= 1) - return list; - half = len >> 1; - p = list; - for (n = half ; --n >= 0 ; ) { - q = p; - p = p->next; - } - q->next = NULL; /* terminate first half of list */ - q = msort(list, half); /* sort first half of list */ - p = msort(p, len - half); /* sort second half */ - lpp = &list; - for (;;) { - if (strcmp(p->text, q->text) < 0) { - *lpp = p; - lpp = &p->next; - if ((p = *lpp) == NULL) { - *lpp = q; - break; - } - } else { - *lpp = q; - lpp = &q->next; - if ((q = *lpp) == NULL) { - *lpp = p; - break; - } - } - } - return list; -} -#endif - - - -/* - * Returns true if the pattern matches the string. - */ - -#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) -/* squoted: string might have quote chars */ -static int -patmatch(char *pattern, char *string, int squoted) -{ - const char *p; - char *q; - - p = preglob(pattern); - q = squoted ? _rmescapes(string, RMESCAPE_ALLOC) : string; - - return !fnmatch(p, q, 0); -} - - -static int -patmatch2(char *pattern, char *string, int squoted) -{ - char *p; - int res; - - sstrnleft--; - p = grabstackstr(expdest); - res = patmatch(pattern, string, squoted); - ungrabstackstr(p, expdest); - return res; -} -#else -static int -patmatch(char *pattern, char *string, int squoted) { - return pmatch(pattern, string, squoted); -} - - -static int -pmatch(char *pattern, char *string, int squoted) -{ - char *p, *q; - char c; - - 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; - - 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; - } - } -breakloop: - if (*q != '\0') - return 0; - return 1; -} -#endif - - - -/* - * Remove any CTLESC characters from a string. - */ - -#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN) -static char * -_rmescapes(char *str, 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) { - memcpy(q, str, len); - q += len; - } - } - 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(union node *pattern, const char *val) -{ - struct stackmark smark; - int result; - char *p; - - setstackmark(&smark); - argbackq = pattern->narg.backquote; - STARTSTACKSTR(expdest); - ifslastp = NULL; - argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); - STPUTC('\0', expdest); - p = grabstackstr(expdest); - result = patmatch(p, (char *)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; -} -/* - * 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 on arm (and others) can't be negative */ -}; - -static struct redirtab *redirlist; - -extern char **environ; - - - -/* - * 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); - } -} - - - -/* - * This routine is called when an error or an interrupt occurs in an - * interactive shell and control is returned to the main command loop. - */ - -/* 1 == check for aliases, 2 == also check for assignments */ -static int checkalias; /* also used in no alias mode for check assignments */ - -static void -reset(void) { - - /* 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 -static inline void putprompt(const char *s) { - out2str(s); -} -#endif - -#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ - - - -/* - * Same as pgetc(), but ignores PEOA. - */ - -#ifdef ASH_ALIAS -static int -pgetc2(void) -{ - int c; - do { - c = pgetc_macro(); - } while (c == PEOA); - return c; -} -#else -static inline int pgetc2() { return pgetc_macro(); } -#endif - -/* - * 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; - } - *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 || parsefile->fd) - nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); - else { - nr = cmdedit_read_input((char*)cmdedit_prompt, buf); - } - } -#else - nr = safe_read(parsefile->fd, buf, BUFSIZ - 1); -#endif - - 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; - } - } - } - } - return nr; -} - -static void -popstring(void) -{ - struct strpush *sp = parsefile->strpush; - - INTOFF; -#ifdef ASH_ALIAS - if (sp->ap) { - if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { - if (!checkalias) { - checkalias = 1; - } - } - if (sp->string != sp->ap->val) { - ckfree(sp->string); - } - - sp->ap->flag &= ~ALIASINUSE; - if (sp->ap->flag & ALIASDEAD) { - unalias(sp->ap->name); - } - } -#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; -} - - -/* - * 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. - */ - -static int -preadbuffer(void) -{ - char *p, *q; - int more; - char savec; - - while (parsefile->strpush) { -#ifdef ASH_ALIAS - if (parsenleft == -1 && parsefile->strpush->ap && - parsenextc[-1] != ' ' && parsenextc[-1] != '\t') { - return PEOA; - } -#endif - popstring(); - if (--parsenleft >= 0) - return (*parsenextc++); - } - if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) - return PEOF; - flushall(); - -again: - if (parselleft <= 0) { - if ((parselleft = preadfd()) <= 0) { - parselleft = parsenleft = EOF_NLEFT; - return PEOF; - } - } - - 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; - } - } - - savec = *q; - *q = '\0'; - - if (vflag) { - out2str(parsenextc); - } - - *q = savec; - - return *parsenextc++; -} - - -/* - * 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; - - 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; -} - - -/* - * 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 -pushfile(void) { - struct parsefile *pf; - - parsefile->nleft = parsenleft; - parsefile->lleft = parselleft; - parsefile->nextc = parsenextc; - parsefile->linno = plinno; - pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); - pf->prev = parsefile; - pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; - parsefile = pf; -} - -#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); - - -/* - * 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; - -/* Return true if fd 0 has already been redirected at least once. */ -static inline int -fd0_redirected_p (void) -{ - return fd0_redirected != 0; -} - -static void dupredirect (const union node *, int, int fd1dup); - -#ifdef 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. - */ - - - -static void setjobctl(int enable) -{ -#ifdef OLD_TTY_DRIVER - int ldisc; -#endif - - if (enable == jobctl || rootshell == 0) - return; - 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 - out2str("sh: can't access tty; job control turned off\n"); - mflag = 0; - return; - } - if (initialpgrp == -1) - initialpgrp = getpgrp(); - else if (initialpgrp != getpgrp()) { - killpg(initialpgrp, SIGTTIN); - continue; - } - } while (0); -#ifdef OLD_TTY_DRIVER - if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { - out2str("sh: need new tty driver to run job control; job control turned off\n"); - mflag = 0; - return; - } -#endif - setsignal(SIGTSTP); - setsignal(SIGTTOU); - setsignal(SIGTTIN); - setpgid(0, rootpid); -#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); - } - jobctl = enable; -} -#endif - - -#ifdef JOBS -static int -killcmd(argc, argv) - int argc; - char **argv; -{ - 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) { - const char *name; - - if (!*argptr) { - out1str("0\n"); - for (i = 1; i < NSIG; i++) { - name = u_signal_names(0, &i, 1); - if(name) - printf(snlfmt, name); - } - return 0; - } - name = u_signal_names(*argptr, &signo, -1); - if (name) - printf(snlfmt, name); - else - error("invalid signal number or exit status: %s", - *argptr); - return 0; - } - - 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); - - return 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 - restartjob(jp); - INTOFF; - status = waitforjob(jp); - INTON; - return status; -} - - -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 -restartjob(jp) - struct job *jp; -{ - struct procstat *ps; - int i; - - if (jp->state == JOBDONE) - return; - INTOFF; - killpg(jp->ps[0].pid, SIGCONT); - for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { - if (WIFSTOPPED(ps->status)) { - ps->status = -1; - jp->state = 0; - } - } - INTON; -} -#endif - -static void showjobs(int change); - - -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. - */ - -static void -showjobs(change) - int change; -{ - int jobno; - int procno; - int i; - struct job *jp; - struct procstat *ps; - int col; - char s[64]; - - TRACE(("showjobs(%d) called\n", change)); - while (dowait(0, (struct job *)NULL) > 0); - for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { - if (! jp->used) - continue; - if (jp->nprocs == 0) { - freejob(jp); - continue; - } - if (change && ! jp->changed) - continue; - procno = jp->nprocs; - for (ps = jp->ps ; ; ps++) { /* for each process */ - if (ps == jp->ps) - 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); - } - } -} - - -/* - * Mark a job structure as unused. - */ - -static void -freejob(struct job *jp) -{ - const struct procstat *ps; - int i; - - INTOFF; - for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { - if (ps->cmd != nullstr) - ckfree(ps->cmd); - } - if (jp->ps != &jp->ps0) - ckfree(jp->ps); - jp->used = 0; -#ifdef JOBS - if (curjob == jp - jobtab + 1) - curjob = 0; -#endif - INTON; -} - - - -static int -waitcmd(argc, argv) - int argc; - char **argv; -{ - struct job *job; - int status, retval; - struct job *jp; - - 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; - } - } -} - - - -/* - * Convert a job name to a job structure. - */ - -static struct job * -getjob(const char *name) -{ - int jobno; - struct job *jp; - int pid; - int i; - - 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 - } 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; - } - } - error("No such job: %s", name); - /* NOTREACHED */ -} - - - -/* - * Return a new job structure, - */ - -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; - 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; - } - INTON; - TRACE(("makejob(0x%lx, %d) returns %%%d\n", (long)node, nprocs, - jp - jobtab + 1)); - return jp; -} - - -/* - * Fork of a subshell. If we are doing job control, give the subshell its - * own process group. Jp is a job structure that the job is to be added to. - * N is the command that will be evaluated by the child. Both jp and n may - * be NULL. The mode parameter can be one of the following: - * FORK_FG - Fork off a foreground process. - * FORK_BG - Fork off a background process. - * FORK_NOJOB - Like FORK_FG, but don't give the process its own - * process group even if job control is on. - * - * When job control is turned off, background processes have their standard - * input redirected to /dev/null (except for the second and later processes - * in a pipeline). - */ - - - -static int -forkshell(struct job *jp, const union node *n, int mode) -{ - int pid; -#ifdef JOBS - int pgrp; -#endif - const char *devnull = _PATH_DEVNULL; - const char *nullerr = "Can't open %s"; - - TRACE(("forkshell(%%%d, 0x%lx, %d) called\n", jp - jobtab, (long)n, - mode)); - INTOFF; - pid = fork(); - if (pid == -1) { - TRACE(("Fork failed, errno=%d\n", errno)); - INTON; - error("Cannot fork"); - } - if (pid == 0) { - struct job *p; - int wasroot; - int i; - - TRACE(("Child shell %d\n", getpid())); - wasroot = rootshell; - rootshell = 0; - 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; -} - - - -/* - * 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; - - 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; -} - - - -/* - * Wait for a process to terminate. - */ - -/* - * 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; - struct procstat *sp; - struct job *jp; - struct job *thisjob; - int done; - int stopped; - int core; - int sig; - - 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; -#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 { - 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; - } - return pid; -} - - - - -/* - * return 1 if there are stopped jobs, otherwise 0 - */ -static int -stoppedjobs(void) -{ - int jobno; - struct job *jp; - - if (job_warning) - return (0); - for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { - if (jp->used == 0) - continue; - if (jp->state == JOBSTOPPED) { - out2str("You have stopped jobs.\n"); - job_warning = 2; - return (1); - } - } - - return (0); -} - -/* - * Return a string identifying a command (to be printed by the - * jobs command. - */ - -static char *cmdnextc; -static int cmdnleft; -#define MAXCMDTEXT 200 - -static void -cmdputs(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; -} - -#define CMDTXT_TABLE -#ifdef CMDTXT_TABLE -/* - * To collect a lot of redundant code in cmdtxt() case statements, we - * implement a mini language here. Each type of node struct has an - * associated instruction sequence that operates on its members via - * their offsets. The instruction are pack in unsigned chars with - * format IIDDDDDE where the bits are - * I : part of the instruction opcode, which are - * 00 : member is a pointer to another node -- process it recursively - * 40 : member is a pointer to a char string -- output it - * 80 : output the string whose index is stored in the data field - * CC : flag signaling that this case needs external processing - * D : data - either the (shifted) index of a fixed string to output or - * the actual offset of the member to operate on in the struct - * (since we assume bit 0 is set, the offset is not shifted) - * E : flag signaling end of instruction sequence - * - * WARNING: In order to handle larger offsets for 64bit archs, this code - * assumes that no offset can be an odd number and stores the - * end-of-instructions flag in bit 0. - */ - -#define CMDTXT_NOMORE 0x01 /* NOTE: no offset should be odd */ -#define CMDTXT_CHARPTR 0x40 -#define CMDTXT_STRING 0x80 -#define CMDTXT_SPECIAL 0xC0 -#define CMDTXT_OFFSETMASK 0x3E - -static const char * const cmdtxt_strings[] = { - /* 0 1 2 3 4 5 6 7 */ - "; ", "(", ")", " && ", " || ", "if ", "; then ", "...", - /* 8 9 10 11 12 13 */ - "while ", "; do ", "; done", "until ", "for ", " in ...", - /* 14 15 16 17 */ - "case ", "???", "() ...", "<<..." -}; - -static const char * const redir_strings[] = { - ">", "<", "<>", ">>", ">|", ">&", "<&" -}; - -static const unsigned char cmdtxt_ops[] = { -#define CMDTXT_NSEMI 0 - offsetof(union node, nbinary.ch1), - 0|CMDTXT_STRING, - offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE, -#define CMDTXT_NCMD (CMDTXT_NSEMI + 3) -#define CMDTXT_NPIPE (CMDTXT_NCMD) -#define CMDTXT_NCASE (CMDTXT_NCMD) -#define CMDTXT_NTO (CMDTXT_NCMD) -#define CMDTXT_NFROM (CMDTXT_NCMD) -#define CMDTXT_NFROMTO (CMDTXT_NCMD) -#define CMDTXT_NAPPEND (CMDTXT_NCMD) -#define CMDTXT_NTOOV (CMDTXT_NCMD) -#define CMDTXT_NTOFD (CMDTXT_NCMD) -#define CMDTXT_NFROMFD (CMDTXT_NCMD) - CMDTXT_SPECIAL, -#define CMDTXT_NREDIR (CMDTXT_NPIPE + 1) -#define CMDTXT_NBACKGND (CMDTXT_NREDIR) - offsetof(union node, nredir.n)|CMDTXT_NOMORE, -#define CMDTXT_NSUBSHELL (CMDTXT_NBACKGND + 1) - (1*2)|CMDTXT_STRING, - offsetof(union node, nredir.n), - (2*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NAND (CMDTXT_NSUBSHELL + 3) - offsetof(union node, nbinary.ch1), - (3*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE, -#define CMDTXT_NOR (CMDTXT_NAND + 3) - offsetof(union node, nbinary.ch1), - (4*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch2)|CMDTXT_NOMORE, -#define CMDTXT_NIF (CMDTXT_NOR + 3) - (5*2)|CMDTXT_STRING, - offsetof(union node, nif.test), - (6*2)|CMDTXT_STRING, - offsetof(union node, nif.ifpart), - (7*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NWHILE (CMDTXT_NIF + 5) - (8*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch1), - (9*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch2), - (10*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NUNTIL (CMDTXT_NWHILE + 5) - (11*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch1), - (9*2)|CMDTXT_STRING, - offsetof(union node, nbinary.ch2), - (10*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NFOR (CMDTXT_NUNTIL + 5) - (12*2)|CMDTXT_STRING, - offsetof(union node, nfor.var)|CMDTXT_CHARPTR, - (13*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NCLIST (CMDTXT_NFOR + 3) /* TODO: IS THIS CORRECT??? */ -#define CMDTXT_NNOT (CMDTXT_NCLIST) /* TODO: IS THIS CORRECT??? */ - (15*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NDEFUN (CMDTXT_NCLIST + 1) - offsetof(union node, narg.text)|CMDTXT_CHARPTR, - (16*2)|CMDTXT_STRING|CMDTXT_NOMORE, -#define CMDTXT_NARG (CMDTXT_NDEFUN + 2) - offsetof(union node, narg.text)|CMDTXT_CHARPTR|CMDTXT_NOMORE, -#define CMDTXT_NHERE (CMDTXT_NARG + 1) -#define CMDTXT_NXHERE (CMDTXT_NHERE) - (17*2)|CMDTXT_STRING|CMDTXT_NOMORE, -}; - -#if CMDTXT_NXHERE != 36 -#error CMDTXT_NXHERE -#endif - -static const unsigned char cmdtxt_ops_index[26] = { - CMDTXT_NSEMI, - CMDTXT_NCMD, - CMDTXT_NPIPE, - CMDTXT_NREDIR, - CMDTXT_NBACKGND, - CMDTXT_NSUBSHELL, - CMDTXT_NAND, - CMDTXT_NOR, - CMDTXT_NIF, - CMDTXT_NWHILE, - CMDTXT_NUNTIL, - CMDTXT_NFOR, - CMDTXT_NCASE, - CMDTXT_NCLIST, - CMDTXT_NDEFUN, - CMDTXT_NARG, - CMDTXT_NTO, - CMDTXT_NFROM, - CMDTXT_NFROMTO, - CMDTXT_NAPPEND, - CMDTXT_NTOOV, - CMDTXT_NTOFD, - CMDTXT_NFROMFD, - CMDTXT_NHERE, - CMDTXT_NXHERE, - CMDTXT_NNOT, -}; - -static void -cmdtxt(const union node *n) -{ - const char *p; - - if (n == NULL) - return; - - p = cmdtxt_ops + (int) cmdtxt_ops_index[n->type]; - if ((*p & CMDTXT_SPECIAL) != CMDTXT_SPECIAL) { /* normal case */ - do { - if (*p & CMDTXT_STRING) { /* output fixed string */ - cmdputs(cmdtxt_strings[((int)(*p & CMDTXT_OFFSETMASK) >> 1)]); - } else { - const char *pf = ((const char *) n) - + ((int)(*p & CMDTXT_OFFSETMASK)); - if (*p & CMDTXT_CHARPTR) { /* output dynamic string */ - cmdputs(*((const char **) pf)); - } else { /* output field */ - cmdtxt(*((const union node **) pf)); - } - } - } while (!(*p++ & CMDTXT_NOMORE)); - } else if (n->type == NCMD) { - union node *np; - 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); - } - } else if (n->type == NPIPE) { - struct nodelist *lp; - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - cmdtxt(lp->n); - if (lp->next) - cmdputs(" | "); - } - } else if (n->type == NCASE) { - cmdputs(cmdtxt_strings[14]); - cmdputs(n->ncase.expr->narg.text); - cmdputs(cmdtxt_strings[13]); - } else { -#if (NTO != 16) || (NFROM != 17) || (NFROMTO != 18) || (NAPPEND != 19) || (NTOOV != 20) || (NTOFD != 21) || (NFROMFD != 22) -#error Assumption violated regarding range and ordering of NTO ... NFROMFD! -#endif - char s[2]; - -#ifdef DEBUG - assert((n->type >= NTO) && (n->type <= NFROMFD)); -#endif - - p = redir_strings[n->type - NTO]; - if (n->nfile.fd != ('>' == *p)) { - s[0] = n->nfile.fd + '0'; - s[1] = '\0'; - cmdputs(s); - } - cmdputs(p); - if (n->type >= NTOFD) { - s[0] = n->ndup.dupfd + '0'; - s[1] = '\0'; - cmdputs(s); - } else { - cmdtxt(n->nfile.fname); - } - } -} -#else /* CMDTXT_TABLE */ -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; - } -} -#endif /* CMDTXT_TABLE */ - -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; -} -/* - * Routines to check for mail. (Perhaps make part of main.c?) - */ - - -#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(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"); - } - mailtime[i] = statb.st_size; - } - nmboxes = i; - popstackmark(&smark); -} - -#define PROFILE 0 - -#if PROFILE -static short profile_buf[16384]; -extern int etext(); -#endif - -static void read_profile (const char *); -static void cmdloop (int); -static void options (int); -static void setoption (int, int); -static void procargs (int, char **); - - -/* - * Main routine. We initialize things, parse the arguments, execute - * profiles if we're a login shell, and then call cmdloop to execute - * commands. The setjmp call sets up the location to jump to when an - * exception occurs. When an exception occurs the variable "state" - * is used to figure out how far we had gotten. - */ - -int -ash_main(argc, argv) - int argc; - char **argv; -{ - struct jmploc jmploc; - struct stackmark smark; - volatile int state; - const char *shinit; - - BLTINCMD = find_builtin("builtin"); - EXECCMD = find_builtin("exec"); - EVALCMD = find_builtin("eval"); - -#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. - */ - if (exception == EXSHELLPROC) { - rootpid = getpid(); - rootshell = 1; - minusc = NULL; - state = 3; - } else { - if (exception == EXEXEC) { - exitstatus = exerrno; - } else if (exception == EXERROR) { - exitstatus = 2; - } - 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; - } - 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 const char sigs[] = { - SIGINT, SIGQUIT, SIGHUP, -#ifdef SIGTSTP - SIGTSTP, -#endif - SIGPIPE - }; -#define SIGSSIZE ((sizeof(sigs)/sizeof(sigs[0])) - 1) /* trailing nul */ - int i; - - for (i = 0; i < SIGSSIZE; i++) - setsignal(sigs[i]); - } - - if (minusc) - evalstring(minusc, 0); - - if (sflag || minusc == NULL) { -state4: /* XXX ??? - why isn't this before the "if" statement */ - cmdloop(1); - } -#if PROFILE - monitor(0); -#endif - exitshell(exitstatus); - /* NOTREACHED */ -} - - -/* - * Read and execute commands. "Top" is nonzero for the top level command - * loop; it turns on prompting if the shell is interactive. - */ - -static void -cmdloop(int top) -{ - union node *n; - struct stackmark smark; - int inter; - int numeof = 0; - - TRACE(("cmdloop(%d) called\n", top)); - setstackmark(&smark); - for (;;) { - if (pendingsigs) - dotrap(); - inter = 0; - if (iflag && top) { - inter++; - showjobs(1); - chkmail(0); - 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); -} - - - -/* - * Read /etc/profile or .profile. Return on error. - */ - -static void -read_profile(name) - const char *name; -{ - int fd; - int xflag_save; - int vflag_save; - - 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 */ - /* Note: Might do a little redundant work, but reduces code size. */ - xflag_save = xflag; - vflag_save = vflag; - if (qflag) { - vflag = xflag = 0; - } - cmdloop(0); - xflag = xflag_save; - vflag = vflag_save; - popfile(); -} - - - -/* - * Read a file containing shell functions. - */ - -static void -readcmdfile(const char *name) -{ - int fd; - - INTOFF; - if ((fd = open(name, O_RDONLY)) >= 0) - setinputfd(fd, 1); - else - error("Can't open %s", name); - INTON; - cmdloop(0); - popfile(); -} - - - -/* - * Take commands from a file. To be compatable we should do a path - * search for the file, which is necessary to find sub-commands. - */ - - -static inline char * -find_dot_file(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) - 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; - - setstackmark(&smark); - fullname = find_dot_file(argv[1]); - setinputfile(fullname, 1); - commandname = fullname; - cmdloop(0); - popfile(); - popstackmark(&smark); - } - 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 */ -} - -static pointer -stalloc(int nbytes) -{ - 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; -} - - -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 void -setstackmark(struct stackmark *mark) -{ - mark->stackp = stackp; - mark->stacknxt = stacknxt; - mark->stacknleft = stacknleft; - mark->marknext = markp; - markp = mark; -} - - -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; -} - - -/* - * 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. - */ - -static void -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 inline void -grabstackblock(int len) -{ - len = ALIGN(len); - stacknxt += len; - stacknleft -= len; -} - - - -/* - * 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(void) { - int len = stackblocksize(); - if (herefd >= 0 && len >= 1024) { - xwrite(herefd, stackblock(), len); - sstrnleft = len - 1; - return stackblock(); - } - growstackblock(); - sstrnleft = stackblocksize() - len - 1; - return stackblock() + len; -} - - -/* - * Called from CHECKSTRSPACE. - */ - -static char * -makestrspace(size_t newlen) { - int len = stackblocksize() - sstrnleft; - do { - growstackblock(); - sstrnleft = stackblocksize() - len; - } while (sstrnleft < newlen); - return stackblock() + len; -} - - - -static void -ungrabstackstr(char *s, char *p) -{ - stacknleft += stacknxt - s; - stacknxt = s; - sstrnleft = stacknleft - (p - s); -} -/* - * Miscelaneous builtins. - */ - - -#undef rflag - -#if !defined(__GLIBC__) || __GLIBC__ == 2 && __GLIBC_MINOR__ < 1 -typedef long rlim_t; -#endif - - - -/* - * 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 int -readcmd(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; - - rflag = 0; - prompt = NULL; - while ((i = nextopt("p:r")) != '\0') { - if (i == 'p') - prompt = optionarg; - else - rflag = 1; - } - if (prompt && isatty(0)) { - out2str(prompt); /* read without cmdedit */ - 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; - } - 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 int -umaskcmd(argc, argv) - int argc; - char **argv; -{ - static const char permuser[3] = "ugo"; - static const char permmode[3] = "rwx"; - static const short int permmask[] = { - S_IRUSR, S_IWUSR, S_IXUSR, - S_IRGRP, S_IWGRP, S_IXGRP, - S_IROTH, S_IWOTH, S_IXOTH - }; - - char *ap; - mode_t mask; - int i; - int symbolic_mode = 0; - - while (nextopt("S") != '\0') { - symbolic_mode = 1; - } - - INTOFF; - mask = umask(0); - umask(mask); - INTON; - - if ((ap = *argptr) == NULL) { - if (symbolic_mode) { - char buf[18]; - char *p = buf; - for (i=0 ; i<3 ; i++) { - int j; - *p++ = permuser[i]; - *p++ = '='; - for (j=0 ; j<3 ; j++) { - if ((mask & permmask[3*i+j]) == 0) { - *p++ = permmode[j]; - } - } - *p++ = ','; - } - *--p = 0; - puts(buf); - } 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 { - mask = ~mask & 0777; - if (parse_mode(ap, &mask) == FALSE) { - error("Illegal mode: %s", ap); - } - umask(~mask & 0777); - } - } - return 0; -} - -/* - * 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. - */ - -struct limits { - const char *name; - short cmd; - short factor; /* multiply by to get rlim_{cur,max} values */ -}; - -static const struct limits limits[] = { -#ifdef RLIMIT_CPU - { "time(seconds)", RLIMIT_CPU, 1 }, -#endif -#ifdef RLIMIT_FSIZE - { "file(blocks)", RLIMIT_FSIZE, 512 }, -#endif -#ifdef RLIMIT_DATA - { "data(kbytes)", RLIMIT_DATA, 1024 }, -#endif -#ifdef RLIMIT_STACK - { "stack(kbytes)", RLIMIT_STACK, 1024 }, -#endif -#ifdef RLIMIT_CORE - { "coredump(blocks)", RLIMIT_CORE, 512 }, -#endif -#ifdef RLIMIT_RSS - { "memory(kbytes)", RLIMIT_RSS, 1024 }, -#endif -#ifdef RLIMIT_MEMLOCK - { "locked memory(kbytes)", RLIMIT_MEMLOCK, 1024 }, -#endif -#ifdef RLIMIT_NPROC - { "process(processes)", RLIMIT_NPROC, 1 }, -#endif -#ifdef RLIMIT_NOFILE - { "nofiles(descriptors)", RLIMIT_NOFILE, 1 }, -#endif -#ifdef RLIMIT_VMEM - { "vmemory(kbytes)", RLIMIT_VMEM, 1024 }, -#endif -#ifdef RLIMIT_SWAP - { "swap(kbytes)", RLIMIT_SWAP, 1024 }, -#endif - { NULL, 0, 0 } -}; - -static int -ulimitcmd(argc, argv) - int argc; - char **argv; -{ - static const char unlimited_string[] = "unlimited"; - 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; - - what = 'f'; - - while ((optc = nextopt("HSa" -#ifdef RLIMIT_CPU - "t" -#endif -#ifdef RLIMIT_FSIZE - "f" -#endif -#ifdef RLIMIT_DATA - "d" -#endif -#ifdef RLIMIT_STACK - "s" -#endif -#ifdef RLIMIT_CORE - "c" -#endif -#ifdef RLIMIT_RSS - "m" -#endif -#ifdef RLIMIT_MEMLOCK - "l" -#endif -#ifdef RLIMIT_NPROC - "p" -#endif -#ifdef RLIMIT_NOFILE - "n" -#endif -#ifdef RLIMIT_VMEM - "v" -#endif -#ifdef RLIMIT_SWAP - "w" -#endif - )) != '\0') { - if (optc == 'H') { - how = HARD; - } else if (optc == 'S') { - how = SOFT; - } else if (optc == 'a') { - all = 1; - } else { - what = optc; - } - } - - for (l = limits; l->name; l++) { - if(l->name[0] == what) - break; - if(l->name[1]=='w' && what=='w') - break; - } - - set = *argptr ? 1 : 0; - if (set) { - char *p = *argptr; - - if (all || argptr[1]) - error("too many arguments"); - if (strcmp(p, unlimited_string) == 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++) { - printf("%-20s ", l->name); - getrlimit(l->cmd, &limit); - OUTPUT_LIMIT: - if (how & SOFT) - val = limit.rlim_cur; - else if (how & HARD) - val = limit.rlim_max; - - if (val == RLIM_INFINITY) - puts(unlimited_string); - else - { - val /= l->factor; - printf("%lld\n", (long long) val); - } - if (!all) { - break; - } - } - return 0; - } - - if (!set) { - goto OUTPUT_LIMIT; - } - - getrlimit(l->cmd, &limit); - 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)"); - return 0; -} -/* - * prefix -- see if pfx is a prefix of string. - */ - -static int -prefix(char const *pfx, char const *string) -{ - while (*pfx) { - if (*pfx++ != *string++) - return 0; - } - return 1; -} - -/* - * Return true if s is a string of digits, and save munber in intptr - * nagative is bad - */ - -static int -is_number(const char *p, int *intptr) -{ - int ret = 0; - - do { - if (! is_digit(*p)) - return 0; - ret *= 10; - ret += digit_val(*p); - p++; - } while (*p != '\0'); - - *intptr = ret; - return 1; -} - -/* - * Convert a string of digits to an integer, printing an error message on - * failure. - */ - -static int -number(const char *s) -{ - int i; - if (! is_number(s, &i)) - error("Illegal number: %s", s); - return i; -} - -/* - * 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; - len2p = len2 + ((len2 < 2) ? len2 : 2); - - CHECKSTRSPACE(len1p + len2p + 1, p); - - if (len1) { - *p = '\''; - q = p + 1 + len1; - memcpy(p + 1, s, len1); - *q++ = '\''; - s += len1; - } - - if (len2 > 1) { - *q = '"'; - q += 1 + len2; - memcpy(q + 1, s, len2); - *q = '"'; - s += len2; - } else if (len2 == 1) { - *q++ = '\\'; - *q = '\''; - s++; - } - - STADJUST(len1p + len2p, p); - } while (*s); - - USTPUTC(0, p); - - return grabstackstr(p); -} - -/* - * Like strdup but works with the ash stack. - */ - -static char * -sstrdup(const char *p) -{ - size_t len = strlen(p) + 1; - return memcpy(stalloc(len), p, len); -} - - -/* - * Routine for dealing with parsed shell commands. - */ - - -static void sizenodelist (const struct nodelist *); -static struct nodelist *copynodelist (const struct nodelist *); -static char *nodesavestr (const char *); - -#define CALCSIZE_TABLE -#define COPYNODE_TABLE -#if defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) -/* - * To collect a lot of redundant code in case statements for copynode() - * and calcsize(), we implement a mini language here. Each type of node - * struct has an associated instruction sequence that operates on its - * members via their offsets. The instruction are pack in unsigned chars - * with format IIDDDDDE where the bits are - * I : part of the instruction opcode, which are - * 00 : member is a pointer to another node - * 40 : member is an integer - * 80 : member is a pointer to a nodelist - * CC : member is a pointer to a char string - * D : data - the actual offset of the member to operate on in the struct - * (since we assume bit 0 is set, it is not shifted) - * E : flag signaling end of instruction sequence - * - * WARNING: In order to handle larger offsets for 64bit archs, this code - * assumes that no offset can be an odd number and stores the - * end-of-instructions flag in bit 0. - */ - -#define NODE_INTEGER 0x40 -#define NODE_NODELIST 0x80 -#define NODE_CHARPTR 0xC0 -#define NODE_NOMORE 0x01 /* Note: no offset should be odd (aligned)*/ -#define NODE_MBRMASK 0xC0 -#define NODE_OFFSETMASK 0x3E - -static const unsigned char copynode_ops[35] = { -#define COPYNODE_OPS0 0 - offsetof(union node, nbinary.ch2), - offsetof(union node, nbinary.ch1)|NODE_NOMORE, -#define COPYNODE_OPS1 (COPYNODE_OPS0 + 2) - offsetof(union node, ncmd.redirect), - offsetof(union node, ncmd.args), - offsetof(union node, ncmd.assign), - offsetof(union node, ncmd.backgnd)|NODE_INTEGER|NODE_NOMORE, -#define COPYNODE_OPS2 (COPYNODE_OPS1 + 4) - offsetof(union node, npipe.cmdlist)|NODE_NODELIST, - offsetof(union node, npipe.backgnd)|NODE_INTEGER|NODE_NOMORE, -#define COPYNODE_OPS3 (COPYNODE_OPS2 + 2) - offsetof(union node, nredir.redirect), - offsetof(union node, nredir.n)|NODE_NOMORE, -#define COPYNODE_OPS4 (COPYNODE_OPS3 + 2) - offsetof(union node, nif.elsepart), - offsetof(union node, nif.ifpart), - offsetof(union node, nif.test)|NODE_NOMORE, -#define COPYNODE_OPS5 (COPYNODE_OPS4 + 3) - offsetof(union node, nfor.var)|NODE_CHARPTR, - offsetof(union node, nfor.body), - offsetof(union node, nfor.args)|NODE_NOMORE, -#define COPYNODE_OPS6 (COPYNODE_OPS5 + 3) - offsetof(union node, ncase.cases), - offsetof(union node, ncase.expr)|NODE_NOMORE, -#define COPYNODE_OPS7 (COPYNODE_OPS6 + 2) - offsetof(union node, nclist.body), - offsetof(union node, nclist.pattern), - offsetof(union node, nclist.next)|NODE_NOMORE, -#define COPYNODE_OPS8 (COPYNODE_OPS7 + 3) - offsetof(union node, narg.backquote)|NODE_NODELIST, - offsetof(union node, narg.text)|NODE_CHARPTR, - offsetof(union node, narg.next)|NODE_NOMORE, -#define COPYNODE_OPS9 (COPYNODE_OPS8 + 3) - offsetof(union node, nfile.fname), - offsetof(union node, nfile.fd)|NODE_INTEGER, - offsetof(union node, nfile.next)|NODE_NOMORE, -#define COPYNODE_OPS10 (COPYNODE_OPS9 + 3) - offsetof(union node, ndup.vname), - offsetof(union node, ndup.dupfd)|NODE_INTEGER, - offsetof(union node, ndup.fd)|NODE_INTEGER, - offsetof(union node, ndup.next)|NODE_NOMORE, -#define COPYNODE_OPS11 (COPYNODE_OPS10 + 4) - offsetof(union node, nhere.doc), - offsetof(union node, nhere.fd)|NODE_INTEGER, - offsetof(union node, nhere.next)|NODE_NOMORE, -#define COPYNODE_OPS12 (COPYNODE_OPS11 + 3) - offsetof(union node, nnot.com)|NODE_NOMORE, -}; - -#if COPYNODE_OPS12 != 34 -#error COPYNODE_OPS12 is incorrect -#endif - -static const unsigned char copynode_ops_index[26] = { - COPYNODE_OPS0, /* NSEMI */ - COPYNODE_OPS1, /* NCMD */ - COPYNODE_OPS2, /* NPIPE */ - COPYNODE_OPS3, /* NREDIR */ - COPYNODE_OPS3, /* NBACKGND */ - COPYNODE_OPS3, /* NSUBSHELL */ - COPYNODE_OPS0, /* NAND */ - COPYNODE_OPS0, /* NOR */ - COPYNODE_OPS4, /* NIF */ - COPYNODE_OPS0, /* NWHILE */ - COPYNODE_OPS0, /* NUNTIL */ - COPYNODE_OPS5, /* NFOR */ - COPYNODE_OPS6, /* NCASE */ - COPYNODE_OPS7, /* NCLIST */ - COPYNODE_OPS8, /* NDEFUN */ - COPYNODE_OPS8, /* NARG */ - COPYNODE_OPS9, /* NTO */ - COPYNODE_OPS9, /* NFROM */ - COPYNODE_OPS9, /* NFROMTO */ - COPYNODE_OPS9, /* NAPPEND */ - COPYNODE_OPS9, /* NTOOV */ - COPYNODE_OPS10, /* NTOFD */ - COPYNODE_OPS10, /* NFROMFD */ - COPYNODE_OPS11, /* NHERE */ - COPYNODE_OPS11, /* NXHERE */ - COPYNODE_OPS12, /* NNOT */ -}; - -#if NODE_CHARPTR != NODE_MBRMASK -#error NODE_CHARPTR != NODE_MBRMASK!!! -#endif -#endif /* defined(CALCSIZE_TABLE) || defined(COPYNODE_TABLE) */ - -#ifdef COPYNODE_TABLE -static union node * -copynode(const union node *n) -{ - union node *new; - const unsigned char *p; - - if (n == NULL) { - return NULL; - } - new = funcblock; - new->type = n->type; - funcblock = (char *) funcblock + (int) nodesize[n->type]; - p = copynode_ops + (int) copynode_ops_index[n->type]; - do { - char *nn = ((char *) new) + ((int)(*p & NODE_OFFSETMASK)); - const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK)); - - if (!(*p & NODE_MBRMASK)) { /* standard node */ - *((union node **)nn) = copynode(*((const union node **) no)); - } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */ - *((const char **)nn) = nodesavestr(*((const char **)no)); - } else if (*p & NODE_NODELIST) { /* nodelist */ - *((struct nodelist **)nn) - = copynodelist(*((const struct nodelist **) no)); - } else { /* integer */ - *((int *) nn) = *((int *) no); - } - } while (!(*p++ & NODE_NOMORE)); - return new; -} -#else /* COPYNODE_TABLE */ -static union node * -copynode(const 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; -} -#endif /* COPYNODE_TABLE */ - -#ifdef CALCSIZE_TABLE -static void -calcsize(const union node *n) -{ - const unsigned char *p; - - if (n == NULL) - return; - funcblocksize += (int) nodesize[n->type]; - - p = copynode_ops + (int) copynode_ops_index[n->type]; - do { - const char *no = ((const char *) n) + ((int)(*p & NODE_OFFSETMASK)); - - if (!(*p & NODE_MBRMASK)) { /* standard node */ - calcsize(*((const union node **) no)); - } else if ((*p & NODE_MBRMASK) == NODE_CHARPTR) { /* string */ - funcstringsize += strlen(*((const char **)no)) + 1; - } else if (*p & NODE_NODELIST) { /* nodelist */ - sizenodelist(*((const struct nodelist **) no)); - } /* else integer -- ignore */ - } while (!(*p++ & NODE_NOMORE)); -} -#else /* CALCSIZE_TABLE */ -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; - }; -} -#endif /* CALCSIZE_TABLE */ - -static void -sizenodelist(const struct nodelist *lp) -{ - while (lp) { - funcblocksize += ALIGN(sizeof(struct nodelist)); - calcsize(lp->n); - lp = lp->next; - } -} - - -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; -} - - -static char * -nodesavestr(const char *s) -{ - const char *p = s; - char *q = funcstring; - char *rtn = funcstring; - - while ((*q++ = *p++) != '\0') - continue; - funcstring = q; - return rtn; -} - -#ifdef ASH_GETOPTS -static int getopts (char *, char *, char **, int *, int *); -#endif - - -/* - * Process the shell command line arguments. - */ - -static void -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++; - - shellparam.p = argptr; - shellparam.optind = 1; - shellparam.optoff = -1; - /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ - while (*argptr) { - shellparam.nparam++; - argptr++; - } - optschanged(); -} - - - -/* - * 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; - - 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); - } -} - - -static void -options(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--; - 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 - break; -#endif - } else if (c == 'o') { - minus_o(*argptr, val); - if (*argptr) - argptr++; - } else { - setoption(c, val); - } - } - } -} - - -static void -setoption(int flag, int val) -{ - int i; - - 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 */ -} - - - -/* - * Set the shell parameters. - */ - -static void -setparam(char **argv) -{ - char **newparam; - char **ap; - int nparam; - - 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; -} - - -/* - * Free the list of positional parameters. - */ - -static void -freeparam(volatile struct shparam *param) -{ - char **ap; - - if (param->malloc) { - for (ap = param->p ; *ap ; ap++) - ckfree(*ap); - ckfree(param->p); - } -} - - - -/* - * The shift builtin command. - */ - -static int -shiftcmd(argc, argv) - int argc; - char **argv; -{ - int n; - char **ap1, **ap2; - - 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; -} - - - -/* - * The set command builtin. - */ - -static int -setcmd(argc, argv) - int argc; - char **argv; -{ - if (argc == 1) - return showvarscmd(argc, argv); - INTOFF; - options(0); - optschanged(); - if (*argptr != NULL) { - setparam(argptr); - } - INTON; - return 0; -} - - -static void -getoptsreset(const char *value) -{ - shellparam.optind = number(value); - shellparam.optoff = -1; -} - -#ifdef BB_LOCALE_SUPPORT -static void change_lc_all(const char *value) -{ - if(value != 0 && *value != 0) - setlocale(LC_ALL, value); -} - -static void change_lc_ctype(const char *value) -{ - if(value != 0 && *value != 0) - setlocale(LC_CTYPE, value); -} - -#endif - -#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. - */ - -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; - } - } - - 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 - - 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; -static struct heredoc *heredoc; -static int quoteflag; /* set if (part of) last token was quoted */ -static int startlinno; /* line # where last token started */ - - -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 char peektoken (void); -static int readtoken (void); -static int xxreadtoken (void); -static int readtoken1 (int, int, const char *, int); -static int noexpand (char *); -static void synexpect (int) __attribute__((noreturn)); -static void synerror (const char *) __attribute__((noreturn)); -static void setprompt (int); - - -/* - * Read and parse a command. Returns NEOF on end of file. (NULL is a - * valid parse tree indicating a blank line.) - */ - -static union node * -parsecmd(int interact) -{ - int t; - - tokpushback = 0; - doprompt = interact; - if (doprompt) - setprompt(1); - else - setprompt(0); - needprompt = 0; - t = readtoken(); - if (t == TEOF) - return NEOF; - if (t == TNL) - return NULL; - tokpushback++; - return list(1); -} - - -static union node * -list(nlflag) - int nlflag; -{ - union node *n1, *n2, *n3; - int tok; - - checkkwd = 2; - if (nlflag == 0 && peektoken()) - return NULL; - n1 = NULL; - for (;;) { - n2 = andor(); - tok = readtoken(); - if (tok == TBACKGND) { - if (n2->type == NCMD || n2->type == NPIPE) { - n2->ncmd.backgnd = 1; - } else if (n2->type == NREDIR) { - n2->type = NBACKGND; - } else { - n3 = (union node *)stalloc(sizeof (struct nredir)); - n3->type = NBACKGND; - n3->nredir.n = n2; - n3->nredir.redirect = NULL; - n2 = n3; - } - } - if (n1 == NULL) { - n1 = n2; - } - else { - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = NSEMI; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; - } - switch (tok) { - case TBACKGND: - case TSEMI: - tok = readtoken(); - /* fall through */ - case TNL: - if (tok == TNL) { - parseheredoc(); - if (nlflag) - return n1; - } else { - tokpushback++; - } - checkkwd = 2; - if (peektoken()) - return n1; - break; - case TEOF: - if (heredoclist) - parseheredoc(); - else - pungetc(); /* push back EOF on input */ - return n1; - default: - if (nlflag) - synexpect(-1); - tokpushback++; - return n1; - } - } -} - - - -static union node * -andor() { - union node *n1, *n2, *n3; - int t; - - checkkwd = 1; - n1 = pipeline(); - for (;;) { - if ((t = readtoken()) == TAND) { - t = NAND; - } else if (t == TOR) { - t = NOR; - } else { - tokpushback++; - return n1; - } - checkkwd = 2; - n2 = pipeline(); - n3 = (union node *)stalloc(sizeof (struct nbinary)); - n3->type = t; - n3->nbinary.ch1 = n1; - n3->nbinary.ch2 = n2; - n1 = n3; - } -} - - - -static union node * -pipeline() { - union node *n1, *n2, *pipenode; - struct nodelist *lp, *prev; - int negate; - - negate = 0; - TRACE(("pipeline: entered\n")); - if (readtoken() == TNOT) { - negate = !negate; - checkkwd = 1; - } else - tokpushback++; - n1 = command(); - if (readtoken() == TPIPE) { - pipenode = (union node *)stalloc(sizeof (struct npipe)); - pipenode->type = NPIPE; - pipenode->npipe.backgnd = 0; - lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - pipenode->npipe.cmdlist = lp; - lp->n = n1; - do { - prev = lp; - lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - checkkwd = 2; - lp->n = command(); - prev->next = lp; - } while (readtoken() == TPIPE); - lp->next = NULL; - n1 = pipenode; - } - tokpushback++; - if (negate) { - n2 = (union node *)stalloc(sizeof (struct nnot)); - n2->type = NNOT; - n2->nnot.com = n1; - return n2; - } else - return n1; -} - - - -static union node * -command() { - union node *n1, *n2; - union node *ap, **app; - union node *cp, **cpp; - union node *redir, **rpp; - int t; - - redir = NULL; - n1 = NULL; - rpp = &redir; - - /* Check for redirection which may precede command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - - switch (readtoken()) { - case TIF: - n1 = (union node *)stalloc(sizeof (struct nif)); - n1->type = NIF; - n1->nif.test = list(0); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n1->nif.ifpart = list(0); - n2 = n1; - while (readtoken() == TELIF) { - n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); - n2 = n2->nif.elsepart; - n2->type = NIF; - n2->nif.test = list(0); - if (readtoken() != TTHEN) - synexpect(TTHEN); - n2->nif.ifpart = list(0); - } - if (lasttoken == TELSE) - n2->nif.elsepart = list(0); - else { - n2->nif.elsepart = NULL; - tokpushback++; - } - if (readtoken() != TFI) - synexpect(TFI); - checkkwd = 1; - break; - case TWHILE: - case TUNTIL: { - int got; - n1 = (union node *)stalloc(sizeof (struct nbinary)); - n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; - n1->nbinary.ch1 = list(0); - if ((got=readtoken()) != TDO) { -TRACE(("expecting DO got %s %s\n", tokname(got), got == TWORD ? wordtext : "")); - synexpect(TDO); - } - n1->nbinary.ch2 = list(0); - if (readtoken() != TDONE) - synexpect(TDONE); - checkkwd = 1; - break; - } - case TFOR: - if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) - synerror("Bad for loop variable"); - n1 = (union node *)stalloc(sizeof (struct nfor)); - n1->type = NFOR; - n1->nfor.var = wordtext; - checkkwd = 1; - if (readtoken() == TIN) { - app = ≈ - while (readtoken() == TWORD) { - n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; - *app = n2; - app = &n2->narg.next; - } - *app = NULL; - n1->nfor.args = ap; - if (lasttoken != TNL && lasttoken != TSEMI) - synexpect(-1); - } else { - static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, - '@', '=', '\0'}; - n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = argvars; - n2->narg.backquote = NULL; - n2->narg.next = NULL; - n1->nfor.args = n2; - /* - * Newline or semicolon here is optional (but note - * that the original Bourne shell only allowed NL). - */ - if (lasttoken != TNL && lasttoken != TSEMI) - tokpushback++; - } - checkkwd = 2; - if (readtoken() != TDO) - synexpect(TDO); - n1->nfor.body = list(0); - if (readtoken() != TDONE) - synexpect(TDONE); - checkkwd = 1; - break; - case TCASE: - n1 = (union node *)stalloc(sizeof (struct ncase)); - n1->type = NCASE; - if (readtoken() != TWORD) - synexpect(TWORD); - n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); - n2->type = NARG; - n2->narg.text = wordtext; - n2->narg.backquote = backquotelist; - n2->narg.next = NULL; - do { - checkkwd = 1; - } while (readtoken() == TNL); - if (lasttoken != TIN) - synerror("expecting \"in\""); - cpp = &n1->ncase.cases; - checkkwd = 2, readtoken(); - do { - if (lasttoken == TLP) - readtoken(); - *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); - cp->type = NCLIST; - app = &cp->nclist.pattern; - for (;;) { - *app = ap = (union node *)stalloc(sizeof (struct narg)); - ap->type = NARG; - ap->narg.text = wordtext; - ap->narg.backquote = backquotelist; - if (checkkwd = 2, readtoken() != TPIPE) - break; - app = &ap->narg.next; - readtoken(); - } - ap->narg.next = NULL; - if (lasttoken != TRP) - synexpect(TRP); - cp->nclist.body = list(0); - - checkkwd = 2; - if ((t = readtoken()) != TESAC) { - if (t != TENDCASE) - synexpect(TENDCASE); - else - checkkwd = 2, readtoken(); - } - cpp = &cp->nclist.next; - } while(lasttoken != TESAC); - *cpp = NULL; - checkkwd = 1; - break; - case TLP: - n1 = (union node *)stalloc(sizeof (struct nredir)); - n1->type = NSUBSHELL; - n1->nredir.n = list(0); - n1->nredir.redirect = NULL; - if (readtoken() != TRP) - synexpect(TRP); - checkkwd = 1; - break; - case TBEGIN: - n1 = list(0); - if (readtoken() != TEND) - synexpect(TEND); - checkkwd = 1; - break; - /* Handle an empty command like other simple commands. */ - case TSEMI: - case TAND: - case TOR: - case TNL: - case TEOF: - case TRP: - case TBACKGND: - /* - * An empty command before a ; doesn't make much sense, and - * should certainly be disallowed in the case of `if ;'. - */ - if (!redir) - synexpect(-1); - case TWORD: - tokpushback++; - n1 = simplecmd(); - return n1; - default: - synexpect(-1); - /* NOTREACHED */ - } - - /* Now check for redirection which may follow command */ - while (readtoken() == TREDIR) { - *rpp = n2 = redirnode; - rpp = &n2->nfile.next; - parsefname(); - } - tokpushback++; - *rpp = NULL; - if (redir) { - if (n1->type != NSUBSHELL) { - n2 = (union node *)stalloc(sizeof (struct nredir)); - n2->type = NREDIR; - n2->nredir.n = n1; - n1 = n2; - } - n1->nredir.redirect = redir; - } - - return n1; -} - - -static union node * -simplecmd() { - union node *args, **app; - union node *n = NULL; - union node *vars, **vpp; - union node **rpp, *redir; - - args = NULL; - app = &args; - vars = NULL; - vpp = &vars; - redir = NULL; - rpp = &redir; - - checkalias = 2; - for (;;) { - switch (readtoken()) { - case TWORD: - case TASSIGN: - n = (union node *)stalloc(sizeof (struct narg)); - n->type = NARG; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - if (lasttoken == TWORD) { - *app = n; - app = &n->narg.next; - } else { - *vpp = n; - vpp = &n->narg.next; - } - break; - case TREDIR: - *rpp = n = redirnode; - rpp = &n->nfile.next; - parsefname(); /* read name of redirection file */ - break; - case TLP: - if ( - args && app == &args->narg.next && - !vars && !redir - ) { - /* We have a function */ - if (readtoken() != TRP) - synexpect(TRP); - n->type = NDEFUN; - checkkwd = 2; - n->narg.next = command(); - return n; - } - /* fall through */ - default: - tokpushback++; - goto out; - } - } -out: - *app = NULL; - *vpp = NULL; - *rpp = NULL; - n = (union node *)stalloc(sizeof (struct ncmd)); - n->type = NCMD; - n->ncmd.backgnd = 0; - n->ncmd.args = args; - n->ncmd.assign = vars; - n->ncmd.redirect = redir; - return n; -} - -static union node * -makename(void) { - union node *n; - - n = (union node *)stalloc(sizeof (struct narg)); - n->type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - return n; -} - -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; - - if (is_digit(text[0]) && text[1] == '\0') - n->ndup.dupfd = digit_val(text[0]); - else if (text[0] == '-' && text[1] == '\0') - n->ndup.dupfd = -1; - else { - - if (err) - synerror("Bad fd number"); - else - n->ndup.vname = makename(); - } -} - - -static void -parsefname(void) { - union node *n = redirnode; - - if (readtoken() != TWORD) - synexpect(-1); - if (n->type == NHERE) { - struct heredoc *here = heredoc; - struct heredoc *p; - int i; - - if (quoteflag == 0) - n->type = NXHERE; - TRACE(("Here document %d\n", n->type)); - if (here->striptabs) { - while (*wordtext == '\t') - wordtext++; - } - if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) - synerror("Illegal eof marker for << redirection"); - rmescapes(wordtext); - here->eofmark = wordtext; - here->next = NULL; - if (heredoclist == NULL) - heredoclist = here; - else { - for (p = heredoclist ; p->next ; p = p->next); - p->next = here; - } - } else if (n->type == NTOFD || n->type == NFROMFD) { - fixredir(n, wordtext, 0); - } else { - n->nfile.fname = makename(); - } -} - - -/* - * Input any here documents. - */ - -static void -parseheredoc() { - struct heredoc *here; - union node *n; - - while (heredoclist) { - here = heredoclist; - heredoclist = here->next; - if (needprompt) { - setprompt(2); - needprompt = 0; - } - readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, - here->eofmark, here->striptabs); - n = (union node *)stalloc(sizeof (struct narg)); - n->narg.type = NARG; - n->narg.next = NULL; - n->narg.text = wordtext; - n->narg.backquote = backquotelist; - here->here->nhere.doc = n; - } -} - -static char -peektoken() { - int t; - - t = readtoken(); - tokpushback++; - return tokname_array[t][0]; -} - -static int -readtoken() { - int t; - -#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) { - /* - * eat newlines - */ - if (checkkwd == 2) { - checkkwd = 0; - while (t == TNL) { - parseheredoc(); - t = xxreadtoken(); - } - } - checkkwd = 0; - /* - * check for keywords - */ - if (t == TWORD && !quoteflag) - { - const char *const *pp; - - if ((pp = findkwd(wordtext))) { - lasttoken = t = pp - tokname_array; - TRACE(("keyword %s recognized\n", tokname(t))); - goto out; - } - } - } - - - 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) { - pushstring(ap->val, strlen(ap->val), ap); - } - checkkwd = savecheckkwd; - goto top; - } - checkalias = 0; -#endif - } -out: -#ifdef DEBUG - if (!alreadyseen) - TRACE(("token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : "")); - else - TRACE(("reread token %s %s\n", tokname(t), t == TWORD || t == TASSIGN ? wordtext : "")); -#endif - return (t); -} - - -/* - * 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. - * If the token is TREDIR, then we set redirnode to a structure containing - * the redirection. - * In all cases, the variable startlinno is set to the number of the line - * on which the token starts. - * - * [Change comment: here documents and internal procedures] - * [Readtoken shouldn't have any arguments. Perhaps we should make the - * word parsing code into a separate routine. In this case, readtoken - * doesn't need to have any internal procedures, but parseword does. - * We could also make parseoperator in essence the main routine, and - * have parseword (readtoken1?) handle both words and redirection.] - */ - -#define NEW_xxreadtoken -#ifdef NEW_xxreadtoken - -static const char xxreadtoken_chars[] = "\n()&|;"; /* singles must be first! */ -static const char xxreadtoken_tokens[] = { - TNL, TLP, TRP, /* only single occurrence allowed */ - TBACKGND, TPIPE, TSEMI, /* if single occurrence */ - TEOF, /* corresponds to trailing nul */ - TAND, TOR, TENDCASE, /* if double occurrence */ -}; - -#define xxreadtoken_doubles \ - (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars)) -#define xxreadtoken_singles \ - (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1) - -static int -xxreadtoken() { - int c; - - if (tokpushback) { - tokpushback = 0; - return lasttoken; - } - if (needprompt) { - setprompt(2); - needprompt = 0; - } - startlinno = plinno; - for (;;) { /* until token or start of word found */ - c = pgetc_macro(); - - if ((c!=' ') && (c!='\t') -#ifdef ASH_ALIAS - && (c!=PEOA) -#endif - ) { - if (c=='#') { - while ((c = pgetc()) != '\n' && c != PEOF); - pungetc(); - } else if (c=='\\') { - if (pgetc() != '\n') { - pungetc(); - goto READTOKEN1; - } - startlinno = ++plinno; - setprompt(doprompt ? 2 : 0); - } else { - const char *p - = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; - - if (c!=PEOF) { - if (c=='\n') { - plinno++; - needprompt = doprompt; - } - - p = strchr(xxreadtoken_chars, c); - if (p == NULL) { - READTOKEN1: - return readtoken1(c, BASESYNTAX, (char *)NULL, 0); - } - - if (p-xxreadtoken_chars >= xxreadtoken_singles) { - if (pgetc() == *p) { /* double occurrence? */ - p += xxreadtoken_doubles + 1; - } else { - pungetc(); - } - } - } - - return lasttoken = xxreadtoken_tokens[p-xxreadtoken_chars]; - } - } - } -} - - -#else -#define RETURN(token) return lasttoken = token - -static int -xxreadtoken() { - int c; - - if (tokpushback) { - tokpushback = 0; - return lasttoken; - } - if (needprompt) { - setprompt(2); - needprompt = 0; - } - startlinno = plinno; - 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); - pungetc(); - continue; - case '\\': - if (pgetc() == '\n') { - startlinno = ++plinno; - if (doprompt) - setprompt(2); - else - setprompt(0); - continue; - } - pungetc(); - goto breakloop; - case '\n': - plinno++; - needprompt = doprompt; - RETURN(TNL); - case PEOF: - RETURN(TEOF); - case '&': - if (pgetc() == '&') - RETURN(TAND); - pungetc(); - RETURN(TBACKGND); - case '|': - if (pgetc() == '|') - RETURN(TOR); - pungetc(); - RETURN(TPIPE); - case ';': - if (pgetc() == ';') - RETURN(TENDCASE); - pungetc(); - RETURN(TSEMI); - case '(': - RETURN(TLP); - case ')': - RETURN(TRP); - default: - goto breakloop; - } - } -breakloop: - return readtoken1(c, BASESYNTAX, (char *)NULL, 0); -#undef RETURN -} -#endif - - -/* - * If eofmark is NULL, read a word or a redirection symbol. If eofmark - * is not NULL, read a here document. In the latter case, eofmark is the - * word which marks the end of the document and striptabs is true if - * leading tabs should be stripped from the document. The argument firstc - * is the first character of the input token or document. - * - * Because C does not have internal subroutines, I have simulated them - * using goto's to implement the subroutine linkage. The following macros - * 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:;} - -static int -readtoken1(int firstc, int syntax, const char *eofmark, int striptabs) -{ - int c = firstc; - char *out; - int len; - char line[EOFMARKLEN + 1]; - 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 oldstyle; - int prevsyntax; /* syntax before arithmetic */ -#if __GNUC__ - /* Avoid longjmp clobbering */ - (void) &out; - (void) "ef; - (void) &dblquote; - (void) &varnest; - (void) &arinest; - (void) &parenlevel; - (void) &dqvarnest; - (void) &oldstyle; - (void) &prevsyntax; - (void) &syntax; -#endif - - startlinno = plinno; - dblquote = 0; - if (syntax == DQSYNTAX) - dblquote = 1; - quotef = 0; - bqlist = NULL; - varnest = 0; - arinest = 0; - parenlevel = 0; - dqvarnest = 0; - - STARTSTACKSTR(out); - 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(SIT(c,syntax)) { - case CNL: /* '\n' */ - if (syntax == BASESYNTAX) - goto endword; /* exit outer loop */ - USTPUTC(c, out); - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - c = pgetc(); - goto loop; /* continue outer loop */ - case CWORD: - USTPUTC(c, out); - break; - case CCTL: - if ((eofmark == NULL || dblquote) && - dqvarnest == 0) - USTPUTC(CTLESC, out); - USTPUTC(c, out); - break; - case CBACK: /* backslash */ - c = pgetc2(); - if (c == PEOF) { - USTPUTC('\\', out); - pungetc(); - } else if (c == '\n') { - if (doprompt) - setprompt(2); - else - setprompt(0); - } else { - if (dblquote && c != '\\' && c != '`' && c != '$' - && (c != '"' || eofmark != NULL)) - USTPUTC('\\', out); - if (SIT(c,SQSYNTAX) == CCTL) - USTPUTC(CTLESC, out); - else if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - USTPUTC(c, out); - quotef++; - } - break; - case CSQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = SQSYNTAX; - break; - case CDQUOTE: - if (eofmark == NULL) - USTPUTC(CTLQUOTEMARK, out); - syntax = DQSYNTAX; - dblquote = 1; - break; - case CENDQUOTE: - if (eofmark != NULL && arinest == 0 && - varnest == 0) { - USTPUTC(c, out); - } else { - if (arinest) { - syntax = ARISYNTAX; - dblquote = 0; - } else if (eofmark == NULL && - dqvarnest == 0) { - syntax = BASESYNTAX; - dblquote = 0; - } - quotef++; - } - break; - case CVAR: /* '$' */ - PARSESUB(); /* parse substitution */ - break; - case CENDVAR: /* '}' */ - if (varnest > 0) { - varnest--; - if (dqvarnest > 0) { - dqvarnest--; - } - USTPUTC(CTLENDVAR, out); - } else { - USTPUTC(c, out); - } - break; -#ifdef ASH_MATH_SUPPORT - case CLP: /* '(' in arithmetic */ - parenlevel++; - USTPUTC(c, out); - break; - case CRP: /* ')' in arithmetic */ - if (parenlevel > 0) { - USTPUTC(c, out); - --parenlevel; - } else { - if (pgetc() == ')') { - if (--arinest == 0) { - USTPUTC(CTLENDARI, out); - syntax = prevsyntax; - if (syntax == DQSYNTAX) - dblquote = 1; - else - dblquote = 0; - } else - USTPUTC(')', out); - } else { - /* - * unbalanced parens - * (don't 2nd guess - no error) - */ - pungetc(); - USTPUTC(')', out); - } - } - break; -#endif - case CBQUOTE: /* '`' */ - PARSEBACKQOLD(); - break; - case CENDFILE: - goto endword; /* exit outer loop */ - case CIGN: - break; - default: - if (varnest == 0) - goto endword; /* exit outer loop */ -#ifdef ASH_ALIAS - if (c != PEOA) -#endif - USTPUTC(c, out); - - } - c = pgetc_macro(); - } - } -endword: - if (syntax == ARISYNTAX) - synerror("Missing '))'"); - if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) - synerror("Unterminated quoted string"); - if (varnest != 0) { - startlinno = plinno; - synerror("Missing '}'"); - } - USTPUTC('\0', out); - len = out - stackblock(); - out = stackblock(); - if (eofmark == NULL) { - if ((c == '>' || c == '<') - && quotef == 0 - && len <= 2 - && (*out == '\0' || is_digit(*out))) { - PARSEREDIR(); - return lasttoken = TREDIR; - } else { - pungetc(); - } - } - quoteflag = quotef; - backquotelist = bqlist; - grabstackblock(len); - wordtext = out; - return lasttoken = TWORD; -/* end of readtoken routine */ - - - -/* - * Check to see whether we are at the end of the here document. When this - * is called, c is set to the first character of the next input line. If - * we are at the end of the here document, this routine sets the c to PEOF. - */ - -checkend: { - if (eofmark) { -#ifdef ASH_ALIAS - if (c == PEOA) { - c = pgetc2(); - } -#endif - if (striptabs) { - while (c == '\t') { - c = pgetc2(); - } - } - if (c == *eofmark) { - if (pfgets(line, sizeof line) != NULL) { - const char *p, *q; - - p = line; - for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); - if (*p == '\n' && *q == '\0') { - c = PEOF; - plinno++; - needprompt = doprompt; - } else { - pushstring(line, strlen(line), NULL); - } - } - } - } - goto checkend_return; -} - - -/* - * Parse a redirection operator. The variable "out" points to a string - * specifying the fd to be redirected. The variable "c" contains the - * first character of the redirection operator. - */ - -parseredir: { - char fd = *out; - union node *np; - - np = (union node *)stalloc(sizeof (struct nfile)); - if (c == '>') { - np->nfile.fd = 1; - c = pgetc(); - if (c == '>') - np->type = NAPPEND; - else if (c == '&') - np->type = NTOFD; - else if (c == '|') - np->type = NTOOV; - else { - np->type = NTO; - pungetc(); - } - } else { /* c == '<' */ - np->nfile.fd = 0; - switch (c = pgetc()) { - case '<': - if (sizeof (struct nfile) != sizeof (struct nhere)) { - np = (union node *)stalloc(sizeof (struct nhere)); - np->nfile.fd = 0; - } - np->type = NHERE; - heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); - heredoc->here = np; - if ((c = pgetc()) == '-') { - heredoc->striptabs = 1; - } else { - heredoc->striptabs = 0; - pungetc(); - } - break; - - case '&': - np->type = NFROMFD; - break; - - case '>': - np->type = NFROMTO; - break; - - default: - np->type = NFROM; - pungetc(); - break; - } - } - if (fd != '\0') - np->nfile.fd = digit_val(fd); - redirnode = np; - goto parseredir_return; -} - - -/* - * Parse a substitution. At this point, we have read the dollar sign - * and nothing else. - */ - -parsesub: { - int subtype; - int typeloc; - int flags; - char *p; - static const char types[] = "}-+?="; - - c = pgetc(); - if ( - c <= PEOA || - (c != '(' && c != '{' && !is_name(c) && !is_special(c)) - ) { - USTPUTC('$', out); - pungetc(); - } else if (c == '(') { /* $(command) or $((arith)) */ - if (pgetc() == '(') { - PARSEARITH(); - } else { - pungetc(); - PARSEBACKQNEW(); - } - } else { - USTPUTC(CTLVAR, out); - typeloc = out - stackblock(); - USTPUTC(VSNORMAL, out); - subtype = VSNORMAL; - if (c == '{') { - c = pgetc(); - if (c == '#') { - if ((c = pgetc()) == '}') - c = '#'; - else - subtype = VSLENGTH; - } - else - subtype = 0; - } - if (c > PEOA && is_name(c)) { - do { - STPUTC(c, out); - c = pgetc(); - } while (c > PEOA && is_in_name(c)); - } else if (is_digit(c)) { - do { - USTPUTC(c, out); - c = pgetc(); - } while (is_digit(c)); - } - else if (is_special(c)) { - USTPUTC(c, out); - c = pgetc(); - } - else -badsub: synerror("Bad substitution"); - - STPUTC('=', out); - flags = 0; - if (subtype == 0) { - switch (c) { - case ':': - flags = VSNUL; - c = pgetc(); - /*FALLTHROUGH*/ - default: - p = strchr(types, c); - if (p == NULL) - goto badsub; - subtype = p - types + VSNORMAL; - break; - case '%': - case '#': - { - int cc = c; - subtype = c == '#' ? VSTRIMLEFT : - VSTRIMRIGHT; - c = pgetc(); - if (c == cc) - subtype++; - else - pungetc(); - break; - } - } - } else { - pungetc(); - } - if (dblquote || arinest) - flags |= VSQUOTE; - *(stackblock() + typeloc) = subtype | flags; - if (subtype != VSNORMAL) { - varnest++; - if (dblquote) { - dqvarnest++; - } - } - } - goto parsesub_return; -} - - -/* - * Called to parse command substitutions. Newstyle is set if the command - * is enclosed inside $(...); nlpp is a pointer to the head of the linked - * list of commands (passed by reference), and savelen is the number of - * characters on the top of the stack which must be preserved. - */ - -parsebackq: { - struct nodelist **nlpp; - int savepbq; - union node *n; - char *volatile str; - struct jmploc jmploc; - struct jmploc *volatile savehandler; - int savelen; - int saveprompt; -#ifdef __GNUC__ - (void) &saveprompt; -#endif - - savepbq = parsebackquote; - if (setjmp(jmploc.loc)) { - if (str) - ckfree(str); - parsebackquote = 0; - handler = savehandler; - longjmp(handler->loc, 1); - } - INTOFF; - str = NULL; - savelen = out - stackblock(); - if (savelen > 0) { - str = ckmalloc(savelen); - memcpy(str, stackblock(), savelen); - } - 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; - - - STARTSTACKSTR(pout); - for (;;) { - if (needprompt) { - setprompt(2); - needprompt = 0; - } - switch (pc = pgetc()) { - case '`': - goto done; - - case '\\': - if ((pc = pgetc()) == '\n') { - plinno++; - if (doprompt) - setprompt(2); - else - setprompt(0); - /* - * If eating a newline, avoid putting - * the newline into the new character - * stream (via the STPUTC after the - * switch). - */ - continue; - } - if (pc != '\\' && pc != '`' && pc != '$' - && (!dblquote || pc != '"')) - STPUTC('\\', pout); - if (pc > PEOA) { - break; - } - /* fall through */ - - case PEOF: -#ifdef ASH_ALIAS - case PEOA: -#endif - startlinno = plinno; - synerror("EOF in backquote substitution"); - - case '\n': - plinno++; - needprompt = doprompt; - break; - - default: - break; - } - STPUTC(pc, pout); - } -done: - STPUTC('\0', pout); - psavelen = pout - stackblock(); - if (psavelen > 0) { - pstr = grabstackstr(pout); - setinputstring(pstr); - } - } - nlpp = &bqlist; - while (*nlpp) - nlpp = &(*nlpp)->next; - *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); - (*nlpp)->next = NULL; - parsebackquote = oldstyle; - - if (oldstyle) { - saveprompt = doprompt; - doprompt = 0; - } - - n = list(0); - - if (oldstyle) - doprompt = saveprompt; - else { - if (readtoken() != TRP) - synexpect(TRP); - } - - (*nlpp)->n = n; - if (oldstyle) { - /* - * Start reading from old file again, ignoring any pushed back - * tokens left from the backquote parsing - */ - popfile(); - tokpushback = 0; - } - while (stackblocksize() <= savelen) - growstackblock(); - STARTSTACKSTR(out); - if (str) { - memcpy(out, str, savelen); - STADJUST(savelen, out); - INTOFF; - ckfree(str); - str = NULL; - INTON; - } - parsebackquote = savepbq; - handler = savehandler; - if (arinest || dblquote) - USTPUTC(CTLBACKQ | CTLQUOTE, out); - else - USTPUTC(CTLBACKQ, out); - if (oldstyle) - goto parsebackq_oldreturn; - else - goto parsebackq_newreturn; -} - -/* - * Parse an arithmetic expansion (indicate start of one and set state) - */ -parsearith: { - - if (++arinest == 1) { - prevsyntax = syntax; - syntax = ARISYNTAX; - USTPUTC(CTLARI, out); - if (dblquote) - USTPUTC('"',out); - else - USTPUTC(' ',out); - } else { - /* - * we collapse embedded arithmetic expansion to - * parenthesis, which should be equivalent - */ - USTPUTC('(', out); - } - goto parsearith_return; -} - -} /* end of readtoken */ - - -/* - * Returns true if the text contains nothing to expand (no dollar signs - * or backquotes). - */ - -static int -noexpand(text) - char *text; - { - char *p; - char c; - - p = text; - while ((c = *p++) != '\0') { - if (c == CTLQUOTEMARK) - continue; - if (c == CTLESC) - p++; - else if (SIT(c,BASESYNTAX) == CCTL) - return 0; - } - return 1; -} - - -/* - * Return true if the argument is a legal variable name (a letter or - * underscore followed by zero or more letters, underscores, and digits). - */ - -static int -goodname(const char *name) -{ - const char *p; - - p = name; - if (! is_name(*p)) - return 0; - while (*++p) { - if (! is_in_name(*p)) - return 0; - } - return 1; -} - - -/* - * Called when an unexpected token is read during the parse. The argument - * is the token that is expected, or -1 if more than one type of token can - * occur at this point. - */ - -static void -synexpect(token) - int token; -{ - char msg[64]; - int l; - - l = sprintf(msg, "%s unexpected", tokname(lasttoken)); - if (token >= 0) - sprintf(msg+l, " (expecting %s)", tokname(token)); - synerror(msg); - /* NOTREACHED */ -} - - -static void -synerror(const char *msg) -{ - if (commandname) - out2fmt("%s: %d: ", commandname, startlinno); - out2fmt("Syntax error: %s\n", msg); - error((char *)NULL); - /* NOTREACHED */ -} - - -/* - * called by editline -- any expansions to the prompt - * should be added here. - */ -static void -setprompt(int whichprompt) -{ - char *prompt; - switch (whichprompt) { - case 1: - prompt = ps1val(); - break; - case 2: - prompt = ps2val(); - break; - default: /* 0 */ - prompt = ""; - } - putprompt(prompt); -} - - -/* - * Code for dealing with input/output redirection. - */ - -#define EMPTY -2 /* marks an unused slot in redirtab */ -#ifndef PIPE_BUF -# define PIPESIZE 4096 /* amount of buffering in a pipe */ -#else -# define PIPESIZE PIPE_BUF -#endif - - -/* - * Open a file in noclobber mode. - * The code was copied from bash. - */ -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. - */ - - /* - * 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; -} - -/* - * 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 inline int -openhere(const union node *redir) -{ - int pip[2]; - int len = 0; - - 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 (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 inline int -openredirect(const union node *redir) -{ - char *fname; - int f; - - switch (redir->nfile.type) { - case NFROM: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDONLY)) < 0) - goto eopen; - break; - case NFROMTO: - fname = redir->nfile.expfname; - if ((f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666)) < 0) - goto ecreate; - break; - case NTO: - /* Take care of noclobber mode. */ - if (Cflag) { - fname = redir->nfile.expfname; - if ((f = noclobberopen(fname)) < 0) - goto ecreate; - break; - } - case NTOOV: - fname = redir->nfile.expfname; -#ifdef O_CREAT - if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) - goto ecreate; -#else - if ((f = creat(fname, 0666)) < 0) - goto ecreate; -#endif - break; - case NAPPEND: - fname = redir->nfile.expfname; -#ifdef O_APPEND - if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) - goto ecreate; -#else - if ((f = open(fname, O_WRONLY)) < 0 - && (f = creat(fname, 0666)) < 0) - goto ecreate; - lseek(f, (off_t)0, 2); -#endif - break; - default: -#ifdef DEBUG - abort(); -#endif - /* Fall through to eliminate warning. */ - case NTOFD: - case NFROMFD: - f = -1; - break; - case NHERE: - case NXHERE: - f = openhere(redir); - break; - } - - return f; -ecreate: - error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); -eopen: - error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); -} - - -/* - * 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 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 (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; - } -} - - -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; - } - - if (f != fd) { - dup_as_newfd(f, fd); - close(f); - } - return; -} - - - -/* - * Undo the effects of the last redirection. - */ - -static void -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--; - close(i); - if (rp->renamed[i] >= 0) { - dup_as_newfd(rp->renamed[i], i); - close(rp->renamed[i]); - } - } - } - redirlist = rp->next; - ckfree(rp); - INTON; -} - -/* - * Discard all saved file descriptors. - */ - -static void -clearredir(void) { - struct redirtab *rp; - int i; - - for (rp = redirlist ; rp ; rp = rp->next) { - for (i = 0 ; i < 10 ; i++) { - if (rp->renamed[i] >= 0) { - close(rp->renamed[i]); - } - rp->renamed[i] = EMPTY; - } - } -} - - -/* - * Copy a file descriptor to be >= to. Returns -1 - * if the source file descriptor is closed, EMPTY if there are no unused - * file descriptors left. - */ - -static int -dup_as_newfd(from, to) - int from; - int to; -{ - int newfd; - - newfd = fcntl(from, F_DUPFD, to); - if (newfd < 0) { - if (errno == EMFILE) - return EMPTY; - else - error("%d: %m", from); - } - return newfd; -} - -#ifdef DEBUG -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 -showtree(n) - union node *n; -{ - trputs("showtree called\n"); - shtree(n, 1, NULL, stdout); -} - - -static void -shtree(n, ind, pfx, fp) - union node *n; - int ind; - char *pfx; - FILE *fp; -{ - struct nodelist *lp; - const char *s; - - if (n == NULL) - return; - - indent(ind, pfx, fp); - switch(n->type) { - case NSEMI: - s = "; "; - goto binop; - case NAND: - s = " && "; - goto binop; - case NOR: - s = " || "; -binop: - shtree(n->nbinary.ch1, ind, NULL, fp); - /* if (ind < 0) */ - fputs(s, fp); - shtree(n->nbinary.ch2, ind, NULL, fp); - break; - case NCMD: - shcmd(n, fp); - if (ind >= 0) - putc('\n', fp); - break; - case NPIPE: - for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { - shcmd(lp->n, fp); - if (lp->next) - fputs(" | ", fp); - } - if (n->npipe.backgnd) - fputs(" &", fp); - if (ind >= 0) - putc('\n', fp); - break; - default: - fprintf(fp, "<node type %d>", n->type); - if (ind >= 0) - putc('\n', fp); - break; - } -} - - - -static void -shcmd(cmd, fp) - union node *cmd; - FILE *fp; -{ - union node *np; - int first; - const char *s; - int dftfd; - - first = 1; - for (np = cmd->ncmd.args ; np ; np = np->narg.next) { - if (! first) - putchar(' '); - sharg(np, fp); - first = 0; - } - for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { - if (! first) - putchar(' '); -#if 1 - s = "*error*"; - dftfd = 0; - if ((np->nfile.type <= NFROMFD) && (np->nfile.type >= NTO)) { - s = redir_strings[np->nfile.type - NTO]; - if (*s == '>') { - dftfd = 1; - } - } -#else - 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; - } -#endif - if (np->nfile.fd != dftfd) - fprintf(fp, "%d", np->nfile.fd); - fputs(s, fp); - if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { - fprintf(fp, "%d", np->ndup.dupfd); - } else { - sharg(np->nfile.fname, fp); - } - first = 0; - } -} - - - -static void -sharg(arg, fp) - union node *arg; - FILE *fp; - { - char *p; - struct nodelist *bqlist; - int subtype; - - if (arg->type != NARG) { - printf("<node type %d>\n", arg->type); - fflush(stdout); - abort(); - } - bqlist = arg->narg.backquote; - for (p = arg->narg.text ; *p ; p++) { - switch (*p) { - case CTLESC: - putc(*++p, fp); - break; - case CTLVAR: - putc('$', fp); - putc('{', fp); - subtype = *++p; - if (subtype == VSLENGTH) - putc('#', fp); - - while (*p != '=') - putc(*p++, fp); - - if (subtype & VSNUL) - putc(':', fp); - - switch (subtype & VSTYPE) { - case VSNORMAL: - putc('}', fp); - break; - case VSMINUS: - putc('-', fp); - break; - case VSPLUS: - putc('+', fp); - break; - case VSQUESTION: - putc('?', fp); - break; - case VSASSIGN: - putc('=', fp); - break; - case VSTRIMLEFT: - putc('#', fp); - break; - case VSTRIMLEFTMAX: - putc('#', fp); - putc('#', fp); - break; - case VSTRIMRIGHT: - putc('%', fp); - break; - case VSTRIMRIGHTMAX: - putc('%', fp); - putc('%', fp); - break; - case VSLENGTH: - break; - default: - printf("<subtype %d>", subtype); - } - break; - case CTLENDVAR: - putc('}', fp); - break; - case CTLBACKQ: - case CTLBACKQ|CTLQUOTE: - putc('$', fp); - putc('(', fp); - shtree(bqlist->n, -1, NULL, fp); - putc(')', fp); - break; - default: - putc(*p, fp); - break; - } - } -} - - -static void -indent(amount, pfx, fp) - int amount; - char *pfx; - FILE *fp; -{ - int i; - - for (i = 0 ; i < amount ; i++) { - if (pfx && i == amount - 1) - fputs(pfx, fp); - putc('\t', fp); - } -} -#endif - - - -/* - * Debugging stuff. - */ - - -#ifdef DEBUG -FILE *tracefile; - -#if DEBUG == 2 -static int debug = 1; -#else -static int debug = 0; -#endif - - -static void -trputc(c) - int c; -{ - if (tracefile == NULL) - return; - putc(c, tracefile); - if (c == '\n') - fflush(tracefile); -} - -static void -trace(const char *fmt, ...) -{ - va_list va; - va_start(va, fmt); - if (tracefile != NULL) { - (void) vfprintf(tracefile, fmt, va); - if (strchr(fmt, '\n')) - (void) fflush(tracefile); - } - va_end(va); -} - - -static void -trputs(s) - const char *s; -{ - if (tracefile == NULL) - return; - fputs(s, tracefile); - if (strchr(s, '\n')) - fflush(tracefile); -} - - -static void -trstring(s) - char *s; -{ - char *p; - char c; - - if (tracefile == NULL) - return; - putc('"', tracefile); - for (p = s ; *p ; p++) { - switch (*p) { - case '\n': c = 'n'; goto backslash; - case '\t': c = 't'; goto backslash; - case '\r': c = 'r'; goto backslash; - case '"': c = '"'; goto backslash; - case '\\': c = '\\'; goto backslash; - case CTLESC: c = 'e'; goto backslash; - case CTLVAR: c = 'v'; goto backslash; - case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; - case CTLBACKQ: c = 'q'; goto backslash; - case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; -backslash: putc('\\', tracefile); - putc(c, tracefile); - break; - default: - if (*p >= ' ' && *p <= '~') - putc(*p, tracefile); - else { - putc('\\', tracefile); - putc(*p >> 6 & 03, tracefile); - putc(*p >> 3 & 07, tracefile); - putc(*p & 07, tracefile); - } - break; - } - } - putc('"', tracefile); -} - - -static void -trargs(ap) - char **ap; -{ - if (tracefile == NULL) - return; - while (*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"; - } - 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. - */ - -static int -trapcmd(argc, argv) - int argc; - char **argv; -{ - char *action; - char **ap; - int signo; - - if (argc <= 1) { - for (signo = 0 ; signo < NSIG ; signo++) { - if (trap[signo] != NULL) { - char *p; - const char *sn; - - p = single_quote(trap[signo]); - sn = sys_siglist[signo]; - if(sn==NULL) - sn = u_signal_names(0, &signo, 0); - if(sn==NULL) - sn = "???"; - printf("trap -- %s %s\n", p, sn); - stunalloc(p); - } - } - return 0; - } - ap = argv + 1; - if (argc == 2) - action = NULL; - else - action = *ap++; - while (*ap) { - if ((signo = decode_signal(*ap, 0)) < 0) - error("%s: bad trap", *ap); - INTOFF; - if (action) { - if (action[0] == '-' && action[1] == '\0') - action = NULL; - else - action = savestr(action); - } - if (trap[signo]) - ckfree(trap[signo]); - trap[signo] = action; - if (signo != 0) - setsignal(signo); - INTON; - ap++; - } - return 0; -} - - - - - - -/* - * Set the signal handler for the specified signal. The routine figures - * out what it should be set to. - */ - -static void -setsignal(int signo) -{ - int action; - char *t; - struct sigaction act; - - if ((t = trap[signo]) == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - else - action = S_IGN; - if (rootshell && action == S_DFL) { - switch (signo) { - case SIGINT: - if (iflag || minusc || sflag == 0) - action = S_CATCH; - break; - case SIGQUIT: -#ifdef DEBUG - { - - if (debug) - break; - } -#endif - /* FALLTHROUGH */ - case SIGTERM: - if (iflag) - action = S_IGN; - break; -#ifdef JOBS - case SIGTSTP: - case SIGTTOU: - if (mflag) - action = S_IGN; - break; -#endif - } - } - - t = &sigmode[signo - 1]; - if (*t == 0) { - /* - * current setting unknown - */ - if (sigaction(signo, 0, &act) == -1) { - /* - * Pretend it worked; maybe we should give a warning - * here, but other shells don't. We don't alter - * sigmode, so that we retry every time. - */ - return; - } - if (act.sa_handler == SIG_IGN) { - if (mflag && (signo == SIGTSTP || - signo == SIGTTIN || signo == SIGTTOU)) { - *t = S_IGN; /* don't hard ignore these */ - } else - *t = S_HARD_IGN; - } else { - *t = S_RESET; /* force to be set */ - } - } - if (*t == S_HARD_IGN || *t == action) - return; - act.sa_handler = ((action == S_CATCH) ? onsig - : ((action == S_IGN) ? SIG_IGN : SIG_DFL)); - *t = action; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - sigaction(signo, &act, 0); -} - -/* - * Ignore a signal. - */ - -static void -ignoresig(signo) - int signo; -{ - if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { - signal(signo, SIG_IGN); - } - sigmode[signo - 1] = S_HARD_IGN; -} - - -/* - * Signal handler. - */ - -static void -onsig(int signo) -{ - if (signo == SIGINT && trap[SIGINT] == NULL) { - onint(); - return; - } - gotsig[signo - 1] = 1; - pendingsigs++; -} - - -/* - * Called to execute a trap. Perhaps we should avoid entering new trap - * handlers while we are executing a trap handler. - */ - -static void -dotrap(void) -{ - int i; - int savestatus; - - for (;;) { - for (i = 1 ; ; i++) { - if (gotsig[i - 1]) - break; - if (i >= NSIG - 1) - goto done; - } - gotsig[i - 1] = 0; - savestatus=exitstatus; - evalstring(trap[i], 0); - exitstatus=savestatus; - } -done: - pendingsigs = 0; -} - -/* - * Called to exit the shell. - */ - -static void -exitshell(int status) -{ - struct jmploc loc1, loc2; - char *p; - - TRACE(("exitshell(%d) pid=%d\n", status, getpid())); - if (setjmp(loc1.loc)) { - goto l1; - } - if (setjmp(loc2.loc)) { - goto l2; - } - handler = &loc1; - if ((p = trap[0]) != NULL && *p != '\0') { - trap[0] = NULL; - evalstring(p, 0); - } -l1: handler = &loc2; /* probably unnecessary */ - flushall(); -#ifdef JOBS - setjobctl(0); -#endif -l2: _exit(status); - /* NOTREACHED */ -} - -static int decode_signal(const char *string, int minsig) -{ - int signo; - const char *name = u_signal_names(string, &signo, minsig); - - return name ? signo : -1; -} - -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 - */ - -/* - * This routine initializes the builtin variables. It is called when the - * shell is initialized and again when a shell procedure is spawned. - */ - -static void -initvar() { - const struct varinit *ip; - struct var *vp; - struct var **vpp; - - for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { - if ((vp->flags & VEXPORT) == 0) { - vpp = hashvar(ip->text); - vp->next = *vpp; - *vpp = vp; - vp->text = strdup(ip->text); - vp->flags = ip->flags; - vp->func = ip->func; - } - } - /* - * PS1 depends on uid - */ - if ((vps1.flags & VEXPORT) == 0) { - vpp = hashvar("PS1="); - vps1.next = *vpp; - *vpp = &vps1; - vps1.text = strdup(geteuid() ? "PS1=$ " : "PS1=# "); - vps1.flags = VSTRFIXED|VTEXTFIXED; - } -} - -/* - * Set the value of a variable. The flags argument is ored with the - * flags of the variable. If val is NULL, the variable is unset. - */ - -static void -setvar(name, val, flags) - const char *name, *val; - int flags; -{ - const char *p; - int len; - int namelen; - char *nameeq; - int isbad; - int vallen = 0; - - isbad = 0; - p = name; - if (! is_name(*p)) - isbad = 1; - p++; - for (;;) { - if (! is_in_name(*p)) { - if (*p == '\0' || *p == '=') - break; - isbad = 1; - } - p++; - } - namelen = p - name; - if (isbad) - error("%.*s: bad variable name", namelen, name); - len = namelen + 2; /* 2 is space for '=' and '\0' */ - if (val == NULL) { - flags |= VUNSET; - } else { - len += vallen = strlen(val); - } - INTOFF; - nameeq = ckmalloc(len); - memcpy(nameeq, name, namelen); - nameeq[namelen] = '='; - if (val) { - memcpy(nameeq + namelen + 1, val, vallen + 1); - } else { - nameeq[namelen + 1] = '\0'; - } - setvareq(nameeq, flags); - INTON; -} - - - -/* - * Same as setvar except that the variable and value are passed in - * the first argument as name=value. Since the first argument will - * be actually stored in the table, it should not be a string that - * will go away. - */ - -static void -setvareq(s, flags) - char *s; - int flags; -{ - struct var *vp, **vpp; - - vpp = hashvar(s); - flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); - if ((vp = *findvar(vpp, s))) { - if (vp->flags & VREADONLY) { - size_t len = strchr(s, '=') - s; - error("%.*s: is read only", len, s); - } - INTOFF; - - if (vp->func && (flags & VNOFUNC) == 0) - (*vp->func)(strchr(s, '=') + 1); - - if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) - ckfree(vp->text); - - vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); - vp->flags |= flags; - vp->text = s; - - /* - * We could roll this to a function, to handle it as - * a regular variable function callback, but why bother? - */ - if (iflag && (vp == &vmpath || (vp == &vmail && !mpathset()))) - chkmail(1); - INTON; - return; - } - /* not found */ - vp = ckmalloc(sizeof (*vp)); - vp->flags = flags; - vp->text = s; - vp->next = *vpp; - vp->func = NULL; - *vpp = vp; -} - - - -/* - * Process a linked list of variable assignments. - */ - -static void -listsetvar(mylist) - struct strlist *mylist; - { - struct strlist *lp; - - INTOFF; - for (lp = mylist ; lp ; lp = lp->next) { - setvareq(savestr(lp->text), 0); - } - INTON; -} - - - -/* - * Find the value of a variable. Returns NULL if not set. - */ - -static const char * -lookupvar(name) - const char *name; - { - struct var *v; - - if ((v = *findvar(hashvar(name), name)) && !(v->flags & VUNSET)) { - return strchr(v->text, '=') + 1; - } - return NULL; -} - - - -/* - * Search the environment of a builtin command. - */ - -static const char * -bltinlookup(const char *name) -{ - const struct strlist *sp; - - for (sp = cmdenviron ; sp ; sp = sp->next) { - if (varequal(sp->text, name)) - return strchr(sp->text, '=') + 1; - } - return lookupvar(name); -} - - - -/* - * Generate a list of exported variables. This routine is used to construct - * the third argument to execve when executing a program. - */ - -static char ** -environment() { - int nenv; - struct var **vpp; - struct var *vp; - char **env; - char **ep; - - nenv = 0; - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if (vp->flags & VEXPORT) - nenv++; - } - ep = env = stalloc((nenv + 1) * sizeof *env); - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) - if (vp->flags & VEXPORT) - *ep++ = vp->text; - } - *ep = NULL; - return env; -} - - -/* - * Called when a shell procedure is invoked to clear out nonexported - * variables. It is also necessary to reallocate variables of with - * VSTACK set since these are currently allocated on the stack. - */ - -static void -shprocvar(void) { - struct var **vpp; - struct var *vp, **prev; - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (prev = vpp ; (vp = *prev) != NULL ; ) { - if ((vp->flags & VEXPORT) == 0) { - *prev = vp->next; - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - if ((vp->flags & VSTRFIXED) == 0) - ckfree(vp); - } else { - if (vp->flags & VSTACK) { - vp->text = savestr(vp->text); - vp->flags &=~ VSTACK; - } - prev = &vp->next; - } - } - } - initvar(); -} - - - -/* - * Command to list all variables which are set. Currently this command - * is invoked from the set command when the set command is called without - * any variables. - */ - -static int -showvarscmd(argc, argv) - int argc; - char **argv; -{ - showvars(nullstr, VUNSET, VUNSET); - return 0; -} - - - -/* - * The export and readonly commands. - */ - -static int -exportcmd(argc, argv) - int argc; - char **argv; -{ - struct var *vp; - char *name; - const char *p; - int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; - int pflag; - - listsetvar(cmdenviron); - pflag = (nextopt("p") == 'p'); - if (argc > 1 && !pflag) { - while ((name = *argptr++) != NULL) { - if ((p = strchr(name, '=')) != NULL) { - p++; - } else { - if ((vp = *findvar(hashvar(name), name))) { - vp->flags |= flag; - goto found; - } - } - setvar(name, p, flag); -found:; - } - } else { - showvars(argv[0], flag, 0); - } - return 0; -} - - -/* - * The "local" command. - */ - -/* funcnest nonzero if we are currently evaluating a function */ - -static int -localcmd(argc, argv) - int argc; - char **argv; -{ - char *name; - - if (! funcnest) - error("Not in a function"); - while ((name = *argptr++) != NULL) { - mklocal(name); - } - return 0; -} - - -/* - * Make a variable a local variable. When a variable is made local, it's - * value and flags are saved in a localvar structure. The saved values - * will be restored when the shell function returns. We handle the name - * "-" as a special case. - */ - -static void -mklocal(name) - char *name; - { - struct localvar *lvp; - struct var **vpp; - struct var *vp; - - INTOFF; - lvp = ckmalloc(sizeof (struct localvar)); - if (name[0] == '-' && name[1] == '\0') { - char *p; - p = ckmalloc(sizeof optet_vals); - lvp->text = memcpy(p, optet_vals, sizeof optet_vals); - vp = NULL; - } else { - vpp = hashvar(name); - vp = *findvar(vpp, name); - if (vp == NULL) { - if (strchr(name, '=')) - setvareq(savestr(name), VSTRFIXED); - else - setvar(name, NULL, VSTRFIXED); - vp = *vpp; /* the new variable */ - lvp->text = NULL; - lvp->flags = VUNSET; - } else { - lvp->text = vp->text; - lvp->flags = vp->flags; - vp->flags |= VSTRFIXED|VTEXTFIXED; - if (strchr(name, '=')) - setvareq(savestr(name), 0); - } - } - lvp->vp = vp; - lvp->next = localvars; - localvars = lvp; - INTON; -} - - -/* - * Called after a function returns. - */ - -static void -poplocalvars() { - struct localvar *lvp; - struct var *vp; - - while ((lvp = localvars) != NULL) { - localvars = lvp->next; - vp = lvp->vp; - 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); - } else { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - vp->flags = lvp->flags; - vp->text = lvp->text; - } - ckfree(lvp); - } -} - - -static int -setvarcmd(argc, argv) - int argc; - char **argv; -{ - if (argc <= 2) - return unsetcmd(argc, argv); - else if (argc == 3) - setvar(argv[1], argv[2], 0); - else - error("List assignment not implemented"); - return 0; -} - - -/* - * The unset builtin command. We unset the function before we unset the - * variable to allow a function to be unset when there is a readonly variable - * with the same name. - */ - -static int -unsetcmd(argc, argv) - int argc; - char **argv; -{ - char **ap; - int i; - int flg_func = 0; - int flg_var = 0; - int ret = 0; - - while ((i = nextopt("vf")) != '\0') { - if (i == 'f') - flg_func = 1; - else - flg_var = 1; - } - if (flg_func == 0 && flg_var == 0) - flg_var = 1; - - for (ap = argptr; *ap ; ap++) { - if (flg_func) - unsetfunc(*ap); - if (flg_var) - ret |= unsetvar(*ap); - } - return ret; -} - - -/* - * Unset the specified variable. - */ - -static int -unsetvar(const char *s) -{ - struct var **vpp; - struct var *vp; - - vpp = findvar(hashvar(s), s); - vp = *vpp; - if (vp) { - if (vp->flags & VREADONLY) - return (1); - INTOFF; - if (*(strchr(vp->text, '=') + 1) != '\0') - setvar(s, nullstr, 0); - vp->flags &= ~VEXPORT; - vp->flags |= VUNSET; - if ((vp->flags & VSTRFIXED) == 0) { - if ((vp->flags & VTEXTFIXED) == 0) - ckfree(vp->text); - *vpp = vp->next; - ckfree(vp); - } - INTON; - return (0); - } - - return (0); -} - - - -/* - * Find the appropriate entry in the hash table from the name. - */ - -static struct var ** -hashvar(const char *p) -{ - unsigned int hashval; - - hashval = ((unsigned char) *p) << 4; - while (*p && *p != '=') - hashval += (unsigned char) *p++; - return &vartab[hashval % VTABSIZE]; -} - - - -/* - * Returns true if the two strings specify the same varable. The first - * variable name is terminated by '='; the second may be terminated by - * either '=' or '\0'. - */ - -static int -varequal(const char *p, const char *q) -{ - while (*p == *q++) { - if (*p++ == '=') - return 1; - } - if (*p == '=' && *(q - 1) == '\0') - return 1; - return 0; -} - -static void -showvars(const char *myprefix, int mask, int xor) -{ - struct var **vpp; - struct var *vp; - const char *sep = myprefix == nullstr ? myprefix : spcstr; - - for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { - for (vp = *vpp ; vp ; vp = vp->next) { - if ((vp->flags & mask) ^ xor) { - char *p; - int len; - - p = strchr(vp->text, '=') + 1; - len = p - vp->text; - p = single_quote(p); - - printf("%s%s%.*s%s\n", myprefix, sep, len, - vp->text, p); - stunalloc(p); - } - } - } -} - -static struct var ** -findvar(struct var **vpp, const char *name) -{ - for (; *vpp; vpp = &(*vpp)->next) { - if (varequal((*vpp)->text, name)) { - break; - } - } - return vpp; -} - -/* - * Copyright (c) 1999 Herbert Xu <herbert@debian.org> - * This file contains code for the times builtin. - * $Id: ash.c,v 1.28 2001/10/19 00:22:22 andersen Exp $ - */ -static int timescmd (int argc, char **argv) -{ - struct tms buf; - long int clk_tck = sysconf(_SC_CLK_TCK); - - times(&buf); - printf("%dm%fs %dm%fs\n%dm%fs %dm%fs\n", - (int) (buf.tms_utime / clk_tck / 60), - ((double) buf.tms_utime) / clk_tck, - (int) (buf.tms_stime / clk_tck / 60), - ((double) buf.tms_stime) / clk_tck, - (int) (buf.tms_cutime / clk_tck / 60), - ((double) buf.tms_cutime) / clk_tck, - (int) (buf.tms_cstime / clk_tck / 60), - ((double) buf.tms_cstime) / clk_tck); - return 0; -} - -#ifdef ASH_MATH_SUPPORT -/* The let builtin. */ -int letcmd(int argc, char **argv) -{ - int errcode; - long result=0; - if (argc == 2) { - char *tmp, *expression, p[13]; - expression = strchr(argv[1], '='); - if (!expression) { - /* Cannot use 'error()' here, or the return code - * will be incorrect */ - out2fmt("sh: let: syntax error: \"%s\"\n", argv[1]); - return 0; - } - *expression = '\0'; - tmp = ++expression; - result = arith(tmp, &errcode); - if (errcode < 0) { - /* Cannot use 'error()' here, or the return code - * will be incorrect */ - out2fmt("sh: let: "); - if(errcode == -2) - out2fmt("divide by zero"); - else - out2fmt("syntax error: \"%s=%s\"\n", argv[1], expression); - return 0; - } - snprintf(p, 12, "%ld", result); - setvar(argv[1], savestr(p), 0); - } else if (argc >= 3) - synerror("invalid operand"); - return !result; -} -#endif - - - -/*- - * 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. <BSD Advertising Clause omitted per the July 22, 1999 licensing change - * ftp://ftp.cs.berkeley.edu/pub/4bsd/README.Impt.License.Change> - * - * 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. - */ |