blob: bcd12f106c90f85386c0e2d46b6b423d23ff5c68 [file] [log] [blame]
/* vi: set sw=4 ts=4: */
/*
* ash shell port for busybox
*
* Copyright (c) 1989, 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Kenneth Almquist.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* This version of ash is adapted from the source in Debian's ash 0.3.8-5
* package.
*
* Modified by Erik Andersen <andersee@debian.org> and
* Vladimir Oleynik <dzo@simtreas.ru> to be used in busybox
*
*
* Original copyright notice is retained at the end of this file.
*/
/* These defines allow you to adjust the feature set to be compiled
* into the ash shell. As a rule, enabling these options will make
* ash get bigger... With all of these options off, ash adds about
* 60k to busybox on an x86 system.*/
/* Enable job control. This allows you to run jobs in the background,
* which is great when ash is being used as an interactive shell, but
* it completely useless for is all you are doing is running scripts.
* This adds about 2.5k on an x86 system. */
#undef JOBS
/* This enables alias support in ash. If you want to support things
* like "alias ls='ls -l'" with ash, enable this. This is only useful
* when ash is used as an intractive shell. This adds about 1.5k */
#define ASH_ALIAS
/* If you need ash to act as a full Posix shell, with full math
* support, enable this. This adds a bit over 2k an x86 system. */
//#undef ASH_MATH_SUPPORT
#define ASH_MATH_SUPPORT
/* Getopts is used by shell procedures to parse positional parameters.
* You probably want to leave this disabled, and use the busybox getopt
* applet if you want to do this sort of thing. There are some scripts
* out there that use it, so it you need it, enable. Most people will
* leave this disabled. This adds 1k on an x86 system. */
#undef ASH_GETOPTS
/* This allows you to override shell builtins and use whatever is on
* the filesystem. This is most useful when ash is acting as a
* standalone shell. Adds about 272 bytes. */
#undef ASH_CMDCMD
/* Optimize size vs speed as size */
#define ASH_OPTIMIZE_FOR_SIZE
/* Enable this to compile in extra debugging noise. When debugging is
* on, debugging info will be written to $HOME/trace and a quit signal
* will generate a core dump. */
#undef DEBUG
/* These are here to work with glibc -- Don't change these... */
#undef FNMATCH_BROKEN
#undef GLOB_BROKEN
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <paths.h>
#include <pwd.h>
#include <setjmp.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <sys/times.h>
#include <sys/types.h>
#include <sys/wait.h>
#if !defined(FNMATCH_BROKEN)
#include <fnmatch.h>
#endif
#if !defined(GLOB_BROKEN)
#include <glob.h>
#endif
#ifdef JOBS
#include <termios.h>
#endif
#include "busybox.h"
#include "cmdedit.h"
/*
* This file was generated by the mksyntax program.
*/
/* Syntax classes */
#define CWORD 0 /* character is nothing special */
#define CNL 1 /* newline character */
#define CBACK 2 /* a backslash character */
#define CSQUOTE 3 /* single quote */
#define CDQUOTE 4 /* double quote */
#define CENDQUOTE 5 /* a terminating quote */
#define CBQUOTE 6 /* backwards single quote */
#define CVAR 7 /* a dollar sign */
#define CENDVAR 8 /* a '}' character */
#define CLP 9 /* a left paren in arithmetic */
#define CRP 10 /* a right paren in arithmetic */
#define CENDFILE 11 /* end of file */
#define CCTL 12 /* like CWORD, except it must be escaped */
#define CSPCL 13 /* these terminate a word */
#define CIGN 14 /* character should be ignored */
/* Syntax classes for is_ functions */
#define ISDIGIT 01 /* a digit */
#define ISUPPER 02 /* an upper case letter */
#define ISLOWER 04 /* a lower case letter */
#define ISUNDER 010 /* an underscore */
#define ISSPECL 020 /* the name of a special parameter */
#define SYNBASE 130
#define PEOF -130
#define PEOA -129
#define TEOF 0
#define TNL 1
#define TSEMI 2
#define TBACKGND 3
#define TAND 4
#define TOR 5
#define TPIPE 6
#define TLP 7
#define TRP 8
#define TENDCASE 9
#define TENDBQUOTE 10
#define TREDIR 11
#define TWORD 12
#define TASSIGN 13
#define TNOT 14
#define TCASE 15
#define TDO 16
#define TDONE 17
#define TELIF 18
#define TELSE 19
#define TESAC 20
#define TFI 21
#define TFOR 22
#define TIF 23
#define TIN 24
#define TTHEN 25
#define TUNTIL 26
#define TWHILE 27
#define TBEGIN 28
#define TEND 29
#define BASESYNTAX (basesyntax + SYNBASE)
#define DQSYNTAX (dqsyntax + SYNBASE)
#define SQSYNTAX (sqsyntax + SYNBASE)
#define ARISYNTAX (arisyntax + SYNBASE)
/* control characters in argument strings */
#define CTLESC '\201'
#define CTLVAR '\202'
#define CTLENDVAR '\203'
#define CTLBACKQ '\204'
#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
/* CTLBACKQ | CTLQUOTE == '\205' */
#define CTLARI '\206'
#define CTLENDARI '\207'
#define CTLQUOTEMARK '\210'
#define is_digit(c) ((c)>='0' && (c)<='9')
#define is_alpha(c) (((c) < CTLESC || (c) > CTLENDARI) && isalpha((unsigned char) (c)))
#define is_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalpha((unsigned char) (c))))
#define is_in_name(c) (((c) < CTLESC || (c) > CTLENDARI) && ((c) == '_' || isalnum((unsigned char) (c))))
#define is_special(c) ((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))
#define digit_val(c) ((c) - '0')
#define _DIAGASSERT(x)
#define S_DFL 1 /* default signal handling (SIG_DFL) */
#define S_CATCH 2 /* signal is caught */
#define S_IGN 3 /* signal is ignored (SIG_IGN) */
#define S_HARD_IGN 4 /* signal is ignored permenantly */
#define S_RESET 5 /* temporary - to reset a hard ignored sig */
/* variable substitution byte (follows CTLVAR) */
#define VSTYPE 0x0f /* type of variable substitution */
#define VSNUL 0x10 /* colon--treat the empty string as unset */
#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
/* values of VSTYPE field */
#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
#define VSMINUS 0x2 /* ${var-text} */
#define VSPLUS 0x3 /* ${var+text} */
#define VSQUESTION 0x4 /* ${var?message} */
#define VSASSIGN 0x5 /* ${var=text} */
#define VSTRIMLEFT 0x6 /* ${var#pattern} */
#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */
#define VSTRIMRIGHT 0x8 /* ${var%pattern} */
#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */
#define VSLENGTH 0xa /* ${#var} */
/* flags passed to redirect */
#define REDIR_PUSH 01 /* save previous values of file descriptors */
#define REDIR_BACKQ 02 /* save the command output to pipe */
/*
* BSD setjmp saves the signal mask, which violates ANSI C and takes time,
* so we use _setjmp instead.
*/
#if defined(BSD)
#define setjmp(jmploc) _setjmp(jmploc)
#define longjmp(jmploc, val) _longjmp(jmploc, val)
#endif
/*
* Most machines require the value returned from malloc to be aligned
* in some way. The following macro will get this right on many machines.
*/
#ifndef ALIGN
union align {
int i;
char *cp;
};
#define ALIGN(nbytes) (((nbytes) + sizeof(union align) - 1) & ~(sizeof(union align) - 1))
#endif
#ifdef BB_LOCALE_SUPPORT
#include <locale.h>
static void change_lc_all(const char *value);
static void change_lc_ctype(const char *value);
#endif
/*
* These macros allow the user to suspend the handling of interrupt signals
* over a period of time. This is similar to SIGHOLD to or sigblock, but
* much more efficient and portable. (But hacking the kernel is so much
* more fun than worrying about efficiency and portability. :-))
*/
static void onint (void);
static volatile int suppressint;
static volatile int intpending;
#define INTOFF suppressint++
#ifndef ASH_OPTIMIZE_FOR_SIZE
#define INTON { if (--suppressint == 0 && intpending) onint(); }
#define FORCEINTON {suppressint = 0; if (intpending) onint();}
#else
static void __inton (void);
static void forceinton (void);
#define INTON __inton()
#define FORCEINTON forceinton()
#endif
#define CLEAR_PENDING_INT intpending = 0
#define int_pending() intpending
typedef void *pointer;
#ifndef NULL
#define NULL (void *)0
#endif
static inline pointer ckmalloc (int sz) { return xmalloc(sz); }
static inline pointer ckrealloc(void *p, int sz) { return xrealloc(p, sz); }
static inline char * savestr (const char *s) { return xstrdup(s); }
static pointer stalloc (int);
static void stunalloc (pointer);
static void ungrabstackstr (char *, char *);
static char * growstackstr(void);
static char * makestrspace(size_t newlen);
static char *sstrdup (const char *);
/*
* Parse trees for commands are allocated in lifo order, so we use a stack
* to make this more efficient, and also to avoid all sorts of exception
* handling code to handle interrupts in the middle of a parse.
*
* The size 504 was chosen because the Ultrix malloc handles that size
* well.
*/
#define MINSIZE 504 /* minimum size of a block */
struct stack_block {
struct stack_block *prev;
char space[MINSIZE];
};
static struct stack_block stackbase;
static struct stack_block *stackp = &stackbase;
static struct stackmark *markp;
static char *stacknxt = stackbase.space;
static int stacknleft = MINSIZE;
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define stackblock() stacknxt
#define stackblocksize() stacknleft
#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize()
#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c)))
#define CHECKSTRSPACE(n, p) { if (sstrnleft < n) p = makestrspace(n); }
#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0'))
#define USTPUTC(c, p) (--sstrnleft, *p++ = (c))
#define STUNPUTC(p) (++sstrnleft, --p)
#define STTOPC(p) p[-1]
#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount))
#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft)
#define ckfree(p) free((pointer)(p))
#ifdef DEBUG
#define TRACE(param) trace param
static void trace (const char *, ...);
static void trargs (char **);
static void showtree (union node *);
static void trputc (int);
static void trputs (const char *);
static void opentrace (void);
#else
#define TRACE(param)
#endif
#define NSEMI 0
#define NCMD 1
#define NPIPE 2
#define NREDIR 3
#define NBACKGND 4
#define NSUBSHELL 5
#define NAND 6
#define NOR 7
#define NIF 8
#define NWHILE 9
#define NUNTIL 10
#define NFOR 11
#define NCASE 12
#define NCLIST 13
#define NDEFUN 14
#define NARG 15
#define NTO 16
#define NFROM 17
#define NFROMTO 18
#define NAPPEND 19
#define NTOOV 20
#define NTOFD 21
#define NFROMFD 22
#define NHERE 23
#define NXHERE 24
#define NNOT 25
/*
* expandarg() flags
*/
#define EXP_FULL 0x1 /* perform word splitting & file globbing */
#define EXP_TILDE 0x2 /* do normal tilde expansion */
#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
#define NOPTS 16
static char optet_vals[NOPTS];
static const char * const optlist[NOPTS] = {
"e" "errexit",
"f" "noglob",
"I" "ignoreeof",
"i" "interactive",
"m" "monitor",
"n" "noexec",
"s" "stdin",
"x" "xtrace",
"v" "verbose",
"V" "vi",
"E" "emacs",
"C" "noclobber",
"a" "allexport",
"b" "notify",
"u" "nounset",
"q" "quietprofile"
};
#define optent_name(optent) (optent+1)
#define optent_letter(optent) optent[0]
#define optent_val(optent) optet_vals[optent]
#define eflag optent_val(0)
#define fflag optent_val(1)
#define Iflag optent_val(2)
#define iflag optent_val(3)
#define mflag optent_val(4)
#define nflag optent_val(5)
#define sflag optent_val(6)
#define xflag optent_val(7)
#define vflag optent_val(8)
#define Vflag optent_val(9)
#define Eflag optent_val(10)
#define Cflag optent_val(11)
#define aflag optent_val(12)
#define bflag optent_val(13)
#define uflag optent_val(14)
#define qflag optent_val(15)
/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
#define FORK_FG 0
#define FORK_BG 1
#define FORK_NOJOB 2
struct nbinary {
int type;
union node *ch1;
union node *ch2;
};
struct ncmd {
int type;
int backgnd;
union node *assign;
union node *args;
union node *redirect;
};
struct npipe {
int type;
int backgnd;
struct nodelist *cmdlist;
};
struct nredir {
int type;
union node *n;
union node *redirect;
};
struct nif {
int type;
union node *test;
union node *ifpart;
union node *elsepart;
};
struct nfor {
int type;
union node *args;
union node *body;
char *var;
};
struct ncase {
int type;
union node *expr;
union node *cases;
};
struct nclist {
int type;
union node *next;
union node *pattern;
union node *body;
};
struct narg {
int type;
union node *next;
char *text;
struct nodelist *backquote;
};
struct nfile {
int type;
union node *next;
int fd;
union node *fname;
char *expfname;
};
struct ndup {
int type;
union node *next;
int fd;
int dupfd;
union node *vname;
};
struct nhere {
int type;
union node *next;
int fd;
union node *doc;
};
struct nnot {
int type;
union node *com;
};
union node {
int type;
struct nbinary nbinary;
struct ncmd ncmd;
struct npipe npipe;
struct nredir nredir;
struct nif nif;
struct nfor nfor;
struct ncase ncase;
struct nclist nclist;
struct narg narg;
struct nfile nfile;
struct ndup ndup;
struct nhere nhere;
struct nnot nnot;
};
struct nodelist {
struct nodelist *next;
union node *n;
};
struct backcmd { /* result of evalbackcmd */
int fd; /* file descriptor to read from */
char *buf; /* buffer */
int nleft; /* number of chars in buffer */
struct job *jp; /* job structure for command */
};
struct cmdentry {
int cmdtype;
union param {
int index;
union node *func;
const struct builtincmd *cmd;
} u;
};
struct strlist {
struct strlist *next;
char *text;
};
struct arglist {
struct strlist *list;
struct strlist **lastp;
};
struct strpush {
struct strpush *prev; /* preceding string on stack */
char *prevstring;
int prevnleft;
#ifdef ASH_ALIAS
struct alias *ap; /* if push was associated with an alias */
#endif
char *string; /* remember the string since it may change */
};
struct parsefile {
struct parsefile *prev; /* preceding file on stack */
int linno; /* current line */
int fd; /* file descriptor (or -1 if string) */
int nleft; /* number of chars left in this line */
int lleft; /* number of chars left in this buffer */
char *nextc; /* next char in buffer */
char *buf; /* input buffer */
struct strpush *strpush; /* for pushing strings at this level */
struct strpush basestrpush; /* so pushing one is fast */
};
struct stackmark {
struct stack_block *stackp;
char *stacknxt;
int stacknleft;
struct stackmark *marknext;
};
struct shparam {
int nparam; /* # of positional parameters (without $0) */
unsigned char malloc; /* if parameter list dynamically allocated */
char **p; /* parameter list */
int optind; /* next parameter to be processed by getopts */
int optoff; /* used by getopts */
};
/*
* When commands are first encountered, they are entered in a hash table.
* This ensures that a full path search will not have to be done for them
* on each invocation.
*
* We should investigate converting to a linear search, even though that
* would make the command name "hash" a misnomer.
*/
#define CMDTABLESIZE 31 /* should be prime */
#define ARB 1 /* actual size determined at run time */
struct tblentry {
struct tblentry *next; /* next entry in hash chain */
union param param; /* definition of builtin function */
short cmdtype; /* index identifying command */
char rehash; /* if set, cd done since entry created */
char cmdname[ARB]; /* name of command */
};
static struct tblentry *cmdtable[CMDTABLESIZE];
static int builtinloc = -1; /* index in path of %builtin, or -1 */
static int exerrno = 0; /* Last exec error */
static void tryexec (char *, char **, char **);
static void printentry (struct tblentry *, int);
static void clearcmdentry (int);
static struct tblentry *cmdlookup (const char *, int);
static void delete_cmd_entry (void);
static int path_change (const char *, int *);
static void flushall (void);
static void out2fmt (const char *, ...)
__attribute__((__format__(__printf__,1,2)));
static int xwrite (int, const char *, int);
static void outstr (const char *p, FILE *file) { fputs(p, file); }
static void out1str(const char *p) { outstr(p, stdout); }
static void out2str(const char *p) { outstr(p, stderr); }
#ifndef ASH_OPTIMIZE_FOR_SIZE
#define out2c(c) putc((c), stderr)
#else
static void out2c(int c) { putc(c, stderr); }
#endif
/* syntax table used when not in quotes */
static const char basesyntax[257] = {
CENDFILE, CSPCL, CWORD, CCTL,
CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CCTL, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CSPCL,
CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CSPCL, CWORD,
CDQUOTE, CWORD, CVAR, CWORD,
CSPCL, CSQUOTE, CSPCL, CSPCL,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CSPCL, CSPCL, CWORD,
CSPCL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CBACK, CWORD,
CWORD, CWORD, CBQUOTE, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CSPCL, CENDVAR,
CWORD
};
/* syntax table used when in double quotes */
static const char dqsyntax[257] = {
CENDFILE, CIGN, CWORD, CCTL,
CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CCTL, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CCTL,
CENDQUOTE,CWORD, CVAR, CWORD,
CWORD, CWORD, CWORD, CWORD,
CCTL, CWORD, CWORD, CCTL,
CWORD, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CCTL, CWORD, CWORD, CCTL,
CWORD, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CCTL, CBACK, CCTL,
CWORD, CWORD, CBQUOTE, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CENDVAR,
CCTL
};
/* syntax table used when in single quotes */
static const char sqsyntax[257] = {
CENDFILE, CIGN, CWORD, CCTL,
CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CCTL, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CCTL,
CWORD, CWORD, CWORD, CWORD,
CWORD, CENDQUOTE,CWORD, CWORD,
CCTL, CWORD, CWORD, CCTL,
CWORD, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CCTL, CWORD, CWORD, CCTL,
CWORD, CCTL, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CCTL, CCTL, CCTL,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CCTL
};
/* syntax table used when in arithmetic */
static const char arisyntax[257] = {
CENDFILE, CIGN, CWORD, CCTL,
CCTL, CCTL, CCTL, CCTL,
CCTL, CCTL, CCTL, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CNL, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CDQUOTE, CWORD, CVAR, CWORD,
CWORD, CSQUOTE, CLP, CRP,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CBACK, CWORD,
CWORD, CWORD, CBQUOTE, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CWORD,
CWORD, CWORD, CWORD, CENDVAR,
CWORD
};
/* character classification table */
static const char is_type[257] = {
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, ISSPECL,
0, ISSPECL, ISSPECL, 0,
0, 0, 0, 0,
ISSPECL, 0, 0, ISSPECL,
0, 0, ISDIGIT, ISDIGIT,
ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
ISDIGIT, ISDIGIT, ISDIGIT, ISDIGIT,
0, 0, 0, 0,
0, ISSPECL, ISSPECL, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, ISUPPER, ISUPPER, ISUPPER,
ISUPPER, 0, 0, 0,
0, ISUNDER, 0, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, ISLOWER, ISLOWER, ISLOWER,
ISLOWER, 0, 0, 0,
0
};
/* Array indicating which tokens mark the end of a list */
static const char tokendlist[] = {
1,
0,
0,
0,
0,
0,
0,
0,
1,
1,
1,
0,
0,
0,
0,
0,
1,
1,
1,
1,
1,
1,
0,
0,
0,
1,
0,
0,
0,
1,
};
static const char *const tokname[] = {
"end of file",
"newline",
"\";\"",
"\"&\"",
"\"&&\"",
"\"||\"",
"\"|\"",
"\"(\"",
"\")\"",
"\";;\"",
"\"`\"",
"redirection",
"word",
"assignment",
"\"!\"",
"\"case\"",
"\"do\"",
"\"done\"",
"\"elif\"",
"\"else\"",
"\"esac\"",
"\"fi\"",
"\"for\"",
"\"if\"",
"\"in\"",
"\"then\"",
"\"until\"",
"\"while\"",
"\"{\"",
"\"}\"",
};
#define KWDOFFSET 14
static const char *const parsekwd[] = {
"!",
"case",
"do",
"done",
"elif",
"else",
"esac",
"fi",
"for",
"if",
"in",
"then",
"until",
"while",
"{",
"}"
};
static int plinno = 1; /* input line number */
static int parselleft; /* copy of parsefile->lleft */
static struct parsefile basepf; /* top level input file */
static char basebuf[BUFSIZ]; /* buffer for top level input file */
static struct parsefile *parsefile = &basepf; /* current input file */
/*
* NEOF is returned by parsecmd when it encounters an end of file. It
* must be distinct from NULL, so we use the address of a variable that
* happens to be handy.
*/
static int tokpushback; /* last token pushed back */
#define NEOF ((union node *)&tokpushback)
static int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
static void error (const char *, ...) __attribute__((__noreturn__));
static void exerror (int, const char *, ...) __attribute__((__noreturn__));
static void shellexec (char **, char **, const char *, int)
__attribute__((noreturn));
static void exitshell (int) __attribute__((noreturn));
static int goodname(const char *);
static void ignoresig (int);
static void onsig (int);
static void dotrap (void);
static int decode_signal (const char *, int);
static void shprocvar(void);
static void deletefuncs(void);
static void setparam (char **);
static void freeparam (volatile struct shparam *);
/* reasons for skipping commands (see comment on breakcmd routine) */
#define SKIPBREAK 1
#define SKIPCONT 2
#define SKIPFUNC 3
#define SKIPFILE 4
/* values of cmdtype */
#define CMDUNKNOWN -1 /* no entry in table for command */
#define CMDNORMAL 0 /* command is an executable program */
#define CMDBUILTIN 1 /* command is a shell builtin */
#define CMDFUNCTION 2 /* command is a shell function */
#define DO_ERR 1 /* find_command prints errors */
#define DO_ABS 2 /* find_command checks absolute paths */
#define DO_NOFUN 4 /* find_command ignores functions */
#define DO_BRUTE 8 /* find_command ignores hash table */
/*
* Shell variables.
*/
/* flags */
#define VEXPORT 0x01 /* variable is exported */
#define VREADONLY 0x02 /* variable cannot be modified */
#define VSTRFIXED 0x04 /* variable struct is staticly allocated */
#define VTEXTFIXED 0x08 /* text is staticly allocated */
#define VSTACK 0x10 /* text is allocated on the stack */
#define VUNSET 0x20 /* the variable is not set */
#define VNOFUNC 0x40 /* don't call the callback function */
struct var {
struct var *next; /* next entry in hash list */
int flags; /* flags are defined above */
char *text; /* name=value */
void (*func) (const char *);
/* function to be called when */
/* the variable gets set/unset */
};
struct localvar {
struct localvar *next; /* next local variable in list */
struct var *vp; /* the variable that was made local */
int flags; /* saved flags */
char *text; /* saved text */
};
#if defined(__GLIBC__) && __GLIBC__ >= 2 && !defined(FNMATCH_BROKEN)
#define rmescapes(p) _rmescapes((p), 0)
static char *_rmescapes (char *, int);
#else
static void rmescapes (char *);
#endif
static int casematch (union node *, const char *);
static void clearredir(void);
static void popstring(void);
static void readcmdfile (const char *);
static int number (const char *);
static int is_number (const char *, int *num);
static char *single_quote (const char *);
static int nextopt (const char *);
static void redirect (union node *, int);
static void popredir (void);
static int dup_as_newfd (int, int);
static void changepath(const char *newval);
static void getoptsreset(const char *value);
static int parsenleft; /* copy of parsefile->nleft */
static char *parsenextc; /* copy of parsefile->nextc */
static int rootpid; /* pid of main shell */
static int rootshell; /* true if we aren't a child of the main shell */
static const char spcstr[] = " ";
static const char snlfmt[] = "%s\n";
static int sstrnleft;
static int herefd = -1;
static struct localvar *localvars;
static struct var vifs;
static struct var vmail;
static struct var vmpath;
static struct var vpath;
static struct var vps1;
static struct var vps2;
static struct var voptind;
#ifdef BB_LOCALE_SUPPORT
static struct var vlc_all;
static struct var vlc_ctype;
#endif
struct varinit {
struct var *var;
int flags;
const char *text;
void (*func) (const char *);
};
static const char defpathvar[] =
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
#define defpath (defpathvar + 5)
#ifdef IFS_BROKEN
static const char defifsvar[] = "IFS= \t\n";
#define defifs (defifsvar + 4)
#else
static const char defifs[] = " \t\n";
#endif
static const struct varinit varinit[] = {
#ifdef IFS_BROKEN
{ &vifs, VSTRFIXED|VTEXTFIXED, defifsvar,
#else
{ &vifs, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS=",
#endif
NULL },
{ &vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL=",
NULL },
{ &vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH=",
NULL },
{ &vpath, VSTRFIXED|VTEXTFIXED, defpathvar,
changepath },
/*
* vps1 depends on uid
*/
{ &vps2, VSTRFIXED|VTEXTFIXED, "PS2=> ",
NULL },
{ &voptind, VSTRFIXED|VTEXTFIXED, "OPTIND=1",
getoptsreset },
#ifdef BB_LOCALE_SUPPORT
{ &vlc_all, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL=",
change_lc_all },
{ &vlc_ctype, VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE=",
change_lc_ctype },
#endif
{ NULL, 0, NULL,
NULL }
};
#define VTABSIZE 39
static struct var *vartab[VTABSIZE];
/*
* The following macros access the values of the above variables.
* They have to skip over the name. They return the null string
* for unset variables.
*/
#define ifsval() (vifs.text + 4)
#define ifsset() ((vifs.flags & VUNSET) == 0)
#define mailval() (vmail.text + 5)
#define mpathval() (vmpath.text + 9)
#define pathval() (vpath.text + 5)
#define ps1val() (vps1.text + 4)
#define ps2val() (vps2.text + 4)
#define optindval() (voptind.text + 7)
#define mpathset() ((vmpath.flags & VUNSET) == 0)
static void initvar (void);
static void setvar (const char *, const char *, int);
static void setvareq (char *, int);
static void listsetvar (struct strlist *);
static const char *lookupvar (const char *);
static const char *bltinlookup (const char *);
static char **environment (void);
static int showvarscmd (int, char **);
static void mklocal (char *);
static void poplocalvars (void);
static int unsetvar (const char *);
static int varequal (const char *, const char *);
static char *arg0; /* value of $0 */
static struct shparam shellparam; /* current positional parameters */
static char **argptr; /* argument list for builtin commands */
static char *optionarg; /* set by nextopt (like getopt) */
static char *optptr; /* used by nextopt */
static char *minusc; /* argument to -c option */
#ifdef ASH_ALIAS
#define ALIASINUSE 1
#define ALIASDEAD 2
#define ATABSIZE 39
struct alias {
struct alias *next;
char *name;
char *val;
int flag;
};
static struct alias *atab[ATABSIZE];
static void setalias (char *, char *);
static struct alias **hashalias (const char *);
static struct alias *freealias (struct alias *);
static struct alias **__lookupalias (const char *);
static void
setalias(name, val)
char *name, *val;
{
struct alias *ap, **app;
app = __lookupalias(name);
ap = *app;
INTOFF;
if (ap) {
if (!(ap->flag & ALIASINUSE)) {
ckfree(ap->val);
}
ap->val = savestr(val);
ap->flag &= ~ALIASDEAD;
} else {
/* not found */
ap = ckmalloc(sizeof (struct alias));
ap->name = savestr(name);
ap->val = savestr(val);
ap->flag = 0;
ap->next = 0;
*app = ap;
}
INTON;
}
static int
unalias(char *name)
{
struct alias **app;
app = __lookupalias(name);
if (*app) {
INTOFF;
*app = freealias(*app);
INTON;
return (0);
}
return (1);
}
static void
rmaliases(void)
{
struct alias *ap, **app;
int i;
INTOFF;
for (i = 0; i < ATABSIZE; i++) {
app = &atab[i];
for (ap = *app; ap; ap = *app) {
*app = freealias(*app);
if (ap == *app) {
app = &ap->next;
}
}
}
INTON;
}
static struct alias *
lookupalias(const char *name, int check)
{
struct alias *ap = *__lookupalias(name);
if (check && ap && (ap->flag & ALIASINUSE))
return (NULL);
return (ap);
}
static void
printalias(const struct alias *ap) {
char *p;
p = single_quote(ap->val);
printf("alias %s=%s\n", ap->name, p);
stunalloc(p);
}
/*
* TODO - sort output
*/
static int
aliascmd(int argc, char **argv)
{
char *n, *v;
int ret = 0;
struct alias *ap;
if (argc == 1) {
int i;
for (i = 0; i < ATABSIZE; i++)
for (ap = atab[i]; ap; ap = ap->next) {
printalias(ap);
}
return (0);
}
while ((n = *++argv) != NULL) {
if ((v = strchr(n+1, '=')) == NULL) { /* n+1: funny ksh stuff */
if ((ap = *__lookupalias(n)) == NULL) {
out2fmt("%s: %s not found\n", "alias", n);
ret = 1;
} else
printalias(ap);
}
else {
*v++ = '\0';
setalias(n, v);
}
}
return (ret);
}
static int
unaliascmd(int argc, char **argv)
{
int i;
while ((i = nextopt("a")) != '\0') {
if (i == 'a') {
rmaliases();
return (0);
}
}
for (i = 0; *argptr; argptr++) {
if (unalias(*argptr)) {
out2fmt("%s: %s not found\n", "unalias", *argptr);
i = 1;
}
}
return (i);
}
static struct alias **
hashalias(p)
const char *p;
{
unsigned int hashval;
hashval = *p << 4;
while (*p)
hashval+= *p++;
return &atab[hashval % ATABSIZE];
}
static struct alias *
freealias(struct alias *ap) {
struct alias *next;
if (ap->flag & ALIASINUSE) {
ap->flag |= ALIASDEAD;
return ap;
}
next = ap->next;
ckfree(ap->name);
ckfree(ap->val);
ckfree(ap);
return next;
}
static struct alias **
__lookupalias(const char *name) {
struct alias **app = hashalias(name);
for (; *app; app = &(*app)->next) {
if (equal(name, (*app)->name)) {
break;
}
}
return app;
}
#endif
#ifdef ASH_MATH_SUPPORT
/* The generated file arith.c has been replaced with a custom hand
* written implementation written by Aaron Lehmann <aaronl@vitelus.com>.
* This is now part of libbb, so that it can be used by all the shells
* in busybox. */
#define ARITH_NUM 257
#define ARITH_LPAREN 258
#define ARITH_RPAREN 259
#define ARITH_OR 260
#define ARITH_AND 261
#define ARITH_BOR 262
#define ARITH_BXOR 263
#define ARITH_BAND 264
#define ARITH_EQ 265
#define ARITH_NE 266
#define ARITH_LT 267
#define ARITH_GT 268
#define ARITH_GE 269
#define ARITH_LE 270
#define ARITH_LSHIFT 271
#define ARITH_RSHIFT 272
#define ARITH_ADD 273
#define ARITH_SUB 274
#define ARITH_MUL 275
#define ARITH_DIV 276
#define ARITH_REM 277
#define ARITH_UNARYMINUS 278
#define ARITH_UNARYPLUS 279
#define ARITH_NOT 280
#define ARITH_BNOT 281
static void expari (int);
#endif
static char *trap[NSIG]; /* trap handler commands */
static char sigmode[NSIG - 1]; /* current value of signal */
static char gotsig[NSIG - 1]; /* indicates specified signal received */
static int pendingsigs; /* indicates some signal received */
/*
* This file was generated by the mkbuiltins program.
*/
#ifdef JOBS
static int bgcmd (int, char **);
static int fgcmd (int, char **);
static int killcmd (int, char **);
#endif
static int bltincmd (int, char **);
static int cdcmd (int, char **);
static int breakcmd (int, char **);
#ifdef ASH_CMDCMD
static int commandcmd (int, char **);
#endif
static int dotcmd (int, char **);
static int evalcmd (int, char **);
static int execcmd (int, char **);
static int exitcmd (int, char **);
static int exportcmd (int, char **);
static int histcmd (int, char **);
static int hashcmd (int, char **);
static int helpcmd (int, char **);
static int jobscmd (int, char **);
static int localcmd (int, char **);
#ifndef BB_PWD
static int pwdcmd (int, char **);
#endif
static int readcmd (int, char **);
static int returncmd (int, char **);
static int setcmd (int, char **);
static int setvarcmd (int, char **);
static int shiftcmd (int, char **);
static int trapcmd (int, char **);
static int umaskcmd (int, char **);
#ifdef ASH_ALIAS
static int aliascmd (int, char **);
static int unaliascmd (int, char **);
#endif
static int unsetcmd (int, char **);
static int waitcmd (int, char **);
static int ulimitcmd (int, char **);
static int timescmd (int, char **);
#ifdef ASH_MATH_SUPPORT
static int letcmd (int, char **);
#endif
static int typecmd (int, char **);
#ifdef ASH_GETOPTS
static int getoptscmd (int, char **);
#endif
#ifndef BB_TRUE_FALSE
static int true_main (int, char **);
static int false_main (int, char **);
#endif
static void setpwd (const char *, int);
#define BUILTIN_NOSPEC "0"
#define BUILTIN_SPECIAL "1"
#define BUILTIN_REGULAR "2"
#define BUILTIN_ASSIGN "4"
#define BUILTIN_SPEC_ASSG "5"
#define BUILTIN_REG_ASSG "6"
#define IS_BUILTIN_SPECIAL(builtincmd) ((builtincmd)->name[0] & 1)
#define IS_BUILTIN_REGULAR(builtincmd) ((builtincmd)->name[0] & 2)
#define IS_BUILTIN_ASSIGN(builtincmd) ((builtincmd)->name[0] & 4)
struct builtincmd {
const char *name;
int (*const builtinfunc) (int, char **);
//unsigned flags;
};
/* It is CRUCIAL that this listing be kept in ascii order, otherwise
* the binary search in find_builtin() will stop working. If you value
* your kneecaps, you'll be sure to *make sure* that any changes made
* to this array result in the listing remaining in ascii order. You
* have been warned.
*/
static const struct builtincmd builtincmds[] = {
{ BUILTIN_SPECIAL ".", dotcmd }, /* first, see declare DOTCMD */
{ BUILTIN_SPECIAL ":", true_main },
#ifdef ASH_ALIAS
{ BUILTIN_REG_ASSG "alias", aliascmd },
#endif
#ifdef JOBS
{ BUILTIN_REGULAR "bg", bgcmd },
#endif
{ BUILTIN_SPECIAL "break", breakcmd },
{ BUILTIN_SPECIAL "builtin", bltincmd },
{ BUILTIN_REGULAR "cd", cdcmd },
{ BUILTIN_NOSPEC "chdir", cdcmd },
#ifdef ASH_CMDCMD
{ BUILTIN_REGULAR "command", commandcmd },
#endif
{ BUILTIN_SPECIAL "continue", breakcmd },
{ BUILTIN_SPECIAL "eval", evalcmd },
{ BUILTIN_SPECIAL "exec", execcmd },
{ BUILTIN_SPECIAL "exit", exitcmd },
{ BUILTIN_SPEC_ASSG "export", exportcmd },
{ BUILTIN_REGULAR "false", false_main },
{ BUILTIN_REGULAR "fc", histcmd },
#ifdef JOBS
{ BUILTIN_REGULAR "fg", fgcmd },
#endif
#ifdef ASH_GETOPTS
{ BUILTIN_REGULAR "getopts", getoptscmd },
#endif
{ BUILTIN_NOSPEC "hash", hashcmd },
{ BUILTIN_NOSPEC "help", helpcmd },
{ BUILTIN_REGULAR "jobs", jobscmd },
#ifdef JOBS
{ BUILTIN_REGULAR "kill", killcmd },
#endif
#ifdef ASH_MATH_SUPPORT
{ BUILTIN_REGULAR "let", letcmd },
#endif
{ BUILTIN_ASSIGN "local", localcmd },
#ifndef BB_PWD
{ BUILTIN_NOSPEC "pwd", pwdcmd },
#endif
{ BUILTIN_REGULAR "read", readcmd },
{ BUILTIN_SPEC_ASSG "readonly", exportcmd },
{ BUILTIN_SPECIAL "return", returncmd },
{ BUILTIN_SPECIAL "set", setcmd },
{ BUILTIN_NOSPEC "setvar", setvarcmd },
{ BUILTIN_SPECIAL "shift", shiftcmd },
{ BUILTIN_SPECIAL "times", timescmd },
{ BUILTIN_SPECIAL "trap", trapcmd },
{ BUILTIN_REGULAR "true", true_main },
{ BUILTIN_NOSPEC "type", typecmd },
{ BUILTIN_NOSPEC "ulimit", ulimitcmd },
{ BUILTIN_REGULAR "umask", umaskcmd },
#ifdef ASH_ALIAS
{ BUILTIN_REGULAR "unalias", unaliascmd },
#endif
{ BUILTIN_SPECIAL "unset", unsetcmd },
{ BUILTIN_REGULAR "wait", waitcmd },
};
#define NUMBUILTINS (sizeof (builtincmds) / sizeof (struct builtincmd) )
static const struct builtincmd *DOTCMD = &builtincmds[0];
static struct builtincmd *BLTINCMD;
static struct builtincmd *EXECCMD;
static struct builtincmd *EVALCMD;
/* states */
#define JOBSTOPPED 1 /* all procs are stopped */
#define JOBDONE 2 /* all procs are completed */
/*
* A job structure contains information about a job. A job is either a
* single process or a set of processes contained in a pipeline. In the
* latter case, pidlist will be non-NULL, and will point to a -1 terminated
* array of pids.
*/
struct procstat {
pid_t pid; /* process id */
int status; /* status flags (defined above) */
char *cmd; /* text of command being run */
};
static int job_warning; /* user was warned about stopped jobs */
#ifdef JOBS
static void setjobctl(int enable);
#else
#define setjobctl(on) /* do nothing */
#endif
struct job {
struct procstat ps0; /* status of process */
struct procstat *ps; /* status or processes when more than one */
short nprocs; /* number of processes */
short pgrp; /* process group of this job */
char state; /* true if job is finished */
char used; /* true if this entry is in used */
char changed; /* true if status has changed */
#ifdef JOBS
char jobctl; /* job running under job control */
#endif
};
static struct job *jobtab; /* array of jobs */
static int njobs; /* size of array */
static int backgndpid = -1; /* pid of last background process */
#ifdef JOBS
static int initialpgrp; /* pgrp of shell on invocation */
static int curjob; /* current job */
static int jobctl;
#endif
static int intreceived;
static struct job *makejob (const union node *, int);
static int forkshell (struct job *, const union node *, int);
static int waitforjob (struct job *);
static int docd (char *, int);
static char *getcomponent (void);
static void updatepwd (const char *);
static void getpwd (void);
static char *padvance (const char **, const char *);
static char nullstr[1]; /* zero length string */
static char *curdir = nullstr; /* current working directory */
static char *cdcomppath;
static int
cdcmd(argc, argv)
int argc;
char **argv;
{
const char *dest;
const char *path;
char *p;
struct stat statb;
int print = 0;
nextopt(nullstr);
if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME")) == NULL)
error("HOME not set");
if (*dest == '\0')
dest = ".";
if (dest[0] == '-' && dest[1] == '\0') {
dest = bltinlookup("OLDPWD");
if (!dest || !*dest) {
dest = curdir;
}
print = 1;
if (dest)
print = 1;
else
dest = ".";
}
if (*dest == '/' || (path = bltinlookup("CDPATH")) == NULL)
path = nullstr;
while ((p = padvance(&path, dest)) != NULL) {
if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
if (!print) {
/*
* XXX - rethink
*/
if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
p += 2;
print = strcmp(p, dest);
}
if (docd(p, print) >= 0)
return 0;
}
}
error("can't cd to %s", dest);
/* NOTREACHED */
}
/*
* Actually do the chdir. In an interactive shell, print the
* directory name if "print" is nonzero.
*/
static int
docd(dest, print)
char *dest;
int print;
{
char *p;
char *q;
char *component;
struct stat statb;
int first;
int badstat;
TRACE(("docd(\"%s\", %d) called\n", dest, print));
/*
* Check each component of the path. If we find a symlink or
* something we can't stat, clear curdir to force a getcwd()
* next time we get the value of the current directory.
*/
badstat = 0;
cdcomppath = sstrdup(dest);
STARTSTACKSTR(p);
if (*dest == '/') {
STPUTC('/', p);
cdcomppath++;
}
first = 1;
while ((q = getcomponent()) != NULL) {
if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
continue;
if (! first)
STPUTC('/', p);
first = 0;
component = q;
while (*q)
STPUTC(*q++, p);
if (equal(component, ".."))
continue;
STACKSTRNUL(p);
if ((lstat(stackblock(), &statb) < 0)
|| (S_ISLNK(statb.st_mode))) {
/* print = 1; */
badstat = 1;
break;
}
}
INTOFF;
if (chdir(dest) < 0) {
INTON;
return -1;
}
updatepwd(badstat ? NULL : dest);
INTON;
if (print && iflag)
printf(snlfmt, curdir);
return 0;
}
/*
* Get the next component of the path name pointed to by cdcomppath.
* This routine overwrites the string pointed to by cdcomppath.
*/
static char *
getcomponent() {
char *p;
char *start;
if ((p = cdcomppath) == NULL)
return NULL;
start = cdcomppath;
while (*p != '/' && *p != '\0')
p++;
if (*p == '\0') {
cdcomppath = NULL;
} else {
*p++ = '\0';
cdcomppath = p;
}
return start;
}
/*
* Update curdir (the name of the current directory) in response to a
* cd command. We also call hashcd to let the routines in exec.c know
* that the current directory has changed.
*/
static void hashcd (void);
static void
updatepwd(const char *dir)
{
char *new;
char *p;
size_t len;
hashcd(); /* update command hash table */
/*
* If our argument is NULL, we don't know the current directory
* any more because we traversed a symbolic link or something
* we couldn't stat().
*/
if (dir == NULL || curdir == nullstr) {
setpwd(0, 1);
return;
}
len = strlen(dir);
cdcomppath = sstrdup(dir);
STARTSTACKSTR(new);
if (*dir != '/') {
p = curdir;
while (*p)
STPUTC(*p++, new);
if (p[-1] == '/')
STUNPUTC(new);
}
while ((p = getcomponent()) != NULL) {
if (equal(p, "..")) {
while (new > stackblock() && (STUNPUTC(new), *new) != '/');
} else if (*p != '\0' && ! equal(p, ".")) {
STPUTC('/', new);
while (*p)
STPUTC(*p++, new);
}
}
if (new == stackblock())
STPUTC('/', new);
STACKSTRNUL(new);
setpwd(stackblock(), 1);
}
#ifndef BB_PWD
static int
pwdcmd(argc, argv)
int argc;
char **argv;
{
printf(snlfmt, curdir);
return 0;
}
#endif
/*
* Find out what the current directory is. If we already know the current
* directory, this routine returns immediately.
*/
static void
getpwd(void)
{
curdir = xgetcwd(0);
if(curdir==0)
curdir = nullstr;
}
static void
setpwd(const char *val, int setold)
{
if (setold) {
setvar("OLDPWD", curdir, VEXPORT);
}
INTOFF;
if (curdir != nullstr) {
free(curdir);
curdir = nullstr;
}
if (!val) {
getpwd();
} else {
curdir = savestr(val);
}
INTON;
setvar("PWD", curdir, VEXPORT);
}
/*
* Errors and exceptions.
*/
/*
* Code to handle exceptions in C.
*/
/*
* We enclose jmp_buf in a structure so that we can declare pointers to
* jump locations. The global variable handler contains the location to
* jump to when an exception occurs, and the global variable exception
* contains a code identifying the exeception. To implement nested
* exception handlers, the user should save the value of handler on entry
* to an inner scope, set handler to point to a jmploc structure for the
* inner scope, and restore handler on exit from the scope.
*/
struct jmploc {
jmp_buf loc;
};
/* exceptions */
#define EXINT 0 /* SIGINT received */
#define EXERROR 1 /* a generic error */
#define EXSHELLPROC 2 /* execute a shell procedure */
#define EXEXEC 3 /* command execution failed */
static struct jmploc *handler;
static int exception;
static void exverror (int, const char *, va_list)
__attribute__((__noreturn__));
/*
* Called to raise an exception. Since C doesn't include exceptions, we
* just do a longjmp to the exception handler. The type of exception is
* stored in the global variable "exception".
*/
static void exraise (int) __attribute__((__noreturn__));
static void
exraise(int e)
{
#ifdef DEBUG
if (handler == NULL)
abort();
#endif
flushall();
exception = e;
longjmp(handler->loc, 1);
}
/*
* Called from trap.c when a SIGINT is received. (If the user specifies
* that SIGINT is to be trapped or ignored using the trap builtin, then
* this routine is not called.) Suppressint is nonzero when interrupts
* are held using the INTOFF macro. The call to _exit is necessary because
* there is a short period after a fork before the signal handlers are
* set to the appropriate value for the child. (The test for iflag is
* just defensive programming.)
*/
static void
onint(void) {
sigset_t mysigset;
if (suppressint) {
intpending++;
return;
}
intpending = 0;
sigemptyset(&mysigset);
sigprocmask(SIG_SETMASK, &mysigset, NULL);
if (rootshell && iflag)
exraise(EXINT);
else {
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
/* NOTREACHED */
}
static char *commandname; /* currently executing command */
/*
* Exverror is called to raise the error exception. If the first argument
* is not NULL then error prints an error message using printf style
* formatting. It then raises the error exception.
*/
static void
exverror(int cond, const char *msg, va_list ap)
{
CLEAR_PENDING_INT;
INTOFF;
#ifdef DEBUG
if (msg)
TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid()));
else
TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid()));
#endif
if (msg) {
if (commandname)
out2fmt("%s: ", commandname);
vfprintf(stderr, msg, ap);
out2c('\n');
}
exraise(cond);
/* NOTREACHED */
}
static void
error(const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
exverror(EXERROR, msg, ap);
/* NOTREACHED */
va_end(ap);
}
static void
exerror(int cond, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
exverror(cond, msg, ap);
/* NOTREACHED */
va_end(ap);
}
/*
* Table of error messages.
*/
struct errname {
short errcode; /* error number */
char action; /* operation which encountered the error */
};
/*
* Types of operations (passed to the errmsg routine).
*/
#define E_OPEN 01 /* opening a file */
#define E_CREAT 02 /* creating a file */
#define E_EXEC 04 /* executing a program */
#define ALL (E_OPEN|E_CREAT|E_EXEC)
static const struct errname errormsg[] = {
{ EINTR, ALL },
{ EACCES, ALL },
{ EIO, ALL },
{ ENOENT, E_OPEN },
{ ENOENT, E_CREAT },
{ ENOENT, E_EXEC },
{ ENOTDIR, E_OPEN },
{ ENOTDIR, E_CREAT },
{ ENOTDIR, E_EXEC },
{ EISDIR, ALL },
{ EEXIST, E_CREAT },
#ifdef EMFILE
{ EMFILE, ALL },
#endif
{ ENFILE, ALL },
{ ENOSPC, ALL },
#ifdef EDQUOT
{ EDQUOT, ALL },
#endif
#ifdef ENOSR
{ ENOSR, ALL },
#endif
{ ENXIO, ALL },
{ EROFS, ALL },
{ ETXTBSY, ALL },
#ifdef EAGAIN
{ EAGAIN, E_EXEC },
#endif
{ ENOMEM, ALL },
#ifdef ENOLINK
{ ENOLINK, ALL },
#endif
#ifdef EMULTIHOP
{ EMULTIHOP, ALL },
#endif
#ifdef ECOMM
{ ECOMM, ALL },
#endif
#ifdef ESTALE
{ ESTALE, ALL },
#endif
#ifdef ETIMEDOUT
{ ETIMEDOUT, ALL },
#endif
#ifdef ELOOP
{ ELOOP, ALL },
#endif
{ E2BIG, E_EXEC },
#ifdef ELIBACC
{ ELIBACC, E_EXEC },
#endif
};
#define ERRNAME_SIZE (sizeof(errormsg)/sizeof(struct errname))
/*
* Return a string describing an error. The returned string may be a
* pointer to a static buffer that will be overwritten on the next call.
* Action describes the operation that got the error.
*/
static const char *
errmsg(int e, int action)
{
struct errname const *ep;
static char buf[12];
for (ep = errormsg ; ep < errormsg+ERRNAME_SIZE; ep++) {
if (ep->errcode == e && (ep->action & action) != 0)
return strerror(e);
}
snprintf(buf, sizeof buf, "error %d", e);
return buf;
}
#ifdef ASH_OPTIMIZE_FOR_SIZE
static void
__inton() {
if (--suppressint == 0 && intpending) {
onint();
}
}
static void forceinton (void) {
suppressint = 0;
if (intpending)
onint();
}
#endif
/* flags in argument to evaltree */
#define EV_EXIT 01 /* exit after evaluating tree */
#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
#define EV_BACKCMD 04 /* command executing within back quotes */
static int evalskip; /* set if we are skipping commands */
static int skipcount; /* number of levels to skip */
static int loopnest; /* current loop nesting level */
static int funcnest; /* depth of function calls */
static struct strlist *cmdenviron; /* environment for builtin command */
static int exitstatus; /* exit status of last command */
static int oexitstatus; /* saved exit status */
static void evalsubshell (const union node *, int);
static void expredir (union node *);
static void prehash (union node *);
static void eprintlist (struct strlist *);
static union node *parsecmd(int);
/*
* Called to reset things after an exception.
*/
/*
* The eval commmand.
*/
static void evalstring (char *, int);
static int
evalcmd(argc, argv)
int argc;
char **argv;
{
char *p;
char *concat;
char **ap;
if (argc > 1) {
p = argv[1];
if (argc > 2) {
STARTSTACKSTR(concat);
ap = argv + 2;
for (;;) {
while (*p)
STPUTC(*p++, concat);
if ((p = *ap++) == NULL)
break;
STPUTC(' ', concat);
}
STPUTC('\0', concat);
p = grabstackstr(concat);
}
evalstring(p, EV_TESTED);
}
return exitstatus;
}
/*
* Execute a command or commands contained in a string.
*/
static void evaltree (union node *, int);
static void setinputstring (char *);
static void popfile (void);
static void setstackmark(struct stackmark *mark);
static void popstackmark(struct stackmark *mark);
static void
evalstring(char *s, int flag)
{
union node *n;
struct stackmark smark;
setstackmark(&smark);
setinputstring(s);
while ((n = parsecmd(0)) != NEOF) {
evaltree(n, flag);
popstackmark(&smark);
}
popfile();
popstackmark(&smark);
}
static struct builtincmd *find_builtin (const char *);
static void expandarg (union node *, struct arglist *, int);
static void calcsize (const union node *);
static union node *copynode (const union node *);
/*
* Make a copy of a parse tree.
*/
static int funcblocksize; /* size of structures in function */
static int funcstringsize; /* size of strings in node */
static pointer funcblock; /* block to allocate function from */
static char *funcstring; /* block to allocate strings from */
static inline union node *
copyfunc(union node *n)
{
if (n == NULL)
return NULL;
funcblocksize = 0;
funcstringsize = 0;
calcsize(n);
funcblock = ckmalloc(funcblocksize + funcstringsize);
funcstring = (char *) funcblock + funcblocksize;
return copynode(n);
}
/*
* Free a parse tree.
*/
static void
freefunc(union node *n)
{
if (n)
ckfree(n);
}
/*
* Add a new command entry, replacing any existing command entry for
* the same name.
*/
static inline void
addcmdentry(char *name, struct cmdentry *entry)
{
struct tblentry *cmdp;
INTOFF;
cmdp = cmdlookup(name, 1);
if (cmdp->cmdtype == CMDFUNCTION) {
freefunc(cmdp->param.func);
}
cmdp->cmdtype = entry->cmdtype;
cmdp->param = entry->u;
INTON;
}
static inline void
evalloop(const union node *n, int flags)
{
int status;
loopnest++;
status = 0;
for (;;) {
evaltree(n->nbinary.ch1, EV_TESTED);
if (evalskip) {
skipping: if (evalskip == SKIPCONT && --skipcount <= 0) {
evalskip = 0;
continue;
}
if (evalskip == SKIPBREAK && --skipcount <= 0)
evalskip = 0;
break;
}
if (n->type == NWHILE) {
if (exitstatus != 0)
break;
} else {
if (exitstatus == 0)
break;
}
evaltree(n->nbinary.ch2, flags & EV_TESTED);
status = exitstatus;
if (evalskip)
goto skipping;
}
loopnest--;
exitstatus = status;
}
static void
evalfor(const union node *n, int flags)
{
struct arglist arglist;
union node *argp;
struct strlist *sp;
struct stackmark smark;
setstackmark(&smark);
arglist.lastp = &arglist.list;
for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
oexitstatus = exitstatus;
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE | EXP_RECORD);
if (evalskip)
goto out;
}
*arglist.lastp = NULL;
exitstatus = 0;
loopnest++;
for (sp = arglist.list ; sp ; sp = sp->next) {
setvar(n->nfor.var, sp->text, 0);
evaltree(n->nfor.body, flags & EV_TESTED);
if (evalskip) {
if (evalskip == SKIPCONT && --skipcount <= 0) {
evalskip = 0;
continue;
}
if (evalskip == SKIPBREAK && --skipcount <= 0)
evalskip = 0;
break;
}
}
loopnest--;
out:
popstackmark(&smark);
}
static inline void
evalcase(const union node *n, int flags)
{
union node *cp;
union node *patp;
struct arglist arglist;
struct stackmark smark;
setstackmark(&smark);
arglist.lastp = &arglist.list;
oexitstatus = exitstatus;
expandarg(n->ncase.expr, &arglist, EXP_TILDE);
for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
if (casematch(patp, arglist.list->text)) {
if (evalskip == 0) {
evaltree(cp->nclist.body, flags);
}
goto out;
}
}
}
out:
popstackmark(&smark);
}
/*
* Evaluate a pipeline. All the processes in the pipeline are children
* of the process creating the pipeline. (This differs from some versions
* of the shell, which make the last process in a pipeline the parent
* of all the rest.)
*/
static inline void
evalpipe(n)
union node *n;
{
struct job *jp;
struct nodelist *lp;
int pipelen;
int prevfd;
int pip[2];
TRACE(("evalpipe(0x%lx) called\n", (long)n));
pipelen = 0;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
pipelen++;
INTOFF;
jp = makejob(n, pipelen);
prevfd = -1;
for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
prehash(lp->n);
pip[1] = -1;
if (lp->next) {
if (pipe(pip) < 0) {
close(prevfd);
error("Pipe call failed");
}
}
if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) {
INTON;
if (prevfd > 0) {
close(0);
dup_as_newfd(prevfd, 0);
close(prevfd);
if (pip[0] == 0) {
pip[0] = -1;
}
}
if (pip[1] >= 0) {
if (pip[0] >= 0) {
close(pip[0]);
}
if (pip[1] != 1) {
close(1);
dup_as_newfd(pip[1], 1);
close(pip[1]);
}
}
evaltree(lp->n, EV_EXIT);
}
if (prevfd >= 0)
close(prevfd);
prevfd = pip[0];
close(pip[1]);
}
INTON;
if (n->npipe.backgnd == 0) {
INTOFF;
exitstatus = waitforjob(jp);
TRACE(("evalpipe: job done exit status %d\n", exitstatus));
INTON;
}
}
static void find_command (const char *, struct cmdentry *, int, const char *);
static int
isassignment(const char *word) {
if (!is_name(*word)) {
return 0;
}
do {
word++;
} while (is_in_name(*word));
return *word == '=';
}
static void
evalcommand(union node *cmd, int flags)
{
struct stackmark smark;
union node *argp;
struct arglist arglist;
struct arglist varlist;
char **argv;
int argc;
char **envp;
struct strlist *sp;
int mode;
struct cmdentry cmdentry;
struct job *jp;
char *volatile savecmdname;
volatile struct shparam saveparam;
struct localvar *volatile savelocalvars;
volatile int e;
char *lastarg;
const char *path;
const struct builtincmd *firstbltin;
struct jmploc *volatile savehandler;
struct jmploc jmploc;
#if __GNUC__
/* Avoid longjmp clobbering */
(void) &argv;
(void) &argc;
(void) &lastarg;
(void) &flags;
#endif
/* First expand the arguments. */
TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
setstackmark(&smark);
arglist.lastp = &arglist.list;
varlist.lastp = &varlist.list;
arglist.list = 0;
oexitstatus = exitstatus;
exitstatus = 0;
path = pathval();
for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
expandarg(argp, &varlist, EXP_VARTILDE);
}
for (
argp = cmd->ncmd.args; argp && !arglist.list;
argp = argp->narg.next
) {
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
}
if (argp) {
struct builtincmd *bcmd;
int pseudovarflag;
bcmd = find_builtin(arglist.list->text);
pseudovarflag = bcmd && IS_BUILTIN_ASSIGN(bcmd);
for (; argp; argp = argp->narg.next) {
if (pseudovarflag && isassignment(argp->narg.text)) {
expandarg(argp, &arglist, EXP_VARTILDE);
continue;
}
expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
}
}
*arglist.lastp = NULL;
*varlist.lastp = NULL;
expredir(cmd->ncmd.redirect);
argc = 0;
for (sp = arglist.list ; sp ; sp = sp->next)
argc++;
argv = stalloc(sizeof (char *) * (argc + 1));
for (sp = arglist.list ; sp ; sp = sp->next) {
TRACE(("evalcommand arg: %s\n", sp->text));
*argv++ = sp->text;
}
*argv = NULL;
lastarg = NULL;
if (iflag && funcnest == 0 && argc > 0)
lastarg = argv[-1];
argv -= argc;
/* Print the command if xflag is set. */
if (xflag) {
out2c('+');
eprintlist(varlist.list);
eprintlist(arglist.list);
out2c('\n');
}
/* Now locate the command. */
if (argc == 0) {
cmdentry.cmdtype = CMDBUILTIN;
firstbltin = cmdentry.u.cmd = BLTINCMD;
} else {
const char *oldpath;
int findflag = DO_ERR;
int oldfindflag;
/*
* Modify the command lookup path, if a PATH= assignment
* is present
*/
for (sp = varlist.list ; sp ; sp = sp->next)
if (varequal(sp->text, defpathvar)) {
path = sp->text + 5;
findflag |= DO_BRUTE;
}
oldpath = path;
oldfindflag = findflag;
firstbltin = 0;
for(;;) {
find_command(argv[0], &cmdentry, findflag, path);
if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */
exitstatus = 127;
goto out;
}
/* implement bltin and command here */
if (cmdentry.cmdtype != CMDBUILTIN) {
break;
}
if (!firstbltin) {
firstbltin = cmdentry.u.cmd;
}
if (cmdentry.u.cmd == BLTINCMD) {
for(;;) {
struct builtincmd *bcmd;
argv++;
if (--argc == 0)
goto found;
if (!(bcmd = find_builtin(*argv))) {
out2fmt("%s: not found\n", *argv);
exitstatus = 127;
goto out;
}
cmdentry.u.cmd = bcmd;
if (bcmd != BLTINCMD)
break;
}
}
if (cmdentry.u.cmd == find_builtin("command")) {
argv++;
if (--argc == 0) {
goto found;
}
if (*argv[0] == '-') {
if (!equal(argv[0], "-p")) {
argv--;
argc++;
break;
}
argv++;
if (--argc == 0) {
goto found;
}
path = defpath;
findflag |= DO_BRUTE;
} else {
path = oldpath;
findflag = oldfindflag;
}
findflag |= DO_NOFUN;
continue;
}
found:
break;
}
}
/* Fork off a child process if necessary. */
if (cmd->ncmd.backgnd
|| (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
) {
jp = makejob(cmd, 1);
mode = cmd->ncmd.backgnd;
if (forkshell(jp, cmd, mode) != 0)
goto parent; /* at end of routine */
flags |= EV_EXIT;
}
/* This is the child process if a fork occurred. */
/* Execute the command. */
if (cmdentry.cmdtype == CMDFUNCTION) {
#ifdef DEBUG
trputs("Shell function: "); trargs(argv);
#endif
exitstatus = oexitstatus;
redirect(cmd->ncmd.redirect, REDIR_PUSH);
saveparam = shellparam;
shellparam.malloc = 0;
shellparam.nparam = argc - 1;
shellparam.p = argv + 1;
INTOFF;
savelocalvars = localvars;
localvars = NULL;
INTON;
if (setjmp(jmploc.loc)) {
if (exception == EXSHELLPROC) {
freeparam((volatile struct shparam *)
&saveparam);
} else {
saveparam.optind = shellparam.optind;
saveparam.optoff = shellparam.optoff;
freeparam(&shellparam);
shellparam = saveparam;
}
poplocalvars();
localvars = savelocalvars;
handler = savehandler;
longjmp(handler->loc, 1);
}
savehandler = handler;
handler = &jmploc;
for (sp = varlist.list ; sp ; sp = sp->next)
mklocal(sp->text);
funcnest++;
evaltree(cmdentry.u.func, flags & EV_TESTED);
funcnest--;
INTOFF;
poplocalvars();
localvars = savelocalvars;
saveparam.optind = shellparam.optind;
saveparam.optoff = shellparam.optoff;
freeparam(&shellparam);
shellparam = saveparam;
handler = savehandler;
popredir();
INTON;
if (evalskip == SKIPFUNC) {
evalskip = 0;
skipcount = 0;
}
if (flags & EV_EXIT)
exitshell(exitstatus);
} else if (cmdentry.cmdtype == CMDBUILTIN) {
#ifdef DEBUG
trputs("builtin command: "); trargs(argv);
#endif
mode = (cmdentry.u.cmd == EXECCMD)? 0 : REDIR_PUSH;
redirect(cmd->ncmd.redirect, mode);
savecmdname = commandname;
if (IS_BUILTIN_SPECIAL(firstbltin)) {
listsetvar(varlist.list);
} else {
cmdenviron = varlist.list;
}
e = -1;
if (setjmp(jmploc.loc)) {
e = exception;
exitstatus = (e == EXINT)? SIGINT+128 : 2;
goto cmddone;
}
savehandler = handler;
handler = &jmploc;
commandname = argv[0];
argptr = argv + 1;
optptr = NULL; /* initialize nextopt */
exitstatus = (*cmdentry.u.cmd->builtinfunc)(argc, argv);
flushall();
cmddone:
cmdenviron = NULL;
if (e != EXSHELLPROC) {
commandname = savecmdname;
if (flags & EV_EXIT)
exitshell(exitstatus);
}
handler = savehandler;
if (e != -1) {
if ((e != EXERROR && e != EXEXEC)
|| cmdentry.u.cmd == BLTINCMD
|| cmdentry.u.cmd == DOTCMD
|| cmdentry.u.cmd == EVALCMD
|| cmdentry.u.cmd == EXECCMD)
exraise(e);
FORCEINTON;
}
if (cmdentry.u.cmd != EXECCMD)
popredir();
} else {
#ifdef DEBUG
trputs("normal command: "); trargs(argv);
#endif
redirect(cmd->ncmd.redirect, 0);
clearredir();
for (sp = varlist.list ; sp ; sp = sp->next)
setvareq(sp->text, VEXPORT|VSTACK);
envp = environment();
shellexec(argv, envp, path, cmdentry.u.index);
}
goto out;
parent: /* parent process gets here (if we forked) */
if (mode == 0) { /* argument to fork */
INTOFF;
exitstatus = waitforjob(jp);
INTON;
}
out:
if (lastarg)
setvar("_", lastarg, 0);
popstackmark(&smark);
}
/*
* Evaluate a parse tree. The value is left in the global variable
* exitstatus.
*/
static void
evaltree(n, flags)
union node *n;
int flags;
{
int checkexit = 0;
if (n == NULL) {
TRACE(("evaltree(NULL) called\n"));
goto out;
}
TRACE(("evaltree(0x%lx: %d) called\n", (long)n, n->type));
switch (n->type) {
case NSEMI:
evaltree(n->nbinary.ch1, flags & EV_TESTED);
if (evalskip)
goto out;
evaltree(n->nbinary.ch2, flags);
break;
case NAND:
evaltree(n->nbinary.ch1, EV_TESTED);
if (evalskip || exitstatus != 0)
goto out;
evaltree(n->nbinary.ch2, flags);
break;
case NOR:
evaltree(n->nbinary.ch1, EV_TESTED);
if (evalskip || exitstatus == 0)
goto out;
evaltree(n->nbinary.ch2, flags);
break;
case NREDIR:
expredir(n->nredir.redirect);
redirect(n->nredir.redirect, REDIR_PUSH);
evaltree(n->nredir.n, flags);
popredir();
break;
case NSUBSHELL:
evalsubshell(n, flags);
break;
case NBACKGND:
evalsubshell(n, flags);
break;
case NIF: {
evaltree(n->nif.test, EV_TESTED);
if (evalskip)
goto out;
if (exitstatus == 0)
evaltree(n->nif.ifpart, flags);
else if (n->nif.elsepart)
evaltree(n->nif.elsepart, flags);
else
exitstatus = 0;
break;
}
case NWHILE:
case NUNTIL:
evalloop(n, flags);
break;
case NFOR:
evalfor(n, flags);
break;
case NCASE:
evalcase(n, flags);
break;
case NDEFUN: {
struct builtincmd *bcmd;
struct cmdentry entry;
if (
(bcmd = find_builtin(n->narg.text)) &&
IS_BUILTIN_SPECIAL(bcmd)
) {
out2fmt("%s is a special built-in\n", n->narg.text);
exitstatus = 1;
break;
}
entry.cmdtype = CMDFUNCTION;
entry.u.func = copyfunc(n->narg.next);
addcmdentry(n->narg.text, &entry);
exitstatus = 0;
break;
}
case NNOT:
evaltree(n->nnot.com, EV_TESTED);
exitstatus = !exitstatus;
break;
case NPIPE:
evalpipe(n);
checkexit = 1;
break;
case NCMD:
evalcommand(n, flags);
checkexit = 1;
break;
#ifdef DEBUG
default:
printf("Node type = %d\n", n->type);
break;
#endif
}
out:
if (pendingsigs)
dotrap();
if (
flags & EV_EXIT ||
(checkexit && eflag && exitstatus && !(flags & EV_TESTED))
)
exitshell(exitstatus);
}
/*
* Kick off a subshell to evaluate a tree.
*/
static void
evalsubshell(const union node *n, int flags)
{
struct job *jp;
int backgnd = (n->type == NBACKGND);
expredir(n->nredir.redirect);
jp = makejob(n, 1);
if (forkshell<