#error "Do not even bother, ash will not run on uClinux"
#endif
+#if DEBUG
+#define TRACE(param) trace param
+#define TRACEV(param) tracev param
+#else
+#define TRACE(param)
+#define TRACEV(param)
+#endif
#ifdef __GLIBC__
/* glibc sucks */
/* ============ Misc data */
+static char nullstr[1]; /* zero length string */
+static const char homestr[] = "HOME";
+static const char snlfmt[] = "%s\n";
+static const char illnum[] = "Illegal number: %s";
+
static int isloginsh;
/* pid of main shell */
static int rootpid;
* ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
*/
+struct strlist {
+ struct strlist *next;
+ char *text;
+};
+
struct strpush {
struct strpush *prev; /* preceding string on stack */
char *prevstring;
stacknxt = p;
}
+/*
+ * Like strdup but works with the ash stack.
+ */
+static char *
+ststrdup(const char *p)
+{
+ size_t len = strlen(p) + 1;
+ return memcpy(stalloc(len), p, len);
+}
+
static void
setstackmark(struct stackmark *mark)
{
return stack_nputstr(s, strlen(s), p);
}
+static char *
+_STPUTC(int c, char *p)
+{
+ if (p == sstrend)
+ p = growstackstr();
+ *p++ = c;
+ return p;
+}
-/* ============ Unsorted yet */
-
-
-static void setpwd(const char *, int);
-
-/* expand.h */
+#define STARTSTACKSTR(p) ((p) = stackblock())
+#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
+#define CHECKSTRSPACE(n, p) \
+ ({ \
+ char *q = (p); \
+ size_t l = (n); \
+ size_t m = sstrend - q; \
+ if (l > m) \
+ (p) = makestrspace(l, q); \
+ 0; \
+ })
+#define USTPUTC(c, p) (*p++ = (c))
+#define STACKSTRNUL(p) ((p) == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0'))
+#define STUNPUTC(p) (--p)
+#define STTOPC(p) p[-1]
+#define STADJUST(amount, p) (p += (amount))
-struct strlist {
- struct strlist *next;
- char *text;
-};
+#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
+#define ungrabstackstr(s, p) stunalloc((s))
+#define stackstrend() ((void *)sstrend)
-struct arglist {
- struct strlist *list;
- struct strlist **lastp;
-};
+/* ============ String helpers */
/*
- * expandarg() flags
+ * prefix -- see if pfx is a prefix of string.
*/
-#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 EXP_VARTILDE2 0x40 /* expand tildes after colons only */
-#define EXP_WORD 0x80 /* expand word in parameter expansion */
-#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
-
-
-union node;
-static void expandarg(union node *, struct arglist *, int);
-#define rmescapes(p) _rmescapes((p), 0)
-static char *_rmescapes(char *, int);
-static int casematch(union node *, char *);
-
-#if ENABLE_ASH_MATH_SUPPORT
-static void expari(int);
-#endif
-
-/* eval.h */
-
+static char *
+prefix(const char *string, const char *pfx)
+{
+ while (*pfx) {
+ if (*pfx++ != *string++)
+ return 0;
+ }
+ return (char *) string;
+}
+/*
+ * Check for a valid number. This should be elsewhere.
+ */
+static int
+is_number(const char *p)
+{
+ do {
+ if (!isdigit(*p))
+ return 0;
+ } while (*++p != '\0');
+ return 1;
+}
-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 */
-};
+/*
+ * Convert a string of digits to an integer, printing an error message on
+ * failure.
+ */
+static int
+number(const char *s)
+{
+ if (!is_number(s))
+ ash_msg_and_raise_error(illnum, s);
+ return atoi(s);
+}
/*
- * This file was generated by the mknodes program.
+ * 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;
-#define NCMD 0
-#define NPIPE 1
-#define NREDIR 2
-#define NBACKGND 3
-#define NSUBSHELL 4
-#define NAND 5
-#define NOR 6
-#define NSEMI 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 NCLOBBER 17
-#define NFROM 18
-#define NFROMTO 19
-#define NAPPEND 20
-#define NTOFD 21
-#define NFROMFD 22
-#define NHERE 23
-#define NXHERE 24
-#define NNOT 25
+ STARTSTACKSTR(p);
+ do {
+ char *q;
+ size_t len;
-struct ncmd {
- int type;
- union node *assign;
- union node *args;
- union node *redirect;
-};
+ len = strchrnul(s, '\'') - s;
-struct npipe {
- int type;
- int backgnd;
- struct nodelist *cmdlist;
-};
+ q = p = makestrspace(len + 3, p);
-struct nredir {
- int type;
- union node *n;
- union node *redirect;
-};
+ *q++ = '\'';
+ q = memcpy(q, s, len) + len;
+ *q++ = '\'';
+ s += len;
-struct nbinary {
- int type;
- union node *ch1;
- union node *ch2;
-};
+ STADJUST(q - p, p);
-struct nif {
- int type;
- union node *test;
- union node *ifpart;
- union node *elsepart;
-};
+ len = strspn(s, "'");
+ if (!len)
+ break;
-struct nfor {
- int type;
- union node *args;
- union node *body;
- char *var;
-};
+ q = p = makestrspace(len + 3, p);
-struct ncase {
- int type;
- union node *expr;
- union node *cases;
-};
+ *q++ = '"';
+ q = memcpy(q, s, len) + len;
+ *q++ = '"';
+ s += len;
-struct nclist {
- int type;
- union node *next;
- union node *pattern;
- union node *body;
-};
+ STADJUST(q - p, p);
+ } while (*s);
-struct narg {
- int type;
- union node *next;
- char *text;
- struct nodelist *backquote;
-};
+ USTPUTC(0, p);
-struct nfile {
- int type;
- union node *next;
- int fd;
- union node *fname;
- char *expfname;
-};
+ return stackblock();
+}
-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;
-};
+static char **argptr; /* argument list for builtin commands */
+static char *optionarg; /* set by nextopt (like getopt) */
+static char *optptr; /* used by nextopt */
-union node {
- int type;
- struct ncmd ncmd;
- struct npipe npipe;
- struct nredir nredir;
- struct nbinary nbinary;
- 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;
-};
+/*
+ * 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;
-struct nodelist {
- struct nodelist *next;
- union node *n;
-};
+ p = optptr;
+ if (p == NULL || *p == '\0') {
+ p = *argptr;
+ if (p == NULL || *p != '-' || *++p == '\0')
+ return '\0';
+ argptr++;
+ if (LONE_DASH(p)) /* check for "--" */
+ return '\0';
+ }
+ c = *p++;
+ for (q = optstring; *q != c; ) {
+ if (*q == '\0')
+ ash_msg_and_raise_error("Illegal option -%c", c);
+ if (*++q == ':')
+ q++;
+ }
+ if (*++q == ':') {
+ if (*p == '\0' && (p = *argptr++) == NULL)
+ ash_msg_and_raise_error("No arg for -%c option", c);
+ optionarg = p;
+ p = NULL;
+ }
+ optptr = p;
+ return c;
+}
-struct funcnode {
- int count;
- union node n;
-};
+/* ============ Variables */
-static void freefunc(struct funcnode *);
-/* parser.h */
+/* flags */
+#define VEXPORT 0x01 /* variable is exported */
+#define VREADONLY 0x02 /* variable cannot be modified */
+#define VSTRFIXED 0x04 /* variable struct is statically allocated */
+#define VTEXTFIXED 0x08 /* text is statically 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 */
+#define VNOSET 0x80 /* do not set variable - just readonly test */
+#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
+#ifdef DYNAMIC_VAR
+# define VDYNAMIC 0x200 /* dynamic variable */
+# else
+# define VDYNAMIC 0
+#endif
-/* control characters in argument strings */
-#define CTL_FIRST '\201' /* first 'special' character */
-#define CTLESC '\201' /* escape next character */
-#define CTLVAR '\202' /* variable defn */
-#define CTLENDVAR '\203'
-#define CTLBACKQ '\204'
-#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
-/* CTLBACKQ | CTLQUOTE == '\205' */
-#define CTLARI '\206' /* arithmetic expression */
-#define CTLENDARI '\207'
-#define CTLQUOTEMARK '\210'
-#define CTL_LAST '\210' /* last 'special' character */
+#if ENABLE_LOCALE_SUPPORT
+static void change_lc_all(const char *value);
+static void change_lc_ctype(const char *value);
+#endif
-/* variable substitution byte (follows CTLVAR) */
-#define VSTYPE 0x0f /* type of variable substitution */
-#define VSNUL 0x10 /* colon--treat the empty string as unset */
-#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */
+static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
+#ifdef IFS_BROKEN
+static const char defifsvar[] = "IFS= \t\n";
+#define defifs (defifsvar + 4)
+#else
+static const char defifs[] = " \t\n";
+#endif
-/* 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 VSTRIMRIGHT 0x6 /* ${var%pattern} */
-#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
-#define VSTRIMLEFT 0x8 /* ${var#pattern} */
-#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
-#define VSLENGTH 0xa /* ${#var} */
+struct var {
+ struct var *next; /* next entry in hash list */
+ int flags; /* flags are defined above */
+ const char *text; /* name=value */
+ void (*func)(const char *); /* function to be called when */
+ /* the variable gets set/unset */
+};
-/* values of checkkwd variable */
-#define CHKALIAS 0x1
-#define CHKKWD 0x2
-#define CHKNL 0x4
+struct localvar {
+ struct localvar *next; /* next local variable in list */
+ struct var *vp; /* the variable that was made local */
+ int flags; /* saved flags */
+ const char *text; /* saved text */
+};
-#define IBUFSIZ (BUFSIZ + 1)
+/* Forward decls for varinit[] */
+#if ENABLE_ASH_MAIL
+static void chkmail(void);
+static void changemail(const char *);
+#endif
+static void changepath(const char *);
+#if ENABLE_ASH_GETOPTS
+static void getoptsreset(const char *);
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+static void change_random(const char *);
+#endif
+
+static struct var varinit[] = {
+#ifdef IFS_BROKEN
+ { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
+#else
+ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
+#endif
+
+#if ENABLE_ASH_MAIL
+ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
+ { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
+#endif
+
+ { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
+ { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
+#if ENABLE_ASH_GETOPTS
+ { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
+#endif
+#if ENABLE_ASH_RANDOM_SUPPORT
+ {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
+#endif
+#if ENABLE_LOCALE_SUPPORT
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
+#endif
+#if ENABLE_FEATURE_EDITING_SAVEHISTORY
+ {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
+#endif
+};
+
+#define vifs varinit[0]
+#if ENABLE_ASH_MAIL
+#define vmail (&vifs)[1]
+#define vmpath (&vmail)[1]
+#else
+#define vmpath vifs
+#endif
+#define vpath (&vmpath)[1]
+#define vps1 (&vpath)[1]
+#define vps2 (&vps1)[1]
+#define vps4 (&vps2)[1]
+#define voptind (&vps4)[1]
+#if ENABLE_ASH_GETOPTS
+#define vrandom (&voptind)[1]
+#else
+#define vrandom (&vps4)[1]
+#endif
+#define defpath (defpathvar + 5)
/*
- * 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.
+ * The following macros access the values of the above variables.
+ * They have to skip over the name. They return the null string
+ * for unset variables.
*/
-static int plinno = 1; /* input line number */
+#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 ps4val() (vps4.text + 4)
+#define optindval() (voptind.text + 7)
-/* number of characters left in input buffer */
-static int parsenleft; /* copy of parsefile->nleft */
-static int parselleft; /* copy of parsefile->lleft */
+#define mpathset() ((vmpath.flags & VUNSET) == 0)
-/* next character in input buffer */
-static char *parsenextc; /* copy of parsefile->nextc */
+static struct var **hashvar(const char *);
-#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
+static int loopnest; /* current loop nesting level */
+/*
+ * The parsefile structure pointed to by the global variable parsefile
+ * contains information about the current file being read.
+ */
+struct redirtab {
+ struct redirtab *next;
+ int renamed[10];
+ int nullredirs;
+};
-static int tokpushback; /* last token pushed back */
-#define NEOF ((union node *)&tokpushback)
-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 int checkkwd;
-static struct nodelist *backquotelist;
-static union node *redirnode;
-static struct heredoc *heredoc;
-static int quoteflag; /* set if (part of) last token was quoted */
+static struct redirtab *redirlist;
+static int nullredirs;
-static void fixredir(union node *, const char *, int);
-static char *endofname(const char *);
+extern char **environ;
-/* shell.h */
+static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
-static char nullstr[1]; /* zero length string */
-static const char spcstr[] = " ";
-static const char snlfmt[] = "%s\n";
-static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
-static const char illnum[] = "Illegal number: %s";
-static const char homestr[] = "HOME";
-
-#if DEBUG
-#define TRACE(param) trace param
-#define TRACEV(param) tracev param
-#else
-#define TRACE(param)
-#define TRACEV(param)
+struct shparam {
+ int nparam; /* # of positional parameters (without $0) */
+ unsigned char malloc; /* if parameter list dynamically allocated */
+ char **p; /* parameter list */
+#if ENABLE_ASH_GETOPTS
+ int optind; /* next parameter to be processed by getopts */
+ int optoff; /* used by getopts */
#endif
+};
-#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
-#define __builtin_expect(x, expected_value) (x)
-#endif
+static struct shparam shellparam; /* $@ current positional parameters */
-#define xlikely(x) __builtin_expect((x),1)
+#define VTABSIZE 39
+static struct var *vartab[VTABSIZE];
-#define TEOF 0
-#define TNL 1
-#define TREDIR 2
-#define TWORD 3
-#define TSEMI 4
-#define TBACKGND 5
-#define TAND 6
-#define TOR 7
-#define TPIPE 8
-#define TLP 9
-#define TRP 10
-#define TENDCASE 11
-#define TENDBQUOTE 12
-#define TNOT 13
-#define TCASE 14
-#define TDO 15
-#define TDONE 16
-#define TELIF 17
-#define TELSE 18
-#define TESAC 19
-#define TFI 20
-#define TFOR 21
-#define TIF 22
-#define TIN 23
-#define TTHEN 24
-#define TUNTIL 25
-#define TWHILE 26
-#define TBEGIN 27
-#define TEND 28
+#if ENABLE_ASH_GETOPTS
+static void
+getoptsreset(const char *value)
+{
+ shellparam.optind = number(value);
+ shellparam.optoff = -1;
+}
+#endif
-/* first char is indicating which tokens mark the end of a list */
-static const char *const tokname_array[] = {
- "\1end of file",
- "\0newline",
- "\0redirection",
- "\0word",
- "\0;",
- "\0&",
- "\0&&",
- "\0||",
- "\0|",
- "\0(",
- "\1)",
- "\1;;",
- "\1`",
-#define KWDOFFSET 13
- /* the following are keywords */
- "\0!",
- "\0case",
- "\1do",
- "\1done",
- "\1elif",
- "\1else",
- "\1esac",
- "\1fi",
- "\0for",
- "\0if",
- "\0in",
- "\1then",
- "\0until",
- "\0while",
- "\0{",
- "\1}",
-};
+#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
-static const char *
-tokname(int tok)
+/*
+ * Return of a legal variable name (a letter or underscore followed by zero or
+ * more letters, underscores, and digits).
+ */
+static char *
+endofname(const char *name)
{
- static char buf[16];
+ char *p;
- if (tok >= TSEMI)
- buf[0] = '"';
- sprintf(buf + (tok >= TSEMI), "%s%c",
- tokname_array[tok] + 1, (tok >= TSEMI ? '"' : 0));
- return buf;
+ p = (char *) name;
+ if (!is_name(*p))
+ return p;
+ while (*++p) {
+ if (!is_in_name(*p))
+ break;
+ }
+ return p;
}
-/* Wrapper around strcmp for qsort/bsearch/... */
+/*
+ * Compares two strings up to the first = or '\0'. The first
+ * string must be terminated by '='; the second may be terminated by
+ * either '=' or '\0'.
+ */
static int
-pstrcmp(const void *a, const void *b)
+varcmp(const char *p, const char *q)
{
- return strcmp((const char *) a, (*(const char *const *) b) + 1);
+ int c, d;
+
+ while ((c = *p) == (d = *q)) {
+ if (!c || c == '=')
+ goto out;
+ p++;
+ q++;
+ }
+ if (c == '=')
+ c = 0;
+ if (d == '=')
+ d = 0;
+ out:
+ return c - d;
}
-static const char *const *
-findkwd(const char *s)
+static int
+varequal(const char *a, const char *b)
{
- return bsearch(s, tokname_array + KWDOFFSET,
- (sizeof(tokname_array) / sizeof(const char *)) - KWDOFFSET,
- sizeof(const char *), pstrcmp);
+ return !varcmp(a, b);
}
-/* 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 */
-
-#if ENABLE_ASH_ALIAS
-#define SYNBASE 130
-#define PEOF -130
-#define PEOA -129
-#define PEOA_OR_PEOF PEOA
-#else
-#define SYNBASE 129
-#define PEOF -129
-#define PEOA_OR_PEOF PEOF
-#endif
+/*
+ * Find the appropriate entry in the hash table from the name.
+ */
+static struct var **
+hashvar(const char *p)
+{
+ unsigned hashval;
-#define is_digit(c) ((unsigned)((c) - '0') <= 9)
-#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
-#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
+ hashval = ((unsigned char) *p) << 4;
+ while (*p && *p != '=')
+ hashval += (unsigned char) *p++;
+ return &vartab[hashval % VTABSIZE];
+}
-/* C99 say: "char" declaration may be signed or unsigned default */
-#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
+static int
+vpcmp(const void *a, const void *b)
+{
+ return varcmp(*(const char **)a, *(const char **)b);
+}
/*
- * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
- * (assuming ascii char codes, as the original implementation did)
+ * This routine initializes the builtin variables.
*/
-#define is_special(c) \
- ( (((unsigned int)c) - 33 < 32) \
- && ((0xc1ff920dUL >> (((unsigned int)c) - 33)) & 1))
+static void
+initvar(void)
+{
+ struct var *vp;
+ struct var *end;
+ struct var **vpp;
-#define digit_val(c) ((c) - '0')
+ /*
+ * PS1 depends on uid
+ */
+#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
+ vps1.text = "PS1=\\w \\$ ";
+#else
+ if (!geteuid())
+ vps1.text = "PS1=# ";
+#endif
+ vp = varinit;
+ end = vp + sizeof(varinit) / sizeof(varinit[0]);
+ do {
+ vpp = hashvar(vp->text);
+ vp->next = *vpp;
+ *vpp = vp;
+ } while (++vp < end);
+}
+
+static struct var **
+findvar(struct var **vpp, const char *name)
+{
+ for (; *vpp; vpp = &(*vpp)->next) {
+ if (varequal((*vpp)->text, name)) {
+ break;
+ }
+ }
+ return vpp;
+}
/*
- * This file was generated by the mksyntax program.
+ * Find the value of a variable. Returns NULL if not set.
*/
+static char *
+lookupvar(const char *name)
+{
+ struct var *v;
-#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
-#define USE_SIT_FUNCTION
+ v = *findvar(hashvar(name), name);
+ if (v) {
+#ifdef DYNAMIC_VAR
+ /*
+ * Dynamic variables are implemented roughly the same way they are
+ * in bash. Namely, they're "special" so long as they aren't unset.
+ * As soon as they're unset, they're no longer dynamic, and dynamic
+ * lookup will no longer happen at that point. -- PFM.
+ */
+ if ((v->flags & VDYNAMIC))
+ (*v->func)(NULL);
#endif
+ if (!(v->flags & VUNSET))
+ return strchrnul(v->text, '=') + 1;
+ }
+ return NULL;
+}
-/* 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 */
-
-#if ENABLE_ASH_MATH_SUPPORT
-static const char S_I_T[][4] = {
-#if ENABLE_ASH_ALIAS
- {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
-#endif
- {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
- {CNL, CNL, CNL, CNL}, /* 2, \n */
- {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
- {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
- {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
- {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
- {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
- {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
- {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
- {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
- {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
-#ifndef USE_SIT_FUNCTION
- {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
- {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
- {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
-#endif
-};
-#else
-static const char S_I_T[][3] = {
-#if ENABLE_ASH_ALIAS
- {CSPCL, CIGN, CIGN}, /* 0, PEOA */
-#endif
- {CSPCL, CWORD, CWORD}, /* 1, ' ' */
- {CNL, CNL, CNL}, /* 2, \n */
- {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
- {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
- {CVAR, CVAR, CWORD}, /* 5, $ */
- {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
- {CSPCL, CWORD, CWORD}, /* 7, ( */
- {CSPCL, CWORD, CWORD}, /* 8, ) */
- {CBACK, CBACK, CCTL}, /* 9, \ */
- {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
- {CENDVAR, CENDVAR, CWORD}, /* 11, } */
-#ifndef USE_SIT_FUNCTION
- {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
- {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
- {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
-#endif
-};
-#endif /* ASH_MATH_SUPPORT */
+/*
+ * Search the environment of a builtin command.
+ */
+static char *
+bltinlookup(const char *name)
+{
+ struct strlist *sp;
-#ifdef USE_SIT_FUNCTION
+ for (sp = cmdenviron; sp; sp = sp->next) {
+ if (varequal(sp->text, name))
+ return strchrnul(sp->text, '=') + 1;
+ }
+ return lookupvar(name);
+}
-#define U_C(c) ((unsigned char)(c))
+/*
+ * 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.
+ * Called with interrupts off.
+ */
+static void
+setvareq(char *s, int flags)
+{
+ struct var *vp, **vpp;
+
+ vpp = hashvar(s);
+ flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
+ vp = *findvar(vpp, s);
+ if (vp) {
+ if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
+ const char *n;
+
+ if (flags & VNOSAVE)
+ free(s);
+ n = vp->text;
+ ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
+ }
+
+ if (flags & VNOSET)
+ return;
+
+ if (vp->func && (flags & VNOFUNC) == 0)
+ (*vp->func)(strchrnul(s, '=') + 1);
+
+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+
+ flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
+ } else {
+ if (flags & VNOSET)
+ return;
+ /* not found */
+ vp = ckmalloc(sizeof(*vp));
+ vp->next = *vpp;
+ vp->func = NULL;
+ *vpp = vp;
+ }
+ if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
+ s = ckstrdup(s);
+ vp->text = s;
+ vp->flags = flags;
+}
+
+/*
+ * 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(const char *name, const char *val, int flags)
+{
+ char *p, *q;
+ size_t namelen;
+ char *nameeq;
+ size_t vallen;
+
+ q = endofname(name);
+ p = strchrnul(q, '=');
+ namelen = p - name;
+ if (!namelen || p != q)
+ ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
+ vallen = 0;
+ if (val == NULL) {
+ flags |= VUNSET;
+ } else {
+ vallen = strlen(val);
+ }
+ INT_OFF;
+ nameeq = ckmalloc(namelen + vallen + 2);
+ p = memcpy(nameeq, name, namelen) + namelen;
+ if (val) {
+ *p++ = '=';
+ p = memcpy(p, val, vallen) + vallen;
+ }
+ *p = '\0';
+ setvareq(nameeq, flags | VNOSAVE);
+ INT_ON;
+}
+#if ENABLE_ASH_GETOPTS
+/*
+ * Safe version of setvar, returns 1 on success 0 on failure.
+ */
static int
-SIT(int c, int syntax)
+setvarsafe(const char *name, const char *val, int flags)
{
- static const char spec_symbls[] = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~";
-#if ENABLE_ASH_ALIAS
- 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 /* "}~" */
- };
-#else
- static const char syntax_index_table[] = {
- 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
- 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
- 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
- 10, 2 /* "}~" */
- };
-#endif
- const char *s;
- int indx;
+ int err;
+ volatile int saveint;
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
- if (c == PEOF) /* 2^8+2 */
- return CENDFILE;
-#if ENABLE_ASH_ALIAS
- if (c == PEOA) /* 2^8+1 */
- indx = 0;
- else
-#endif
- if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
- return CCTL;
+ SAVE_INT(saveint);
+ if (setjmp(jmploc.loc))
+ err = 1;
else {
- s = strchr(spec_symbls, c);
- if (s == NULL || *s == '\0')
- return CWORD;
- indx = syntax_index_table[(s - spec_symbls)];
+ exception_handler = &jmploc;
+ setvar(name, val, flags);
+ err = 0;
}
- return S_I_T[indx][syntax];
+ exception_handler = savehandler;
+ RESTORE_INT(saveint);
+ return err;
}
+#endif
-#else /* USE_SIT_FUNCTION */
-
-#define SIT(c, syntax) S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]
+/*
+ * Unset the specified variable.
+ */
+static int
+unsetvar(const char *s)
+{
+ struct var **vpp;
+ struct var *vp;
+ int retval;
-#if ENABLE_ASH_ALIAS
-#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_CWORD 4
-#define CVAR_CVAR_CWORD_CVAR 5
-#define CSQUOTE_CWORD_CENDQUOTE_CWORD 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
-#else
-#define CSPCL_CWORD_CWORD_CWORD 0
-#define CNL_CNL_CNL_CNL 1
-#define CWORD_CCTL_CCTL_CWORD 2
-#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
-#define CVAR_CVAR_CWORD_CVAR 4
-#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
-#define CSPCL_CWORD_CWORD_CLP 6
-#define CSPCL_CWORD_CWORD_CRP 7
-#define CBACK_CBACK_CCTL_CBACK 8
-#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
-#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
-#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
-#define CWORD_CWORD_CWORD_CWORD 12
-#define CCTL_CCTL_CCTL_CCTL 13
-#endif
+ vpp = findvar(hashvar(s), s);
+ vp = *vpp;
+ retval = 2;
+ if (vp) {
+ int flags = vp->flags;
-static const char syntax_index_table[258] = {
- /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
+ retval = 1;
+ if (flags & VREADONLY)
+ goto out;
+#ifdef DYNAMIC_VAR
+ vp->flags &= ~VDYNAMIC;
+#endif
+ if (flags & VUNSET)
+ goto ok;
+ if ((flags & VSTRFIXED) == 0) {
+ INT_OFF;
+ if ((flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+ *vpp = vp->next;
+ free(vp);
+ INT_ON;
+ } else {
+ setvar(s, 0, 0);
+ vp->flags &= ~VEXPORT;
+ }
+ ok:
+ retval = 0;
+ }
+ out:
+ return retval;
+}
+
+/*
+ * Process a linked list of variable assignments.
+ */
+static void
+listsetvar(struct strlist *list_set_var, int flags)
+{
+ struct strlist *lp = list_set_var;
+
+ if (!lp)
+ return;
+ INT_OFF;
+ do {
+ setvareq(lp->text, flags);
+ } while ((lp = lp->next));
+ INT_ON;
+}
+
+/*
+ * Generate a list of variables satisfying the given conditions.
+ */
+static char **
+listvars(int on, int off, char ***end)
+{
+ struct var **vpp;
+ struct var *vp;
+ char **ep;
+ int mask;
+
+ STARTSTACKSTR(ep);
+ vpp = vartab;
+ mask = on | off;
+ do {
+ for (vp = *vpp; vp; vp = vp->next) {
+ if ((vp->flags & mask) == on) {
+ if (ep == stackstrend())
+ ep = growstackstr();
+ *ep++ = (char *) vp->text;
+ }
+ }
+ } while (++vpp < vartab + VTABSIZE);
+ if (ep == stackstrend())
+ ep = growstackstr();
+ if (end)
+ *end = ep;
+ *ep++ = NULL;
+ return grabstackstr(ep);
+}
+
+
+/* ============ Path search helper
+ *
+ * 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; /* set by padvance */
+
+static char *
+padvance(const char **path, const char *name)
+{
+ const char *p;
+ char *q;
+ const char *start;
+ size_t 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);
+}
+
+
+/* ============ Prompt */
+
+static int doprompt; /* if set, prompt the user */
+static int needprompt; /* true if interactive and at start of line */
+
+#if ENABLE_FEATURE_EDITING
+static line_input_t *line_input_state;
+static const char *cmdedit_prompt;
+static void
+putprompt(const char *s)
+{
+ if (ENABLE_ASH_EXPAND_PRMT) {
+ free((char*)cmdedit_prompt);
+ cmdedit_prompt = xstrdup(s);
+ return;
+ }
+ cmdedit_prompt = s;
+}
+#else
+static void
+putprompt(const char *s)
+{
+ out2str(s);
+}
+#endif
+
+#if ENABLE_ASH_EXPAND_PRMT
+/* expandstr() needs parsing machinery, so it is far away ahead... */
+static const char *expandstr(const char *ps);
+#else
+#define expandstr(s) s
+#endif
+
+static void
+setprompt(int whichprompt)
+{
+ const char *prompt;
+#if ENABLE_ASH_EXPAND_PRMT
+ struct stackmark smark;
+#endif
+
+ needprompt = 0;
+
+ switch (whichprompt) {
+ case 1:
+ prompt = ps1val();
+ break;
+ case 2:
+ prompt = ps2val();
+ break;
+ default: /* 0 */
+ prompt = nullstr;
+ }
+#if ENABLE_ASH_EXPAND_PRMT
+ setstackmark(&smark);
+ stalloc(stackblocksize());
+#endif
+ putprompt(expandstr(prompt));
+#if ENABLE_ASH_EXPAND_PRMT
+ popstackmark(&smark);
+#endif
+}
+
+
+/* ============ The cd and pwd commands */
+
+#define CD_PHYSICAL 1
+#define CD_PRINT 2
+
+static int docd(const char *, int);
+
+static char *curdir = nullstr; /* current working directory */
+static char *physdir = nullstr; /* physical working directory */
+
+static int
+cdopt(void)
+{
+ int flags = 0;
+ int i, j;
+
+ j = 'L';
+ while ((i = nextopt("LP"))) {
+ if (i != j) {
+ flags ^= CD_PHYSICAL;
+ j = i;
+ }
+ }
+
+ return flags;
+}
+
+/*
+ * Update curdir (the name of the current directory) in response to a
+ * cd command.
+ */
+static const char *
+updatepwd(const char *dir)
+{
+ char *new;
+ char *p;
+ char *cdcomppath;
+ const char *lim;
+
+ cdcomppath = ststrdup(dir);
+ STARTSTACKSTR(new);
+ if (*dir != '/') {
+ if (curdir == nullstr)
+ return 0;
+ new = stack_putstr(curdir, new);
+ }
+ new = makestrspace(strlen(dir) + 2, new);
+ lim = stackblock() + 1;
+ if (*dir != '/') {
+ if (new[-1] != '/')
+ USTPUTC('/', new);
+ if (new > lim && *lim == '/')
+ lim++;
+ } else {
+ USTPUTC('/', new);
+ cdcomppath++;
+ if (dir[1] == '/' && dir[2] != '/') {
+ USTPUTC('/', new);
+ cdcomppath++;
+ lim++;
+ }
+ }
+ p = strtok(cdcomppath, "/");
+ while (p) {
+ switch (*p) {
+ case '.':
+ if (p[1] == '.' && p[2] == '\0') {
+ while (new > lim) {
+ STUNPUTC(new);
+ if (new[-1] == '/')
+ break;
+ }
+ break;
+ } else if (p[1] == '\0')
+ break;
+ /* fall through */
+ default:
+ new = stack_putstr(p, new);
+ USTPUTC('/', new);
+ }
+ p = strtok(0, "/");
+ }
+ if (new > lim)
+ STUNPUTC(new);
+ *new = 0;
+ return stackblock();
+}
+
+/*
+ * Find out what the current directory is. If we already know the current
+ * directory, this routine returns immediately.
+ */
+static char *
+getpwd(void)
+{
+ char *dir = getcwd(0, 0);
+ return dir ? dir : nullstr;
+}
+
+static void
+setpwd(const char *val, int setold)
+{
+ char *oldcur, *dir;
+
+ oldcur = dir = curdir;
+
+ if (setold) {
+ setvar("OLDPWD", oldcur, VEXPORT);
+ }
+ INT_OFF;
+ if (physdir != nullstr) {
+ if (physdir != oldcur)
+ free(physdir);
+ physdir = nullstr;
+ }
+ if (oldcur == val || !val) {
+ char *s = getpwd();
+ physdir = s;
+ if (!val)
+ dir = s;
+ } else
+ dir = ckstrdup(val);
+ if (oldcur != dir && oldcur != nullstr) {
+ free(oldcur);
+ }
+ curdir = dir;
+ INT_ON;
+ setvar("PWD", dir, VEXPORT);
+}
+
+static void hashcd(void);
+
+/*
+ * Actually do the chdir. We also call hashcd to let the routines in exec.c
+ * know that the current directory has changed.
+ */
+static int
+docd(const char *dest, int flags)
+{
+ const char *dir = 0;
+ int err;
+
+ TRACE(("docd(\"%s\", %d) called\n", dest, flags));
+
+ INT_OFF;
+ if (!(flags & CD_PHYSICAL)) {
+ dir = updatepwd(dest);
+ if (dir)
+ dest = dir;
+ }
+ err = chdir(dest);
+ if (err)
+ goto out;
+ setpwd(dir, 1);
+ hashcd();
+ out:
+ INT_ON;
+ return err;
+}
+
+static int
+cdcmd(int argc, char **argv)
+{
+ const char *dest;
+ const char *path;
+ const char *p;
+ char c;
+ struct stat statb;
+ int flags;
+
+ flags = cdopt();
+ dest = *argptr;
+ if (!dest)
+ dest = bltinlookup(homestr);
+ else if (LONE_DASH(dest)) {
+ dest = bltinlookup("OLDPWD");
+ flags |= CD_PRINT;
+ }
+ if (!dest)
+ dest = nullstr;
+ if (*dest == '/')
+ goto step7;
+ if (*dest == '.') {
+ c = dest[1];
+ dotdot:
+ switch (c) {
+ case '\0':
+ case '/':
+ goto step6;
+ case '.':
+ c = dest[2];
+ if (c != '.')
+ goto dotdot;
+ }
+ }
+ if (!*dest)
+ dest = ".";
+ path = bltinlookup("CDPATH");
+ if (!path) {
+ step6:
+ step7:
+ p = dest;
+ goto docd;
+ }
+ do {
+ c = *path;
+ p = padvance(&path, dest);
+ if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
+ if (c && c != ':')
+ flags |= CD_PRINT;
+ docd:
+ if (!docd(p, flags))
+ goto out;
+ break;
+ }
+ } while (path);
+ ash_msg_and_raise_error("can't cd to %s", dest);
+ /* NOTREACHED */
+ out:
+ if (flags & CD_PRINT)
+ out1fmt(snlfmt, curdir);
+ return 0;
+}
+
+static int
+pwdcmd(int argc, char **argv)
+{
+ int flags;
+ const char *dir = curdir;
+
+ flags = cdopt();
+ if (flags) {
+ if (physdir == nullstr)
+ setpwd(dir, 0);
+ dir = physdir;
+ }
+ out1fmt(snlfmt, dir);
+ return 0;
+}
+
+
+/* ============ Unsorted yet */
+
+
+/* expand.h */
+
+struct arglist {
+ struct strlist *list;
+ struct strlist **lastp;
+};
+
+/*
+ * expandarg() flags
+ */
+#define EXP_FULL 0x1 /* perform word splitting & file globbing */
+#define EXP_TILDE 0x2 /* do normal tilde expansion */
+#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
+#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
+#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
+#define EXP_RECORD 0x20 /* need to record arguments for ifs breakup */
+#define EXP_VARTILDE2 0x40 /* expand tildes after colons only */
+#define EXP_WORD 0x80 /* expand word in parameter expansion */
+#define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */
+
+
+union node;
+static void expandarg(union node *, struct arglist *, int);
+#define rmescapes(p) _rmescapes((p), 0)
+static char *_rmescapes(char *, int);
+static int casematch(union node *, char *);
+
+#if ENABLE_ASH_MATH_SUPPORT
+static void expari(int);
+#endif
+
+/* eval.h */
+
+
+
+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 */
+};
+
+/*
+ * This file was generated by the mknodes program.
+ */
+
+#define NCMD 0
+#define NPIPE 1
+#define NREDIR 2
+#define NBACKGND 3
+#define NSUBSHELL 4
+#define NAND 5
+#define NOR 6
+#define NSEMI 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 NCLOBBER 17
+#define NFROM 18
+#define NFROMTO 19
+#define NAPPEND 20
+#define NTOFD 21
+#define NFROMFD 22
+#define NHERE 23
+#define NXHERE 24
+#define NNOT 25
+
+
+struct ncmd {
+ int type;
+ 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 nbinary {
+ int type;
+ union node *ch1;
+ union node *ch2;
+};
+
+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 ncmd ncmd;
+ struct npipe npipe;
+ struct nredir nredir;
+ struct nbinary nbinary;
+ 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 funcnode {
+ int count;
+ union node n;
+};
+
+
+static void freefunc(struct funcnode *);
+/* parser.h */
+
+/* control characters in argument strings */
+#define CTL_FIRST '\201' /* first 'special' character */
+#define CTLESC '\201' /* escape next character */
+#define CTLVAR '\202' /* variable defn */
+#define CTLENDVAR '\203'
+#define CTLBACKQ '\204'
+#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */
+/* CTLBACKQ | CTLQUOTE == '\205' */
+#define CTLARI '\206' /* arithmetic expression */
+#define CTLENDARI '\207'
+#define CTLQUOTEMARK '\210'
+#define CTL_LAST '\210' /* last 'special' character */
+
+/* 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 VSTRIMRIGHT 0x6 /* ${var%pattern} */
+#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
+#define VSTRIMLEFT 0x8 /* ${var#pattern} */
+#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
+#define VSLENGTH 0xa /* ${#var} */
+
+/* values of checkkwd variable */
+#define CHKALIAS 0x1
+#define CHKKWD 0x2
+#define CHKNL 0x4
+
+#define IBUFSIZ (BUFSIZ + 1)
+
+/*
+ * 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 plinno = 1; /* input line number */
+
+/* number of characters left in input buffer */
+static int parsenleft; /* copy of parsefile->nleft */
+static int parselleft; /* copy of parsefile->lleft */
+
+/* next character in input buffer */
+static char *parsenextc; /* copy of parsefile->nextc */
+
+#define basebuf bb_common_bufsiz1 /* buffer for top level input file */
+
+
+static int tokpushback; /* last token pushed back */
+#define NEOF ((union node *)&tokpushback)
+static int parsebackquote; /* nonzero if we are inside backquotes */
+static int lasttoken; /* last token read */
+static char *wordtext; /* text of last word returned by readtoken */
+static int checkkwd;
+static struct nodelist *backquotelist;
+static union node *redirnode;
+static struct heredoc *heredoc;
+static int quoteflag; /* set if (part of) last token was quoted */
+
+static void fixredir(union node *, const char *, int);
+static char *endofname(const char *);
+
+/* shell.h */
+
+static const char spcstr[] = " ";
+static const char dolatstr[] = { CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' };
+
+#if !defined(__GNUC__) || (__GNUC__ == 2 && __GNUC_MINOR__ < 96)
+#define __builtin_expect(x, expected_value) (x)
+#endif
+
+#define xlikely(x) __builtin_expect((x),1)
+
+
+#define TEOF 0
+#define TNL 1
+#define TREDIR 2
+#define TWORD 3
+#define TSEMI 4
+#define TBACKGND 5
+#define TAND 6
+#define TOR 7
+#define TPIPE 8
+#define TLP 9
+#define TRP 10
+#define TENDCASE 11
+#define TENDBQUOTE 12
+#define TNOT 13
+#define TCASE 14
+#define TDO 15
+#define TDONE 16
+#define TELIF 17
+#define TELSE 18
+#define TESAC 19
+#define TFI 20
+#define TFOR 21
+#define TIF 22
+#define TIN 23
+#define TTHEN 24
+#define TUNTIL 25
+#define TWHILE 26
+#define TBEGIN 27
+#define TEND 28
+
+/* first char is indicating which tokens mark the end of a list */
+static const char *const tokname_array[] = {
+ "\1end of file",
+ "\0newline",
+ "\0redirection",
+ "\0word",
+ "\0;",
+ "\0&",
+ "\0&&",
+ "\0||",
+ "\0|",
+ "\0(",
+ "\1)",
+ "\1;;",
+ "\1`",
+#define KWDOFFSET 13
+ /* 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;
+}
+
+/* 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);
+}
+
+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);
+}
+
+/* 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 */
+
+#if ENABLE_ASH_ALIAS
+#define SYNBASE 130
+#define PEOF -130
+#define PEOA -129
+#define PEOA_OR_PEOF PEOA
+#else
+#define SYNBASE 129
+#define PEOF -129
+#define PEOA_OR_PEOF PEOF
+#endif
+
+/* C99 say: "char" declaration may be signed or unsigned default */
+#define SC2INT(chr2may_be_negative_int) (int)((signed char)chr2may_be_negative_int)
+
+/*
+ * 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')
+
+/*
+ * This file was generated by the mksyntax program.
+ */
+
+#if ENABLE_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 */
+
+#if ENABLE_ASH_MATH_SUPPORT
+static const char S_I_T[][4] = {
+#if ENABLE_ASH_ALIAS
+ {CSPCL, CIGN, CIGN, CIGN}, /* 0, PEOA */
+#endif
+ {CSPCL, CWORD, CWORD, CWORD}, /* 1, ' ' */
+ {CNL, CNL, CNL, CNL}, /* 2, \n */
+ {CWORD, CCTL, CCTL, CWORD}, /* 3, !*-/:=?[]~ */
+ {CDQUOTE, CENDQUOTE, CWORD, CWORD}, /* 4, '"' */
+ {CVAR, CVAR, CWORD, CVAR}, /* 5, $ */
+ {CSQUOTE, CWORD, CENDQUOTE, CWORD}, /* 6, "'" */
+ {CSPCL, CWORD, CWORD, CLP}, /* 7, ( */
+ {CSPCL, CWORD, CWORD, CRP}, /* 8, ) */
+ {CBACK, CBACK, CCTL, CBACK}, /* 9, \ */
+ {CBQUOTE, CBQUOTE, CWORD, CBQUOTE}, /* 10, ` */
+ {CENDVAR, CENDVAR, CWORD, CENDVAR}, /* 11, } */
+#ifndef USE_SIT_FUNCTION
+ {CENDFILE, CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
+ {CWORD, CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
+ {CCTL, CCTL, CCTL, CCTL} /* 14, CTLESC ... */
+#endif
+};
+#else
+static const char S_I_T[][3] = {
+#if ENABLE_ASH_ALIAS
+ {CSPCL, CIGN, CIGN}, /* 0, PEOA */
+#endif
+ {CSPCL, CWORD, CWORD}, /* 1, ' ' */
+ {CNL, CNL, CNL}, /* 2, \n */
+ {CWORD, CCTL, CCTL}, /* 3, !*-/:=?[]~ */
+ {CDQUOTE, CENDQUOTE, CWORD}, /* 4, '"' */
+ {CVAR, CVAR, CWORD}, /* 5, $ */
+ {CSQUOTE, CWORD, CENDQUOTE}, /* 6, "'" */
+ {CSPCL, CWORD, CWORD}, /* 7, ( */
+ {CSPCL, CWORD, CWORD}, /* 8, ) */
+ {CBACK, CBACK, CCTL}, /* 9, \ */
+ {CBQUOTE, CBQUOTE, CWORD}, /* 10, ` */
+ {CENDVAR, CENDVAR, CWORD}, /* 11, } */
+#ifndef USE_SIT_FUNCTION
+ {CENDFILE, CENDFILE, CENDFILE}, /* 12, PEOF */
+ {CWORD, CWORD, CWORD}, /* 13, 0-9A-Za-z */
+ {CCTL, CCTL, CCTL} /* 14, CTLESC ... */
+#endif
+};
+#endif /* ASH_MATH_SUPPORT */
+
+#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 !\"$&'()*-/:;<=>?[\\]`|}~";
+#if ENABLE_ASH_ALIAS
+ 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 /* "}~" */
+ };
+#else
+ static const char syntax_index_table[] = {
+ 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
+ 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */
+ 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
+ 10, 2 /* "}~" */
+ };
+#endif
+ const char *s;
+ int indx;
+
+ if (c == PEOF) /* 2^8+2 */
+ return CENDFILE;
+#if ENABLE_ASH_ALIAS
+ if (c == PEOA) /* 2^8+1 */
+ indx = 0;
+ else
+#endif
+ if (U_C(c) >= U_C(CTLESC) && U_C(c) <= U_C(CTLQUOTEMARK))
+ return CCTL;
+ else {
+ s = strchr(spec_symbls, c);
+ if (s == NULL || *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]
+
+#if ENABLE_ASH_ALIAS
+#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_CWORD 4
+#define CVAR_CVAR_CWORD_CVAR 5
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 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
+#else
+#define CSPCL_CWORD_CWORD_CWORD 0
+#define CNL_CNL_CNL_CNL 1
+#define CWORD_CCTL_CCTL_CWORD 2
+#define CDQUOTE_CENDQUOTE_CWORD_CWORD 3
+#define CVAR_CVAR_CWORD_CVAR 4
+#define CSQUOTE_CWORD_CENDQUOTE_CWORD 5
+#define CSPCL_CWORD_CWORD_CLP 6
+#define CSPCL_CWORD_CWORD_CRP 7
+#define CBACK_CBACK_CCTL_CBACK 8
+#define CBQUOTE_CBQUOTE_CWORD_CBQUOTE 9
+#define CENDVAR_CENDVAR_CWORD_CENDVAR 10
+#define CENDFILE_CENDFILE_CENDFILE_CENDFILE 11
+#define CWORD_CWORD_CWORD_CWORD 12
+#define CCTL_CCTL_CCTL_CCTL 13
+#endif
+
+static const char syntax_index_table[258] = {
+ /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
/* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
#if ENABLE_ASH_ALIAS
/* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN,
static int killcmd(int, char **);
#endif
-/* mail.h */
-
-#if ENABLE_ASH_MAIL
-static void chkmail(void);
-static void changemail(const char *);
-#endif
-
/* exec.h */
/* values of cmdtype */
#define DO_ALTPATH 0x08 /* using alternate path */
#define DO_ALTBLTIN 0x20 /* %builtin in alt. path */
-static const char *pathopt; /* set by padvance */
-
static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
static char *padvance(const char **, const char *);
static void find_command(char *, struct cmdentry *, int, const char *);
static struct builtincmd *find_builtin(const char *);
-static void hashcd(void);
-static void changepath(const char *);
static void defun(char *, union node *);
static void unsetfunc(const char *);
#if ENABLE_ASH_RANDOM_SUPPORT
static unsigned long rseed;
-static void change_random(const char *);
# ifndef DYNAMIC_VAR
# define DYNAMIC_VAR
# endif
#endif
-/* var.h */
-
-/*
- * Shell variables.
- */
-
-#if ENABLE_ASH_GETOPTS
-static void getoptsreset(const char *);
-#endif
-
-/* flags */
-#define VEXPORT 0x01 /* variable is exported */
-#define VREADONLY 0x02 /* variable cannot be modified */
-#define VSTRFIXED 0x04 /* variable struct is statically allocated */
-#define VTEXTFIXED 0x08 /* text is statically 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 */
-#define VNOSET 0x80 /* do not set variable - just readonly test */
-#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
-#ifdef DYNAMIC_VAR
-# define VDYNAMIC 0x200 /* dynamic variable */
-# else
-# define VDYNAMIC 0
-#endif
-
-struct var {
- struct var *next; /* next entry in hash list */
- int flags; /* flags are defined above */
- const 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 */
- const char *text; /* saved text */
-};
-
-
-static struct localvar *localvars;
-
-/*
- * Shell variables.
- */
-#if ENABLE_LOCALE_SUPPORT
-static void change_lc_all(const char *value);
-static void change_lc_ctype(const char *value);
-#endif
-
-
-#define VTABSIZE 39
-
-static const char defpathvar[] = "PATH=/usr/local/bin:/usr/bin:/sbin:/bin";
-#ifdef IFS_BROKEN
-static const char defifsvar[] = "IFS= \t\n";
-#define defifs (defifsvar + 4)
-#else
-static const char defifs[] = " \t\n";
-#endif
-
-
-static struct var varinit[] = {
-#ifdef IFS_BROKEN
- { 0, VSTRFIXED|VTEXTFIXED, defifsvar, 0 },
-#else
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0", 0 },
-#endif
-
-#if ENABLE_ASH_MAIL
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0", changemail },
- { 0, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail },
-#endif
-
- { 0, VSTRFIXED|VTEXTFIXED, defpathvar, changepath },
- { 0, VSTRFIXED|VTEXTFIXED, "PS1=$ ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS2=> ", 0 },
- { 0, VSTRFIXED|VTEXTFIXED, "PS4=+ ", 0 },
-#if ENABLE_ASH_GETOPTS
- { 0, VSTRFIXED|VTEXTFIXED, "OPTIND=1", getoptsreset },
-#endif
-#if ENABLE_ASH_RANDOM_SUPPORT
- {0, VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM\0", change_random },
-#endif
-#if ENABLE_LOCALE_SUPPORT
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_ALL\0", change_lc_all },
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "LC_CTYPE\0", change_lc_ctype },
-#endif
-#if ENABLE_FEATURE_EDITING_SAVEHISTORY
- {0, VSTRFIXED | VTEXTFIXED | VUNSET, "HISTFILE\0", NULL },
-#endif
-};
-
-#define vifs varinit[0]
-#if ENABLE_ASH_MAIL
-#define vmail (&vifs)[1]
-#define vmpath (&vmail)[1]
-#else
-#define vmpath vifs
-#endif
-#define vpath (&vmpath)[1]
-#define vps1 (&vpath)[1]
-#define vps2 (&vps1)[1]
-#define vps4 (&vps2)[1]
-#define voptind (&vps4)[1]
-#if ENABLE_ASH_GETOPTS
-#define vrandom (&voptind)[1]
-#else
-#define vrandom (&vps4)[1]
-#endif
-#define defpath (defpathvar + 5)
-
-/*
- * 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 ps4val() (vps4.text + 4)
-#define optindval() (voptind.text + 7)
-
-#define mpathset() ((vmpath.flags & VUNSET) == 0)
-
-static void setvar(const char *, const char *, int);
-static void setvareq(char *, int);
-static void listsetvar(struct strlist *, int);
-static char *lookupvar(const char *);
-static char *bltinlookup(const char *);
-static char **listvars(int, int, char ***);
-#define environment() listvars(VEXPORT, VUNSET, 0)
-static int showvars(const char *, int, int);
-static void poplocalvars(void);
-static int unsetvar(const char *);
-#if ENABLE_ASH_GETOPTS
-static int setvarsafe(const char *, const char *, int);
-#endif
-static int varcmp(const char *, const char *);
-static struct var **hashvar(const char *);
-
-
-static int varequal(const char *a, const char *b)
-{
- return !varcmp(a, b);
-}
-
-
-static int loopnest; /* current loop nesting level */
-
-/*
- * The parsefile structure pointed to by the global variable parsefile
- * contains information about the current file being read.
- */
-
-
-struct redirtab {
- struct redirtab *next;
- int renamed[10];
- int nullredirs;
-};
-
-static struct redirtab *redirlist;
-static int nullredirs;
-
-extern char **environ;
-
-
-static int preverrout_fd; /* save fd2 before print debug if xflag is set. */
-
-
-/*
- * Initialization code.
- */
-
-/*
- * This routine initializes the builtin variables.
- */
-
-static void initvar(void)
-{
- struct var *vp;
- struct var *end;
- struct var **vpp;
-
- /*
- * PS1 depends on uid
- */
-#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
- vps1.text = "PS1=\\w \\$ ";
-#else
- if (!geteuid())
- vps1.text = "PS1=# ";
-#endif
- vp = varinit;
- end = vp + sizeof(varinit) / sizeof(varinit[0]);
- do {
- vpp = hashvar(vp->text);
- vp->next = *vpp;
- *vpp = vp;
- } while (++vp < end);
-}
-
/* PEOF (the end of file marker) */
enum {
* and restores it when files are pushed and popped. The user of this
* package must set its value.
*/
-
-static int pgetc(void);
-static int pgetc2(void);
-static int preadbuffer(void);
static void pungetc(void);
static void pushstring(char *, void *);
static void popstring(void);
static void popallfiles(void);
static void closescript(void);
-
/* jobs.h */
static void readcmdfile(char *);
-
-
-static char *_STPUTC(int c, char *p)
-{
- if (p == sstrend)
- p = growstackstr();
- *p++ = c;
- return p;
-}
-
-#define STARTSTACKSTR(p) ((p) = stackblock())
-#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
-#define CHECKSTRSPACE(n, p) \
- ({ \
- char *q = (p); \
- size_t l = (n); \
- size_t m = sstrend - q; \
- if (l > m) \
- (p) = makestrspace(l, q); \
- 0; \
- })
-#define USTPUTC(c, p) (*p++ = (c))
-#define STACKSTRNUL(p) ((p) == sstrend? (p = growstackstr(), *p = '\0') : (*p = '\0'))
-#define STUNPUTC(p) (--p)
-#define STTOPC(p) p[-1]
-#define STADJUST(amount, p) (p += (amount))
-
-#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
-#define ungrabstackstr(s, p) stunalloc((s))
-#define stackstrend() ((void *)sstrend)
+
/* mystring.h */
static int number(const char *);
static int is_number(const char *);
static char *single_quote(const char *);
-static char *sstrdup(const char *);
#define equal(s1, s2) (strcmp(s1, s2) == 0)
#define scopy(s1, s2) ((void)strcpy(s2, s1))
/* options.h */
-struct shparam {
- int nparam; /* # of positional parameters (without $0) */
- unsigned char malloc; /* if parameter list dynamically allocated */
- char **p; /* parameter list */
-#if ENABLE_ASH_GETOPTS
- int optind; /* next parameter to be processed by getopts */
- int optoff; /* used by getopts */
-#endif
-};
-
-
-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 */
}
#endif /* ASH_ALIAS */
-
-/* cd.c */
-
-/*
- * The cd and pwd commands.
- */
-
-#define CD_PHYSICAL 1
-#define CD_PRINT 2
-
-static int docd(const char *, int);
-static int cdopt(void);
-
-static char *curdir = nullstr; /* current working directory */
-static char *physdir = nullstr; /* physical working directory */
-
-static int
-cdopt(void)
-{
- int flags = 0;
- int i, j;
-
- j = 'L';
- while ((i = nextopt("LP"))) {
- if (i != j) {
- flags ^= CD_PHYSICAL;
- j = i;
- }
- }
-
- return flags;
-}
-
-static int
-cdcmd(int argc, char **argv)
-{
- const char *dest;
- const char *path;
- const char *p;
- char c;
- struct stat statb;
- int flags;
-
- flags = cdopt();
- dest = *argptr;
- if (!dest)
- dest = bltinlookup(homestr);
- else if (LONE_DASH(dest)) {
- dest = bltinlookup("OLDPWD");
- flags |= CD_PRINT;
- }
- if (!dest)
- dest = nullstr;
- if (*dest == '/')
- goto step7;
- if (*dest == '.') {
- c = dest[1];
- dotdot:
- switch (c) {
- case '\0':
- case '/':
- goto step6;
- case '.':
- c = dest[2];
- if (c != '.')
- goto dotdot;
- }
- }
- if (!*dest)
- dest = ".";
- path = bltinlookup("CDPATH");
- if (!path) {
- step6:
- step7:
- p = dest;
- goto docd;
- }
- do {
- c = *path;
- p = padvance(&path, dest);
- if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
- if (c && c != ':')
- flags |= CD_PRINT;
- docd:
- if (!docd(p, flags))
- goto out;
- break;
- }
- } while (path);
- ash_msg_and_raise_error("can't cd to %s", dest);
- /* NOTREACHED */
- out:
- if (flags & CD_PRINT)
- out1fmt(snlfmt, curdir);
- return 0;
-}
-
-
-/*
- * Update curdir (the name of the current directory) in response to a
- * cd command.
- */
-static const char * updatepwd(const char *dir)
-{
- char *new;
- char *p;
- char *cdcomppath;
- const char *lim;
-
- cdcomppath = sstrdup(dir);
- STARTSTACKSTR(new);
- if (*dir != '/') {
- if (curdir == nullstr)
- return 0;
- new = stack_putstr(curdir, new);
- }
- new = makestrspace(strlen(dir) + 2, new);
- lim = stackblock() + 1;
- if (*dir != '/') {
- if (new[-1] != '/')
- USTPUTC('/', new);
- if (new > lim && *lim == '/')
- lim++;
- } else {
- USTPUTC('/', new);
- cdcomppath++;
- if (dir[1] == '/' && dir[2] != '/') {
- USTPUTC('/', new);
- cdcomppath++;
- lim++;
- }
- }
- p = strtok(cdcomppath, "/");
- while (p) {
- switch (*p) {
- case '.':
- if (p[1] == '.' && p[2] == '\0') {
- while (new > lim) {
- STUNPUTC(new);
- if (new[-1] == '/')
- break;
- }
- break;
- } else if (p[1] == '\0')
- break;
- /* fall through */
- default:
- new = stack_putstr(p, new);
- USTPUTC('/', new);
- }
- p = strtok(0, "/");
- }
- if (new > lim)
- STUNPUTC(new);
- *new = 0;
- return stackblock();
-}
-
-
-/*
- * Actually do the chdir. We also call hashcd to let the routines in exec.c
- * know that the current directory has changed.
- */
-static int
-docd(const char *dest, int flags)
-{
- const char *dir = 0;
- int err;
-
- TRACE(("docd(\"%s\", %d) called\n", dest, flags));
-
- INT_OFF;
- if (!(flags & CD_PHYSICAL)) {
- dir = updatepwd(dest);
- if (dir)
- dest = dir;
- }
- err = chdir(dest);
- if (err)
- goto out;
- setpwd(dir, 1);
- hashcd();
- out:
- INT_ON;
- return err;
-}
-
-/*
- * Find out what the current directory is. If we already know the current
- * directory, this routine returns immediately.
- */
-static char * getpwd(void)
-{
- char *dir = getcwd(0, 0);
- return dir ? dir : nullstr;
-}
-
-static int
-pwdcmd(int argc, char **argv)
-{
- int flags;
- const char *dir = curdir;
-
- flags = cdopt();
- if (flags) {
- if (physdir == nullstr)
- setpwd(dir, 0);
- dir = physdir;
- }
- out1fmt(snlfmt, dir);
- return 0;
-}
-
-static void
-setpwd(const char *val, int setold)
-{
- char *oldcur, *dir;
-
- oldcur = dir = curdir;
-
- if (setold) {
- setvar("OLDPWD", oldcur, VEXPORT);
- }
- INT_OFF;
- if (physdir != nullstr) {
- if (physdir != oldcur)
- free(physdir);
- physdir = nullstr;
- }
- if (oldcur == val || !val) {
- char *s = getpwd();
- physdir = s;
- if (!val)
- dir = s;
- } else
- dir = ckstrdup(val);
- if (oldcur != dir && oldcur != nullstr) {
- free(oldcur);
- }
- curdir = dir;
- INT_ON;
- setvar("PWD", dir, VEXPORT);
-}
-
/* eval.c */
/*
}
#if ENABLE_ASH_CMDCMD
-static char ** parse_command_args(char **argv, const char **path)
+static char **
+parse_command_args(char **argv, const char **path)
{
char *cp, c;
return *q == '=';
}
-#if ENABLE_ASH_EXPAND_PRMT
-static const char *expandstr(const char *ps);
-#else
-#define expandstr(s) s
-#endif
-
/*
* Execute a simple command.
*/
return i;
}
+static struct localvar *localvars;
+
+/*
+ * Called after a function returns.
+ * Interrupts must be off.
+ */
+static void
+poplocalvars(void)
+{
+ struct localvar *lvp;
+ struct var *vp;
+
+ while ((lvp = localvars) != NULL) {
+ localvars = lvp->next;
+ vp = lvp->vp;
+ TRACE(("poplocalvar %s", vp ? vp->text : "-"));
+ if (vp == NULL) { /* $- saved */
+ memcpy(optlist, lvp->text, sizeof(optlist));
+ free((char*)lvp->text);
+ optschanged();
+ } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
+ unsetvar(vp->text);
+ } else {
+ if (vp->func)
+ (*vp->func)(strchrnul(lvp->text, '=') + 1);
+ if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
+ free((char*)vp->text);
+ vp->flags = lvp->flags;
+ vp->text = lvp->text;
+ }
+ free(lvp);
+ }
+}
+
static int
evalfun(struct funcnode *func, int argc, char **argv, int flags)
{
}
-static int goodname(const char *p)
+static int
+goodname(const char *p)
{
return !*endofname(p);
}
* Exec a program. Never returns. If you change this routine, you may
* have to change the find_command routine as well.
*/
+#define environment() listvars(VEXPORT, VUNSET, 0)
static void shellexec(char **, const char *, int) ATTRIBUTE_NORETURN;
static void
shellexec(char **argv, const char *path, int idx)
}
-/*
- * 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 char *
-padvance(const char **path, const char *name)
-{
- const char *p;
- char *q;
- const char *start;
- size_t len;
-
- if (*path == NULL)
- return NULL;
- start = *path;
- for (p = start; *p && *p != ':' && *p != '%'; p++);
- len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
- while (stackblocksize() < len)
- growstackblock();
- q = stackblock();
- if (p != start) {
- memcpy(q, start, p - start);
- q += p - start;
- *q++ = '/';
- }
- strcpy(q, name);
- pathopt = NULL;
- if (*p == '%') {
- pathopt = ++p;
- while (*p && *p != ':') p++;
- }
- if (*p == ':')
- *path = p + 1;
- else
- *path = NULL;
- return stalloc(len);
-}
-
-
/*** Command hashing code ***/
static void
struct strlist *sp;
sp = stalloc(sizeof(*sp));
- sp->text = sstrdup(name);
+ sp->text = ststrdup(name);
*exparg.lastp = sp;
exparg.lastp = &sp->next;
}
* Read a character from the script, returning PEOF on end of file.
* Nul characters in the input are silently discarded.
*/
-
-
+static int preadbuffer(void);
#define pgetc_as_macro() (--parsenleft >= 0? SC2INT(*parsenextc++) : preadbuffer())
#if ENABLE_ASH_OPTIMIZE_FOR_SIZE
}
#endif
-
/*
* Same as pgetc(), but ignores PEOA.
*/
#if ENABLE_ASH_ALIAS
-static int pgetc2(void)
+static int
+pgetc2(void)
{
int c;
return c;
}
#else
-static int pgetc2(void)
+static int
+pgetc2(void)
{
return pgetc_macro();
}
/*
* Read a line from the script.
*/
-
-static char * pfgets(char *line, int len)
+static char *
+pfgets(char *line, int len)
{
char *p = line;
int nleft = len;
return line;
}
-
-#if ENABLE_FEATURE_EDITING
-static line_input_t *line_input_state;
-//static SKIP_ASH_EXPAND_PRMT(const) char *cmdedit_prompt;
-static const char *cmdedit_prompt;
-static void putprompt(const char *s)
-{
- if (ENABLE_ASH_EXPAND_PRMT) {
- free((char*)cmdedit_prompt);
- cmdedit_prompt = xstrdup(s);
- return;
- }
- cmdedit_prompt = s;
-}
-#else
-static void putprompt(const char *s)
-{
- out2str(s);
-}
-#endif
-
#if ENABLE_FEATURE_EDITING_VI
#define setvimode(on) do { \
if (on) line_input_state->flags |= VI_MODE; \
#define setvimode(on) viflag = 0 /* forcibly keep the option off */
#endif
-
-static int preadfd(void)
+static int
+preadfd(void)
{
int nr;
char *buf = parsefile->buf;
{
const char *mpath;
char *p;
- char *q;
- time_t *mtp;
- struct stackmark smark;
- struct stat statb;
-
- setstackmark(&smark);
- mpath = mpathset() ? mpathval() : mailval();
- for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
- p = padvance(&mpath, nullstr);
- if (p == NULL)
- break;
- if (*p == '\0')
- continue;
- for (q = p; *q; q++);
-#if DEBUG
- if (q[-1] != '/')
- abort();
-#endif
- q[-1] = '\0'; /* delete trailing '/' */
- if (stat(p, &statb) < 0) {
- *mtp = 0;
- continue;
- }
- if (!mail_var_path_changed && statb.st_mtime != *mtp) {
- fprintf(
- stderr, snlfmt,
- pathopt ? pathopt : "you have mail"
- );
- }
- *mtp = statb.st_mtime;
- }
- mail_var_path_changed = 0;
- popstackmark(&smark);
-}
-
-
-static void
-changemail(const char *val)
-{
- mail_var_path_changed++;
-}
-
-#endif /* ASH_MAIL */
-
-/*
- * Take commands from a file. To be compatible we should do a path
- * search for the file, which is necessary to find sub-commands.
- */
-static char *
-find_dot_file(char *name)
-{
- char *fullname;
- const char *path = pathval();
- struct stat statb;
-
- /* don't try this for absolute or relative paths */
- if (strchr(name, '/'))
- return name;
-
- while ((fullname = padvance(&path, name)) != 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 */
- ash_msg_and_raise_error("%s: not found", name);
- /* NOTREACHED */
-}
-
-/* mystring.c */
-
-/*
- * String functions.
- *
- * number(s) Convert a string of digits to an integer.
- * is_number(s) Return true if s is a string of digits.
- */
-
-/*
- * prefix -- see if pfx is a prefix of string.
- */
-static char *
-prefix(const char *string, const char *pfx)
-{
- while (*pfx) {
- if (*pfx++ != *string++)
- return 0;
- }
- return (char *) string;
-}
-
-
-/*
- * Convert a string of digits to an integer, printing an error message on
- * failure.
- */
-static int
-number(const char *s)
-{
- if (!is_number(s))
- ash_msg_and_raise_error(illnum, s);
- return atoi(s);
-}
-
-
-/*
- * Check for a valid number. This should be elsewhere.
- */
-static int
-is_number(const char *p)
-{
- do {
- if (!is_digit(*p))
- return 0;
- } while (*++p != '\0');
- return 1;
-}
-
-
-/*
- * Produce a possibly single quoted string suitable as input to the shell.
- * The return string is allocated on the stack.
- */
-static char *
-single_quote(const char *s)
-{
- char *p;
-
- STARTSTACKSTR(p);
-
- do {
- char *q;
- size_t len;
-
- len = strchrnul(s, '\'') - s;
-
- q = p = makestrspace(len + 3, p);
-
- *q++ = '\'';
- q = memcpy(q, s, len) + len;
- *q++ = '\'';
- s += len;
-
- STADJUST(q - p, p);
+ char *q;
+ time_t *mtp;
+ struct stackmark smark;
+ struct stat statb;
- len = strspn(s, "'");
- if (!len)
+ setstackmark(&smark);
+ mpath = mpathset() ? mpathval() : mailval();
+ for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) {
+ p = padvance(&mpath, nullstr);
+ if (p == NULL)
break;
+ if (*p == '\0')
+ continue;
+ for (q = p; *q; q++);
+#if DEBUG
+ if (q[-1] != '/')
+ abort();
+#endif
+ q[-1] = '\0'; /* delete trailing '/' */
+ if (stat(p, &statb) < 0) {
+ *mtp = 0;
+ continue;
+ }
+ if (!mail_var_path_changed && statb.st_mtime != *mtp) {
+ fprintf(
+ stderr, snlfmt,
+ pathopt ? pathopt : "you have mail"
+ );
+ }
+ *mtp = statb.st_mtime;
+ }
+ mail_var_path_changed = 0;
+ popstackmark(&smark);
+}
- q = p = makestrspace(len + 3, p);
-
- *q++ = '"';
- q = memcpy(q, s, len) + len;
- *q++ = '"';
- s += len;
-
- STADJUST(q - p, p);
- } while (*s);
-
- USTPUTC(0, p);
- return stackblock();
+static void
+changemail(const char *val)
+{
+ mail_var_path_changed++;
}
+#endif /* ASH_MAIL */
/*
- * Like strdup but works with the ash stack.
+ * Take commands from a file. To be compatible we should do a path
+ * search for the file, which is necessary to find sub-commands.
*/
static char *
-sstrdup(const char *p)
+find_dot_file(char *name)
{
- size_t len = strlen(p) + 1;
- return memcpy(stalloc(len), p, len);
-}
+ char *fullname;
+ const char *path = pathval();
+ struct stat statb;
+
+ /* don't try this for absolute or relative paths */
+ if (strchr(name, '/'))
+ return name;
+
+ while ((fullname = padvance(&path, name)) != 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 */
+ ash_msg_and_raise_error("%s: not found", name);
+ /* NOTREACHED */
+}
static void
calcsize(union node *n)
return new;
}
-
-static struct nodelist *
-copynodelist(struct nodelist *lp)
-{
- struct nodelist *start;
- struct nodelist **lpp;
-
- lpp = &start;
- while (lp) {
- *lpp = funcblock;
- funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
- (*lpp)->n = copynode(lp->n);
- lp = lp->next;
- lpp = &(*lpp)->next;
- }
- *lpp = NULL;
- return start;
-}
-
-
-static char *
-nodeckstrdup(char *s)
-{
- char *rtn = funcstring;
-
- strcpy(funcstring, s);
- funcstring += strlen(s) + 1;
- return rtn;
-}
-
-
-/*
- * Free a parse tree.
- */
-static void
-freefunc(struct funcnode *f)
-{
- if (f && --f->count < 0)
- free(f);
-}
-
-
-static void setoption(int, int);
-
-
-static void
-optschanged(void)
-{
-#if DEBUG
- opentrace();
-#endif
- setinteractive(iflag);
- setjobctl(mflag);
- setvimode(viflag);
-}
-
-static void minus_o(char *name, int val)
-{
- int i;
-
- if (name) {
- for (i = 0; i < NOPTS; i++) {
- if (equal(name, optnames(i))) {
- optlist[i] = val;
- return;
- }
- }
- ash_msg_and_raise_error("Illegal option -o %s", name);
- }
- out1str("Current option settings\n");
- for (i = 0; i < NOPTS; i++)
- out1fmt("%-16s%s\n", optnames(i),
- optlist[i] ? "on" : "off");
-}
-
-
-/*
- * Process shell options. The global variable argptr contains a pointer
- * to the argument list; we advance it past the options.
- */
-static void
-options(int cmdline)
-{
- char *p;
- int val;
- int c;
-
- if (cmdline)
- minusc = NULL;
- while ((p = *argptr) != NULL) {
- argptr++;
- c = *p++;
- if (c == '-') {
- val = 1;
- if (p[0] == '\0' || LONE_DASH(p)) {
- 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) {
- minusc = p; /* command is after shell args*/
- } else if (c == 'o') {
- minus_o(*argptr, val);
- if (*argptr)
- argptr++;
- } else if (cmdline && (c == '-')) { // long options
- if (strcmp(p, "login") == 0)
- isloginsh = 1;
- break;
- } else {
- setoption(c, val);
- }
- }
- }
-}
-
-
-static void
-setoption(int flag, int val)
-{
- int i;
-
- for (i = 0; i < NOPTS; i++) {
- if (optletters(i) == flag) {
- optlist[i] = val;
- return;
- }
- }
- ash_msg_and_raise_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++ = ckstrdup(*argv++);
- }
- *ap = NULL;
- freeparam(&shellparam);
- shellparam.malloc = 1;
- shellparam.nparam = nparam;
- shellparam.p = newparam;
-#if ENABLE_ASH_GETOPTS
- shellparam.optind = 1;
- shellparam.optoff = -1;
-#endif
-}
-
-
-/*
- * Free the list of positional parameters.
- */
-static void
-freeparam(volatile struct shparam *param)
+
+static struct nodelist *
+copynodelist(struct nodelist *lp)
{
- char **ap;
+ struct nodelist *start;
+ struct nodelist **lpp;
- if (param->malloc) {
- for (ap = param->p; *ap; ap++)
- free(*ap);
- free(param->p);
+ lpp = &start;
+ while (lp) {
+ *lpp = funcblock;
+ funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
+ (*lpp)->n = copynode(lp->n);
+ lp = lp->next;
+ lpp = &(*lpp)->next;
}
+ *lpp = NULL;
+ return start;
}
-/*
- * The shift builtin command.
- */
-static int
-shiftcmd(int argc, char **argv)
+static char *
+nodeckstrdup(char *s)
{
- int n;
- char **ap1, **ap2;
+ char *rtn = funcstring;
- n = 1;
- if (argc > 1)
- n = number(argv[1]);
- if (n > shellparam.nparam)
- ash_msg_and_raise_error("can't shift that many");
- INT_OFF;
- shellparam.nparam -= n;
- for (ap1 = shellparam.p; --n >= 0; ap1++) {
- if (shellparam.malloc)
- free(*ap1);
- }
- ap2 = shellparam.p;
- while ((*ap2++ = *ap1++) != NULL);
-#if ENABLE_ASH_GETOPTS
- shellparam.optind = 1;
- shellparam.optoff = -1;
-#endif
- INT_ON;
- return 0;
+ strcpy(funcstring, s);
+ funcstring += strlen(s) + 1;
+ return rtn;
}
/*
- * The set command builtin.
+ * Free a parse tree.
*/
-static int
-setcmd(int argc, char **argv)
+static void
+freefunc(struct funcnode *f)
{
- if (argc == 1)
- return showvars(nullstr, 0, VUNSET);
- INT_OFF;
- options(0);
- optschanged();
- if (*argptr != NULL) {
- setparam(argptr);
- }
- INT_ON;
- return 0;
+ if (f && --f->count < 0)
+ free(f);
}
-#if ENABLE_ASH_GETOPTS
static void
-getoptsreset(const char *value)
+optschanged(void)
{
- shellparam.optind = number(value);
- shellparam.optoff = -1;
-}
+#if DEBUG
+ opentrace();
#endif
-
-#if ENABLE_LOCALE_SUPPORT
-static void change_lc_all(const char *value)
-{
- if (value && *value != '\0')
- setlocale(LC_ALL, value);
-}
-
-static void change_lc_ctype(const char *value)
-{
- if (value && *value != '\0')
- setlocale(LC_CTYPE, value);
+ setinteractive(iflag);
+ setjobctl(mflag);
+ setvimode(viflag);
}
-#endif
-
-#if ENABLE_ASH_RANDOM_SUPPORT
-/* Roughly copied from bash.. */
-static void change_random(const char *value)
+static void
+minus_o(char *name, int val)
{
- if (value == NULL) {
- /* "get", generate */
- char buf[16];
+ int i;
- rseed = rseed * 1103515245 + 12345;
- sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
- /* set without recursion */
- setvar(vrandom.text, buf, VNOFUNC);
- vrandom.flags &= ~VNOFUNC;
- } else {
- /* set/reset */
- rseed = strtoul(value, (char **)NULL, 10);
+ if (name) {
+ for (i = 0; i < NOPTS; i++) {
+ if (equal(name, optnames(i))) {
+ optlist[i] = val;
+ return;
+ }
+ }
+ ash_msg_and_raise_error("Illegal option -o %s", name);
}
+ out1str("Current option settings\n");
+ for (i = 0; i < NOPTS; i++)
+ out1fmt("%-16s%s\n", optnames(i),
+ optlist[i] ? "on" : "off");
}
-#endif
-#if ENABLE_ASH_GETOPTS
-static int
-getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
+static void
+setoption(int flag, int val)
{
- char *p, *q;
- char c = '?';
- int done = 0;
- int err = 0;
- char s[12];
- char **optnext;
-
- if (*param_optind < 1)
- return 1;
- optnext = optfirst + *param_optind - 1;
-
- if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
- p = NULL;
- else
- p = optnext[-1] + *optoff;
- if (p == NULL || *p == '\0') {
- /* Current word is done, advance */
- p = *optnext;
- if (p == NULL || *p != '-' || *++p == '\0') {
- atend:
- p = NULL;
- done = 1;
- goto out;
- }
- optnext++;
- if (LONE_DASH(p)) /* 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 {
- fprintf(stderr, "Illegal option -%c\n", c);
- (void) unsetvar("OPTARG");
- }
- c = '?';
- goto out;
- }
- if (*++q == ':')
- q++;
- }
+ int i;
- if (*++q == ':') {
- if (*p == '\0' && (p = *optnext) == NULL) {
- if (optstr[0] == ':') {
- s[0] = c;
- s[1] = '\0';
- err |= setvarsafe("OPTARG", s, 0);
- c = ':';
- } else {
- fprintf(stderr, "No arg for -%c option\n", c);
- (void) unsetvar("OPTARG");
- c = '?';
- }
- goto out;
+ for (i = 0; i < NOPTS; i++) {
+ if (optletters(i) == flag) {
+ optlist[i] = val;
+ return;
}
-
- if (p == *optnext)
- optnext++;
- err |= setvarsafe("OPTARG", p, 0);
- p = NULL;
- } else
- err |= setvarsafe("OPTARG", nullstr, 0);
- out:
- *optoff = p ? p - *(optnext - 1) : -1;
- *param_optind = optnext - optfirst + 1;
- fmtstr(s, sizeof(s), "%d", *param_optind);
- err |= setvarsafe("OPTIND", s, VNOFUNC);
- s[0] = c;
- s[1] = '\0';
- err |= setvarsafe(optvar, s, 0);
- if (err) {
- *param_optind = 1;
- *optoff = -1;
- flush_stdout_stderr();
- raise_exception(EXERROR);
}
- return done;
+ ash_msg_and_raise_error("Illegal option -%c", flag);
+ /* NOTREACHED */
}
/*
- * 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.
+ * Process shell options. The global variable argptr contains a pointer
+ * to the argument list; we advance it past the options.
*/
-static int
-getoptscmd(int argc, char **argv)
+static void
+options(int cmdline)
{
- char **optbase;
+ char *p;
+ int val;
+ int c;
- if (argc < 3)
- ash_msg_and_raise_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;
+ if (cmdline)
+ minusc = NULL;
+ while ((p = *argptr) != NULL) {
+ argptr++;
+ c = *p++;
+ if (c == '-') {
+ val = 1;
+ if (p[0] == '\0' || LONE_DASH(p)) {
+ 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;
}
- } else {
- optbase = &argv[3];
- if (shellparam.optind > argc - 2) {
- shellparam.optind = 1;
- shellparam.optoff = -1;
+ while ((c = *p++) != '\0') {
+ if (c == 'c' && cmdline) {
+ minusc = p; /* command is after shell args*/
+ } else if (c == 'o') {
+ minus_o(*argptr, val);
+ if (*argptr)
+ argptr++;
+ } else if (cmdline && (c == '-')) { // long options
+ if (strcmp(p, "login") == 0)
+ isloginsh = 1;
+ break;
+ } else {
+ setoption(c, val);
+ }
}
}
-
- return getopts(argv[1], argv[2], optbase, &shellparam.optind,
- &shellparam.optoff);
}
-#endif /* ASH_GETOPTS */
+
/*
- * 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.
+ * Set the shell parameters.
*/
-static int
-nextopt(const char *optstring)
+static void
+setparam(char **argv)
{
- char *p;
- const char *q;
- char c;
+ char **newparam;
+ char **ap;
+ int nparam;
- p = optptr;
- if (p == NULL || *p == '\0') {
- p = *argptr;
- if (p == NULL || *p != '-' || *++p == '\0')
- return '\0';
- argptr++;
- if (LONE_DASH(p)) /* check for "--" */
- return '\0';
- }
- c = *p++;
- for (q = optstring; *q != c; ) {
- if (*q == '\0')
- ash_msg_and_raise_error("Illegal option -%c", c);
- if (*++q == ':')
- q++;
- }
- if (*++q == ':') {
- if (*p == '\0' && (p = *argptr++) == NULL)
- ash_msg_and_raise_error("No arg for -%c option", c);
- optionarg = p;
- p = NULL;
+ for (nparam = 0; argv[nparam]; nparam++);
+ ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
+ while (*argv) {
+ *ap++ = ckstrdup(*argv++);
}
- optptr = p;
- return c;
+ *ap = NULL;
+ freeparam(&shellparam);
+ shellparam.malloc = 1;
+ shellparam.nparam = nparam;
+ shellparam.p = newparam;
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
}
-/* parser.c */
-
-
/*
- * Shell command parser.
+ * Free the list of positional parameters.
*/
-
-#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 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 union node *makename(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 firstc, int syntax, char *eofmark, int striptabs);
-static int noexpand(char *);
-static void setprompt(int);
-
-static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
static void
-raise_error_syntax(const char *msg)
+freeparam(volatile struct shparam *param)
{
- ash_msg_and_raise_error("Syntax error: %s", msg);
- /* NOTREACHED */
+ char **ap;
+
+ if (param->malloc) {
+ for (ap = param->p; *ap; ap++)
+ free(*ap);
+ free(param->p);
+ }
}
+
/*
- * 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.
+ * The shift builtin command.
*/
-static void raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
-static void
-raise_error_unexpected_syntax(int token)
+static int
+shiftcmd(int argc, char **argv)
{
- char msg[64];
- int l;
+ int n;
+ char **ap1, **ap2;
- l = sprintf(msg, "%s unexpected", tokname(lasttoken));
- if (token >= 0)
- sprintf(msg + l, " (expecting %s)", tokname(token));
- raise_error_syntax(msg);
- /* NOTREACHED */
+ n = 1;
+ if (argc > 1)
+ n = number(argv[1]);
+ if (n > shellparam.nparam)
+ ash_msg_and_raise_error("can't shift that many");
+ INT_OFF;
+ shellparam.nparam -= n;
+ for (ap1 = shellparam.p; --n >= 0; ap1++) {
+ if (shellparam.malloc)
+ free(*ap1);
+ }
+ ap2 = shellparam.p;
+ while ((*ap2++ = *ap1++) != NULL);
+#if ENABLE_ASH_GETOPTS
+ shellparam.optind = 1;
+ shellparam.optoff = -1;
+#endif
+ INT_ON;
+ return 0;
}
+
/*
- * Read and parse a command. Returns NEOF on end of file. (NULL is a
- * valid parse tree indicating a blank line.)
+ * POSIX requires that 'set' (but not export or readonly) output the
+ * variables in lexicographic order - by the locale's collating order (sigh).
+ * Maybe we could keep them in an ordered balanced binary tree
+ * instead of hashed lists.
+ * For now just roll 'em through qsort for printing...
*/
-static union node *
-parsecmd(int interact)
+static int
+showvars(const char *sep_prefix, int on, int off)
{
- int t;
+ const char *sep;
+ char **ep, **epend;
- tokpushback = 0;
- doprompt = interact;
- if (doprompt)
- setprompt(doprompt);
- needprompt = 0;
- t = readtoken();
- if (t == TEOF)
- return NEOF;
- if (t == TNL)
- return NULL;
- tokpushback++;
- return list(1);
-}
+ ep = listvars(on, off, &epend);
+ qsort(ep, epend - ep, sizeof(char *), vpcmp);
+ sep = *sep_prefix ? spcstr : sep_prefix;
-static union node *
-list(int nlflag)
-{
- union node *n1, *n2, *n3;
- int tok;
+ for (; ep < epend; ep++) {
+ const char *p;
+ const char *q;
- checkkwd = CHKNL | CHKKWD | CHKALIAS;
- if (nlflag == 2 && peektoken())
- return NULL;
- n1 = NULL;
- for (;;) {
- n2 = andor();
- tok = readtoken();
- if (tok == TBACKGND) {
- if (n2->type == NPIPE) {
- n2->npipe.backgnd = 1;
- } else {
- if (n2->type != NREDIR) {
- n3 = stalloc(sizeof(struct nredir));
- n3->nredir.n = n2;
- n3->nredir.redirect = NULL;
- n2 = n3;
- }
- n2->type = NBACKGND;
- }
- }
- if (n1 == NULL) {
- n1 = n2;
- } else {
- n3 = 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 == 1)
- return n1;
- } else {
- tokpushback++;
- }
- checkkwd = CHKNL | CHKKWD | CHKALIAS;
- if (peektoken())
- return n1;
- break;
- case TEOF:
- if (heredoclist)
- parseheredoc();
- else
- pungetc(); /* push back EOF on input */
- return n1;
- default:
- if (nlflag == 1)
- raise_error_unexpected_syntax(-1);
- tokpushback++;
- return n1;
- }
+ p = strchrnul(*ep, '=');
+ q = nullstr;
+ if (*p)
+ q = single_quote(++p);
+ out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
+ }
+ return 0;
+}
+
+/*
+ * The set command builtin.
+ */
+static int
+setcmd(int argc, char **argv)
+{
+ if (argc == 1)
+ return showvars(nullstr, 0, VUNSET);
+ INT_OFF;
+ options(0);
+ optschanged();
+ if (*argptr != NULL) {
+ setparam(argptr);
}
+ INT_ON;
+ return 0;
}
-static union node *
-andor(void)
+#if ENABLE_LOCALE_SUPPORT
+static void change_lc_all(const char *value)
{
- union node *n1, *n2, *n3;
- int t;
-
- n1 = pipeline();
- for (;;) {
- t = readtoken();
- if (t == TAND) {
- t = NAND;
- } else if (t == TOR) {
- t = NOR;
- } else {
- tokpushback++;
- return n1;
- }
- checkkwd = CHKNL | CHKKWD | CHKALIAS;
- n2 = pipeline();
- n3 = stalloc(sizeof(struct nbinary));
- n3->type = t;
- n3->nbinary.ch1 = n1;
- n3->nbinary.ch2 = n2;
- n1 = n3;
- }
+ if (value && *value != '\0')
+ setlocale(LC_ALL, value);
}
+static void change_lc_ctype(const char *value)
+{
+ if (value && *value != '\0')
+ setlocale(LC_CTYPE, value);
+}
+#endif
-static union node *
-pipeline(void)
+#if ENABLE_ASH_RANDOM_SUPPORT
+/* Roughly copied from bash.. */
+static void change_random(const char *value)
{
- union node *n1, *n2, *pipenode;
- struct nodelist *lp, *prev;
- int negate;
+ if (value == NULL) {
+ /* "get", generate */
+ char buf[16];
- negate = 0;
- TRACE(("pipeline: entered\n"));
- if (readtoken() == TNOT) {
- negate = !negate;
- checkkwd = CHKKWD | CHKALIAS;
- } else
- tokpushback++;
- n1 = command();
- if (readtoken() == TPIPE) {
- pipenode = stalloc(sizeof(struct npipe));
- pipenode->type = NPIPE;
- pipenode->npipe.backgnd = 0;
- lp = stalloc(sizeof(struct nodelist));
- pipenode->npipe.cmdlist = lp;
- lp->n = n1;
- do {
- prev = lp;
- lp = stalloc(sizeof(struct nodelist));
- checkkwd = CHKNL | CHKKWD | CHKALIAS;
- lp->n = command();
- prev->next = lp;
- } while (readtoken() == TPIPE);
- lp->next = NULL;
- n1 = pipenode;
- }
- tokpushback++;
- if (negate) {
- n2 = stalloc(sizeof(struct nnot));
- n2->type = NNOT;
- n2->nnot.com = n1;
- return n2;
+ rseed = rseed * 1103515245 + 12345;
+ sprintf(buf, "%d", (unsigned int)((rseed & 32767)));
+ /* set without recursion */
+ setvar(vrandom.text, buf, VNOFUNC);
+ vrandom.flags &= ~VNOFUNC;
+ } else {
+ /* set/reset */
+ rseed = strtoul(value, (char **)NULL, 10);
}
- return n1;
}
+#endif
-static union node *
-command(void)
+#if ENABLE_ASH_GETOPTS
+static int
+getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *optoff)
{
- union node *n1, *n2;
- union node *ap, **app;
- union node *cp, **cpp;
- union node *redir, **rpp;
- union node **rpp2;
- int t;
-
- redir = NULL;
- rpp2 = &redir;
+ char *p, *q;
+ char c = '?';
+ int done = 0;
+ int err = 0;
+ char s[12];
+ char **optnext;
- switch (readtoken()) {
- default:
- raise_error_unexpected_syntax(-1);
- /* NOTREACHED */
- case TIF:
- n1 = stalloc(sizeof(struct nif));
- n1->type = NIF;
- n1->nif.test = list(0);
- if (readtoken() != TTHEN)
- raise_error_unexpected_syntax(TTHEN);
- n1->nif.ifpart = list(0);
- n2 = n1;
- while (readtoken() == TELIF) {
- n2->nif.elsepart = stalloc(sizeof(struct nif));
- n2 = n2->nif.elsepart;
- n2->type = NIF;
- n2->nif.test = list(0);
- if (readtoken() != TTHEN)
- raise_error_unexpected_syntax(TTHEN);
- n2->nif.ifpart = list(0);
- }
- if (lasttoken == TELSE)
- n2->nif.elsepart = list(0);
- else {
- n2->nif.elsepart = NULL;
- tokpushback++;
- }
- t = TFI;
- break;
- case TWHILE:
- case TUNTIL: {
- int got;
- n1 = 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 : ""));
- raise_error_unexpected_syntax(TDO);
- }
- n1->nbinary.ch2 = list(0);
- t = TDONE;
- break;
- }
- case TFOR:
- if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
- raise_error_syntax("Bad for loop variable");
- n1 = stalloc(sizeof(struct nfor));
- n1->type = NFOR;
- n1->nfor.var = wordtext;
- checkkwd = CHKKWD | CHKALIAS;
- if (readtoken() == TIN) {
- app = ≈
- while (readtoken() == TWORD) {
- n2 = 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)
- raise_error_unexpected_syntax(-1);
- } else {
- n2 = stalloc(sizeof(struct narg));
- n2->type = NARG;
- n2->narg.text = (char *)dolatstr;
- 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 = CHKNL | CHKKWD | CHKALIAS;
- if (readtoken() != TDO)
- raise_error_unexpected_syntax(TDO);
- n1->nfor.body = list(0);
- t = TDONE;
- break;
- case TCASE:
- n1 = stalloc(sizeof(struct ncase));
- n1->type = NCASE;
- if (readtoken() != TWORD)
- raise_error_unexpected_syntax(TWORD);
- n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
- n2->type = NARG;
- n2->narg.text = wordtext;
- n2->narg.backquote = backquotelist;
- n2->narg.next = NULL;
- do {
- checkkwd = CHKKWD | CHKALIAS;
- } while (readtoken() == TNL);
- if (lasttoken != TIN)
- raise_error_unexpected_syntax(TIN);
- cpp = &n1->ncase.cases;
- next_case:
- checkkwd = CHKNL | CHKKWD;
- t = readtoken();
- while (t != TESAC) {
- if (lasttoken == TLP)
- readtoken();
- *cpp = cp = stalloc(sizeof(struct nclist));
- cp->type = NCLIST;
- app = &cp->nclist.pattern;
- for (;;) {
- *app = ap = stalloc(sizeof(struct narg));
- ap->type = NARG;
- ap->narg.text = wordtext;
- ap->narg.backquote = backquotelist;
- if (readtoken() != TPIPE)
- break;
- app = &ap->narg.next;
- readtoken();
- }
- ap->narg.next = NULL;
- if (lasttoken != TRP)
- raise_error_unexpected_syntax(TRP);
- cp->nclist.body = list(2);
+ if (*param_optind < 1)
+ return 1;
+ optnext = optfirst + *param_optind - 1;
- cpp = &cp->nclist.next;
+ if (*param_optind <= 1 || *optoff < 0 || strlen(optnext[-1]) < *optoff)
+ p = NULL;
+ else
+ p = optnext[-1] + *optoff;
+ if (p == NULL || *p == '\0') {
+ /* Current word is done, advance */
+ p = *optnext;
+ if (p == NULL || *p != '-' || *++p == '\0') {
+ atend:
+ p = NULL;
+ done = 1;
+ goto out;
+ }
+ optnext++;
+ if (LONE_DASH(p)) /* check for "--" */
+ goto atend;
+ }
- checkkwd = CHKNL | CHKKWD;
- t = readtoken();
- if (t != TESAC) {
- if (t != TENDCASE)
- raise_error_unexpected_syntax(TENDCASE);
- goto next_case;
+ 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 {
+ fprintf(stderr, "Illegal option -%c\n", c);
+ unsetvar("OPTARG");
}
+ c = '?';
+ goto out;
}
- *cpp = NULL;
- goto redir;
- case TLP:
- n1 = stalloc(sizeof(struct nredir));
- n1->type = NSUBSHELL;
- n1->nredir.n = list(0);
- n1->nredir.redirect = NULL;
- t = TRP;
- break;
- case TBEGIN:
- n1 = list(0);
- t = TEND;
- break;
- case TWORD:
- case TREDIR:
- tokpushback++;
- return simplecmd();
+ if (*++q == ':')
+ q++;
}
- if (readtoken() != t)
- raise_error_unexpected_syntax(t);
+ if (*++q == ':') {
+ if (*p == '\0' && (p = *optnext) == NULL) {
+ if (optstr[0] == ':') {
+ s[0] = c;
+ s[1] = '\0';
+ err |= setvarsafe("OPTARG", s, 0);
+ c = ':';
+ } else {
+ fprintf(stderr, "No arg for -%c option\n", c);
+ unsetvar("OPTARG");
+ c = '?';
+ }
+ goto out;
+ }
- redir:
- /* Now check for redirection which may follow command */
- checkkwd = CHKKWD | CHKALIAS;
- rpp = rpp2;
- while (readtoken() == TREDIR) {
- *rpp = n2 = redirnode;
- rpp = &n2->nfile.next;
- parsefname();
+ if (p == *optnext)
+ optnext++;
+ err |= setvarsafe("OPTARG", p, 0);
+ p = NULL;
+ } else
+ err |= setvarsafe("OPTARG", nullstr, 0);
+ out:
+ *optoff = p ? p - *(optnext - 1) : -1;
+ *param_optind = optnext - optfirst + 1;
+ fmtstr(s, sizeof(s), "%d", *param_optind);
+ err |= setvarsafe("OPTIND", s, VNOFUNC);
+ s[0] = c;
+ s[1] = '\0';
+ err |= setvarsafe(optvar, s, 0);
+ if (err) {
+ *param_optind = 1;
+ *optoff = -1;
+ flush_stdout_stderr();
+ raise_exception(EXERROR);
}
- tokpushback++;
- *rpp = NULL;
- if (redir) {
- if (n1->type != NSUBSHELL) {
- n2 = stalloc(sizeof(struct nredir));
- n2->type = NREDIR;
- n2->nredir.n = n1;
- n1 = n2;
+ return done;
+}
+
+
+/*
+ * 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(int argc, char **argv)
+{
+ char **optbase;
+
+ if (argc < 3)
+ ash_msg_and_raise_error("Usage: getopts optstring var [arg]");
+ 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;
}
- n1->nredir.redirect = redir;
}
- return n1;
+
+ return getopts(argv[1], argv[2], optbase, &shellparam.optind,
+ &shellparam.optoff);
}
+#endif /* ASH_GETOPTS */
-static union node *
-simplecmd(void)
+/* ============ Shell parser */
+
+static void raise_error_syntax(const char *) ATTRIBUTE_NORETURN;
+static void
+raise_error_syntax(const char *msg)
{
- union node *args, **app;
- union node *n = NULL;
- union node *vars, **vpp;
- union node **rpp, *redir;
- int savecheckkwd;
+ ash_msg_and_raise_error("Syntax error: %s", msg);
+ /* NOTREACHED */
+}
- args = NULL;
- app = &args;
- vars = NULL;
- vpp = &vars;
- redir = NULL;
- rpp = &redir;
+/*
+ * 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 raise_error_unexpected_syntax(int) ATTRIBUTE_NORETURN;
+static void
+raise_error_unexpected_syntax(int token)
+{
+ char msg[64];
+ int l;
- savecheckkwd = CHKALIAS;
+ l = sprintf(msg, "%s unexpected", tokname(lasttoken));
+ if (token >= 0)
+ sprintf(msg + l, " (expecting %s)", tokname(token));
+ raise_error_syntax(msg);
+ /* NOTREACHED */
+}
+
+#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 */
+
+/* parsing is heavily cross-recursive, need these forward decls */
+static union node *andor(void);
+static union node *pipeline(void);
+static union node *parse_command(void);
+static void parseheredoc(void);
+static char peektoken(void);
+static int readtoken(void);
+
+static union node *
+list(int nlflag)
+{
+ union node *n1, *n2, *n3;
+ int tok;
+
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ if (nlflag == 2 && peektoken())
+ return NULL;
+ n1 = NULL;
for (;;) {
- checkkwd = savecheckkwd;
- switch (readtoken()) {
- case TWORD:
- n = stalloc(sizeof(struct narg));
- n->type = NARG;
- n->narg.text = wordtext;
- n->narg.backquote = backquotelist;
- if (savecheckkwd && isassignment(wordtext)) {
- *vpp = n;
- vpp = &n->narg.next;
+ n2 = andor();
+ tok = readtoken();
+ if (tok == TBACKGND) {
+ if (n2->type == NPIPE) {
+ n2->npipe.backgnd = 1;
} else {
- *app = n;
- app = &n->narg.next;
- savecheckkwd = 0;
- }
- 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
- ) {
- struct builtincmd *bcmd;
- const char *name;
-
- /* We have a function */
- if (readtoken() != TRP)
- raise_error_unexpected_syntax(TRP);
- name = n->narg.text;
- if (!goodname(name)
- || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
- ) {
- raise_error_syntax("Bad function name");
+ if (n2->type != NREDIR) {
+ n3 = stalloc(sizeof(struct nredir));
+ n3->nredir.n = n2;
+ n3->nredir.redirect = NULL;
+ n2 = n3;
}
- n->type = NDEFUN;
- checkkwd = CHKNL | CHKKWD | CHKALIAS;
- n->narg.next = command();
- return n;
+ n2->type = NBACKGND;
+ }
+ }
+ if (n1 == NULL) {
+ n1 = n2;
+ } else {
+ n3 = 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 == 1)
+ return n1;
+ } else {
+ tokpushback++;
}
- /* fall through */
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ if (peektoken())
+ return n1;
+ break;
+ case TEOF:
+ if (heredoclist)
+ parseheredoc();
+ else
+ pungetc(); /* push back EOF on input */
+ return n1;
default:
+ if (nlflag == 1)
+ raise_error_unexpected_syntax(-1);
tokpushback++;
- goto out;
+ return n1;
}
}
- out:
- *app = NULL;
- *vpp = NULL;
- *rpp = NULL;
- n = stalloc(sizeof(struct ncmd));
- n->type = NCMD;
- n->ncmd.args = args;
- n->ncmd.assign = vars;
- n->ncmd.redirect = redir;
- return n;
+}
+
+static union node *
+andor(void)
+{
+ union node *n1, *n2, *n3;
+ int t;
+
+ n1 = pipeline();
+ for (;;) {
+ t = readtoken();
+ if (t == TAND) {
+ t = NAND;
+ } else if (t == TOR) {
+ t = NOR;
+ } else {
+ tokpushback++;
+ return n1;
+ }
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n2 = pipeline();
+ n3 = stalloc(sizeof(struct nbinary));
+ n3->type = t;
+ n3->nbinary.ch1 = n1;
+ n3->nbinary.ch2 = n2;
+ n1 = n3;
+ }
+}
+
+static union node *
+pipeline(void)
+{
+ union node *n1, *n2, *pipenode;
+ struct nodelist *lp, *prev;
+ int negate;
+
+ negate = 0;
+ TRACE(("pipeline: entered\n"));
+ if (readtoken() == TNOT) {
+ negate = !negate;
+ checkkwd = CHKKWD | CHKALIAS;
+ } else
+ tokpushback++;
+ n1 = parse_command();
+ if (readtoken() == TPIPE) {
+ pipenode = stalloc(sizeof(struct npipe));
+ pipenode->type = NPIPE;
+ pipenode->npipe.backgnd = 0;
+ lp = stalloc(sizeof(struct nodelist));
+ pipenode->npipe.cmdlist = lp;
+ lp->n = n1;
+ do {
+ prev = lp;
+ lp = stalloc(sizeof(struct nodelist));
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ lp->n = parse_command();
+ prev->next = lp;
+ } while (readtoken() == TPIPE);
+ lp->next = NULL;
+ n1 = pipenode;
+ }
+ tokpushback++;
+ if (negate) {
+ n2 = stalloc(sizeof(struct nnot));
+ n2->type = NNOT;
+ n2->nnot.com = n1;
+ return n2;
+ }
+ return n1;
}
static union node *
if (!err)
n->ndup.vname = NULL;
- if (is_digit(text[0]) && text[1] == '\0')
+ if (isdigit(text[0]) && text[1] == '\0')
n->ndup.dupfd = digit_val(text[0]);
else if (LONE_DASH(text))
n->ndup.dupfd = -1;
}
}
+/*
+ * Returns true if the text contains nothing to expand (no dollar signs
+ * or backquotes).
+ */
+static int
+noexpand(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;
+}
static void
parsefname(void)
}
}
-
-/*
- * Input any here documents.
- */
-static void
-parseheredoc(void)
-{
- struct heredoc *here;
- union node *n;
-
- here = heredoclist;
- heredoclist = 0;
-
- while (here) {
- if (needprompt) {
- setprompt(2);
- }
- readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
- here->eofmark, here->striptabs);
- n = 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;
- here = here->next;
- }
-}
-
-static char
-peektoken(void)
-{
- int t;
-
- t = readtoken();
- tokpushback++;
- return tokname_array[t][0];
-}
-
-static int
-readtoken(void)
+static union node *
+simplecmd(void)
{
- int t;
-#if DEBUG
- int alreadyseen = tokpushback;
-#endif
-
-#if ENABLE_ASH_ALIAS
- top:
-#endif
-
- t = xxreadtoken();
-
- /*
- * eat newlines
- */
- if (checkkwd & CHKNL) {
- while (t == TNL) {
- parseheredoc();
- t = xxreadtoken();
- }
- }
-
- if (t != TWORD || quoteflag) {
- goto out;
- }
+ union node *args, **app;
+ union node *n = NULL;
+ union node *vars, **vpp;
+ union node **rpp, *redir;
+ int savecheckkwd;
- /*
- * check for keywords
- */
- if (checkkwd & CHKKWD) {
- const char *const *pp;
+ args = NULL;
+ app = &args;
+ vars = NULL;
+ vpp = &vars;
+ redir = NULL;
+ rpp = &redir;
- pp = findkwd(wordtext);
- if (pp) {
- lasttoken = t = pp - tokname_array;
- TRACE(("keyword %s recognized\n", tokname(t)));
- goto out;
- }
- }
+ savecheckkwd = CHKALIAS;
+ for (;;) {
+ checkkwd = savecheckkwd;
+ switch (readtoken()) {
+ case TWORD:
+ n = stalloc(sizeof(struct narg));
+ n->type = NARG;
+ n->narg.text = wordtext;
+ n->narg.backquote = backquotelist;
+ if (savecheckkwd && isassignment(wordtext)) {
+ *vpp = n;
+ vpp = &n->narg.next;
+ } else {
+ *app = n;
+ app = &n->narg.next;
+ savecheckkwd = 0;
+ }
+ 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
+ ) {
+ struct builtincmd *bcmd;
+ const char *name;
- if (checkkwd & CHKALIAS) {
-#if ENABLE_ASH_ALIAS
- struct alias *ap;
- ap = lookupalias(wordtext, 1);
- if (ap != NULL) {
- if (*ap->val) {
- pushstring(ap->val, ap);
+ /* We have a function */
+ if (readtoken() != TRP)
+ raise_error_unexpected_syntax(TRP);
+ name = n->narg.text;
+ if (!goodname(name)
+ || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
+ ) {
+ raise_error_syntax("Bad function name");
+ }
+ n->type = NDEFUN;
+ checkkwd = CHKNL | CHKKWD | CHKALIAS;
+ n->narg.next = parse_command();
+ return n;
}
- goto top;
+ /* fall through */
+ default:
+ tokpushback++;
+ goto out;
}
-#endif
}
out:
- checkkwd = 0;
-#if DEBUG
- if (!alreadyseen)
- TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
- else
- TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
-#endif
- return t;
+ *app = NULL;
+ *vpp = NULL;
+ *rpp = NULL;
+ n = stalloc(sizeof(struct ncmd));
+ n->type = NCMD;
+ n->ncmd.args = args;
+ n->ncmd.assign = vars;
+ n->ncmd.redirect = redir;
+ return n;
}
-
-/*
- * 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
-/* singles must be first! */
-static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
-
-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(void)
+static union node *
+parse_command(void)
{
- int c;
+ union node *n1, *n2;
+ union node *ap, **app;
+ union node *cp, **cpp;
+ union node *redir, **rpp;
+ union node **rpp2;
+ int t;
- if (tokpushback) {
- tokpushback = 0;
- return lasttoken;
- }
- if (needprompt) {
- setprompt(2);
- }
- startlinno = plinno;
- for (;;) { /* until token or start of word found */
- c = pgetc_macro();
+ redir = NULL;
+ rpp2 = &redir;
- if ((c != ' ') && (c != '\t')
-#if ENABLE_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;
- if (doprompt)
- setprompt(2);
- } else {
- const char *p
- = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
+ switch (readtoken()) {
+ default:
+ raise_error_unexpected_syntax(-1);
+ /* NOTREACHED */
+ case TIF:
+ n1 = stalloc(sizeof(struct nif));
+ n1->type = NIF;
+ n1->nif.test = list(0);
+ if (readtoken() != TTHEN)
+ raise_error_unexpected_syntax(TTHEN);
+ n1->nif.ifpart = list(0);
+ n2 = n1;
+ while (readtoken() == TELIF) {
+ n2->nif.elsepart = stalloc(sizeof(struct nif));
+ n2 = n2->nif.elsepart;
+ n2->type = NIF;
+ n2->nif.test = list(0);
+ if (readtoken() != TTHEN)
+ raise_error_unexpected_syntax(TTHEN);
+ n2->nif.ifpart = list(0);
+ }
+ if (lasttoken == TELSE)
+ n2->nif.elsepart = list(0);
+ else {
+ n2->nif.elsepart = NULL;
+ tokpushback++;
+ }
+ t = TFI;
+ break;
+ case TWHILE:
+ case TUNTIL: {
+ int got;
+ n1 = stalloc(sizeof(struct nbinary));
+ n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
+ n1->nbinary.ch1 = list(0);
+ got = readtoken();
+ if (got != TDO) {
+ TRACE(("expecting DO got %s %s\n", tokname(got),
+ got == TWORD ? wordtext : ""));
+ raise_error_unexpected_syntax(TDO);
+ }
+ n1->nbinary.ch2 = list(0);
+ t = TDONE;
+ break;
+ }
+ case TFOR:
+ if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
+ raise_error_syntax("Bad for loop variable");
+ n1 = stalloc(sizeof(struct nfor));
+ n1->type = NFOR;
+ n1->nfor.var = wordtext;
+ checkkwd = CHKKWD | CHKALIAS;
+ if (readtoken() == TIN) {
+ app = ≈
+ while (readtoken() == TWORD) {
+ n2 = 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)
+ raise_error_unexpected_syntax(-1);
+ } else {
+ n2 = stalloc(sizeof(struct narg));
+ n2->type = NARG;
+ n2->narg.text = (char *)dolatstr;
+ 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 = CHKNL | CHKKWD | CHKALIAS;
+ if (readtoken() != TDO)
+ raise_error_unexpected_syntax(TDO);
+ n1->nfor.body = list(0);
+ t = TDONE;
+ break;
+ case TCASE:
+ n1 = stalloc(sizeof(struct ncase));
+ n1->type = NCASE;
+ if (readtoken() != TWORD)
+ raise_error_unexpected_syntax(TWORD);
+ n1->ncase.expr = n2 = stalloc(sizeof(struct narg));
+ n2->type = NARG;
+ n2->narg.text = wordtext;
+ n2->narg.backquote = backquotelist;
+ n2->narg.next = NULL;
+ do {
+ checkkwd = CHKKWD | CHKALIAS;
+ } while (readtoken() == TNL);
+ if (lasttoken != TIN)
+ raise_error_unexpected_syntax(TIN);
+ cpp = &n1->ncase.cases;
+ next_case:
+ checkkwd = CHKNL | CHKKWD;
+ t = readtoken();
+ while (t != TESAC) {
+ if (lasttoken == TLP)
+ readtoken();
+ *cpp = cp = stalloc(sizeof(struct nclist));
+ cp->type = NCLIST;
+ app = &cp->nclist.pattern;
+ for (;;) {
+ *app = ap = stalloc(sizeof(struct narg));
+ ap->type = NARG;
+ ap->narg.text = wordtext;
+ ap->narg.backquote = backquotelist;
+ if (readtoken() != TPIPE)
+ break;
+ app = &ap->narg.next;
+ readtoken();
+ }
+ ap->narg.next = NULL;
+ if (lasttoken != TRP)
+ raise_error_unexpected_syntax(TRP);
+ cp->nclist.body = list(2);
- if (c != PEOF) {
- if (c == '\n') {
- plinno++;
- needprompt = doprompt;
- }
+ cpp = &cp->nclist.next;
- p = strchr(xxreadtoken_chars, c);
- if (p == NULL) {
- READTOKEN1:
- return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
- }
+ checkkwd = CHKNL | CHKKWD;
+ t = readtoken();
+ if (t != TESAC) {
+ if (t != TENDCASE)
+ raise_error_unexpected_syntax(TENDCASE);
+ goto next_case;
+ }
+ }
+ *cpp = NULL;
+ goto redir;
+ case TLP:
+ n1 = stalloc(sizeof(struct nredir));
+ n1->type = NSUBSHELL;
+ n1->nredir.n = list(0);
+ n1->nredir.redirect = NULL;
+ t = TRP;
+ break;
+ case TBEGIN:
+ n1 = list(0);
+ t = TEND;
+ break;
+ case TWORD:
+ case TREDIR:
+ tokpushback++;
+ return simplecmd();
+ }
- if (p - xxreadtoken_chars >= xxreadtoken_singles) {
- if (pgetc() == *p) { /* double occurrence? */
- p += xxreadtoken_doubles + 1;
- } else {
- pungetc();
- }
- }
- }
- return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
- }
- }
- } /* for */
-}
-#else
-#define RETURN(token) return lasttoken = token
-static int
-xxreadtoken(void)
-{
- int c;
+ if (readtoken() != t)
+ raise_error_unexpected_syntax(t);
- if (tokpushback) {
- tokpushback = 0;
- return lasttoken;
- }
- if (needprompt) {
- setprompt(2);
+ redir:
+ /* Now check for redirection which may follow command */
+ checkkwd = CHKKWD | CHKALIAS;
+ rpp = rpp2;
+ while (readtoken() == TREDIR) {
+ *rpp = n2 = redirnode;
+ rpp = &n2->nfile.next;
+ parsefname();
}
- startlinno = plinno;
- for (;;) { /* until token or start of word found */
- c = pgetc_macro();
- switch (c) {
- case ' ': case '\t':
-#if ENABLE_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);
- 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;
+ tokpushback++;
+ *rpp = NULL;
+ if (redir) {
+ if (n1->type != NSUBSHELL) {
+ n2 = stalloc(sizeof(struct nredir));
+ n2->type = NREDIR;
+ n2->nredir.n = n1;
+ n1 = n2;
}
+ n1->nredir.redirect = redir;
}
- breakloop:
- return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
-#undef RETURN
+ return n1;
}
-#endif /* NEW_xxreadtoken */
-
/*
* If eofmark is NULL, read a word or a redirection symbol. If eofmark
if ((c == '>' || c == '<')
&& quotef == 0
&& len <= 2
- && (*out == '\0' || is_digit(*out))) {
+ && (*out == '\0' || isdigit(*out))) {
PARSEREDIR();
return lasttoken = TREDIR;
} else {
STPUTC(c, out);
c = pgetc();
} while (c > PEOA_OR_PEOF && is_in_name(c));
- } else if (is_digit(c)) {
+ } else if (isdigit(c)) {
do {
STPUTC(c, out);
c = pgetc();
- } while (is_digit(c));
+ } while (isdigit(c));
} else if (is_special(c)) {
USTPUTC(c, out);
c = pgetc();
if (oldstyle)
doprompt = saveprompt;
- else {
- if (readtoken() != TRP)
- raise_error_unexpected_syntax(TRP);
- }
+ else if (readtoken() != TRP)
+ raise_error_unexpected_syntax(TRP);
(*nlpp)->n = n;
if (oldstyle) {
else
USTPUTC(CTLBACKQ, out);
if (oldstyle)
- goto parsebackq_oldreturn;
- goto parsebackq_newreturn;
-}
-
-#if ENABLE_ASH_MATH_SUPPORT
-/*
- * 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;
-}
-#endif
-
-} /* end of readtoken */
-
-
-/*
- * Returns true if the text contains nothing to expand (no dollar signs
- * or backquotes).
- */
-static int
-noexpand(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 of a legal variable name (a letter or underscore followed by zero or
- * more letters, underscores, and digits).
- */
-static char *
-endofname(const char *name)
-{
- char *p;
-
- p = (char *) name;
- if (!is_name(*p))
- return p;
- while (*++p) {
- if (!is_in_name(*p))
- break;
- }
- return p;
-}
-
-
-/*
- * called by editline -- any expansions to the prompt
- * should be added here.
- */
-#if ENABLE_ASH_EXPAND_PRMT
-static const char *
-expandstr(const char *ps)
-{
- union node n;
-
- /* XXX Fix (char *) cast. */
- setinputstring((char *)ps);
- readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
- popfile();
-
- n.narg.type = NARG;
- n.narg.next = NULL;
- n.narg.text = wordtext;
- n.narg.backquote = backquotelist;
-
- expandarg(&n, NULL, 0);
- return stackblock();
-}
-#endif
-
-static void setprompt(int whichprompt)
-{
- const char *prompt;
-#if ENABLE_ASH_EXPAND_PRMT
- struct stackmark smark;
-#endif
-
- needprompt = 0;
-
- switch (whichprompt) {
- case 1:
- prompt = ps1val();
- break;
- case 2:
- prompt = ps2val();
- break;
- default: /* 0 */
- prompt = nullstr;
- }
-#if ENABLE_ASH_EXPAND_PRMT
- setstackmark(&smark);
- stalloc(stackblocksize());
-#endif
- putprompt(expandstr(prompt));
-#if ENABLE_ASH_EXPAND_PRMT
- popstackmark(&smark);
-#endif
-}
-
-
-/*
- * Execute a command or commands contained in a string.
- */
-static int
-evalstring(char *s, int mask)
-{
- union node *n;
- struct stackmark smark;
- int skip;
-
- setinputstring(s);
- setstackmark(&smark);
-
- skip = 0;
- while ((n = parsecmd(0)) != NEOF) {
- evaltree(n, 0);
- popstackmark(&smark);
- skip = evalskip;
- if (skip)
- break;
- }
- popfile();
-
- skip &= mask;
- evalskip = skip;
- return skip;
+ goto parsebackq_oldreturn;
+ goto parsebackq_newreturn;
}
+#if ENABLE_ASH_MATH_SUPPORT
/*
- * The eval command.
+ * Parse an arithmetic expansion (indicate start of one and set state)
*/
-static int
-evalcmd(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 (;;) {
- concat = stack_putstr(p, concat);
- p = *ap++;
- if (p == NULL)
- break;
- STPUTC(' ', concat);
- }
- STPUTC('\0', concat);
- p = grabstackstr(concat);
- }
- evalstring(p, ~SKIPEVAL);
-
+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);
}
- return exitstatus;
+ goto parsearith_return;
}
+#endif
+
+} /* end of readtoken */
/*
- * Read and execute commands. "Top" is nonzero for the top level command
- * loop; it turns on prompting if the shell is interactive.
+ * 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.]
*/
-static int
-cmdloop(int top)
-{
- union node *n;
- struct stackmark smark;
- int inter;
- int numeof = 0;
-
- TRACE(("cmdloop(%d) called\n", top));
- for (;;) {
- int skip;
+#define NEW_xxreadtoken
+#ifdef NEW_xxreadtoken
+/* singles must be first! */
+static const char xxreadtoken_chars[7] = { '\n', '(', ')', '&', '|', ';', 0 };
- setstackmark(&smark);
-#if JOBS
- if (jobctl)
- showjobs(stderr, SHOW_CHANGED);
-#endif
- inter = 0;
- if (iflag && top) {
- inter++;
-#if ENABLE_ASH_MAIL
- chkmail();
-#endif
- }
- 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 (nflag == 0) {
- job_warning = (job_warning == 2) ? 1 : 0;
- numeof = 0;
- evaltree(n, 0);
- }
- popstackmark(&smark);
- skip = evalskip;
+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 */
+};
- if (skip) {
- evalskip = 0;
- return skip & SKIPEVAL;
- }
- }
- return 0;
-}
+#define xxreadtoken_doubles \
+ (sizeof(xxreadtoken_tokens) - sizeof(xxreadtoken_chars))
+#define xxreadtoken_singles \
+ (sizeof(xxreadtoken_chars) - xxreadtoken_doubles - 1)
static int
-dotcmd(int argc, char **argv)
+xxreadtoken(void)
{
- struct strlist *sp;
- volatile struct shparam saveparam;
- int status = 0;
+ int c;
- for (sp = cmdenviron; sp; sp = sp->next)
- setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
+ if (tokpushback) {
+ tokpushback = 0;
+ return lasttoken;
+ }
+ if (needprompt) {
+ setprompt(2);
+ }
+ startlinno = plinno;
+ for (;;) { /* until token or start of word found */
+ c = pgetc_macro();
- if (argc >= 2) { /* That's what SVR2 does */
- char *fullname;
+ if ((c != ' ') && (c != '\t')
+#if ENABLE_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;
+ if (doprompt)
+ setprompt(2);
+ } else {
+ const char *p
+ = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
- fullname = find_dot_file(argv[1]);
+ if (c != PEOF) {
+ if (c == '\n') {
+ plinno++;
+ needprompt = doprompt;
+ }
- if (argc > 2) {
- saveparam = shellparam;
- shellparam.malloc = 0;
- shellparam.nparam = argc - 2;
- shellparam.p = argv + 2;
- };
+ p = strchr(xxreadtoken_chars, c);
+ if (p == NULL) {
+ READTOKEN1:
+ return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
+ }
- setinputfile(fullname, INPUT_PUSH_FILE);
- commandname = fullname;
- cmdloop(0);
- popfile();
+ if (p - xxreadtoken_chars >= xxreadtoken_singles) {
+ if (pgetc() == *p) { /* double occurrence? */
+ p += xxreadtoken_doubles + 1;
+ } else {
+ pungetc();
+ }
+ }
+ }
+ return lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
+ }
+ }
+ } /* for */
+}
+#else
+#define RETURN(token) return lasttoken = token
+static int
+xxreadtoken(void)
+{
+ int c;
- if (argc > 2) {
- freeparam(&shellparam);
- shellparam = saveparam;
- };
- status = exitstatus;
+ if (tokpushback) {
+ tokpushback = 0;
+ return lasttoken;
+ }
+ if (needprompt) {
+ setprompt(2);
+ }
+ startlinno = plinno;
+ for (;;) { /* until token or start of word found */
+ c = pgetc_macro();
+ switch (c) {
+ case ' ': case '\t':
+#if ENABLE_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);
+ 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;
+ }
}
- return status;
-}
-
-static int
-exitcmd(int argc, char **argv)
-{
- if (stoppedjobs())
- return 0;
- if (argc > 1)
- exitstatus = number(argv[1]);
- raise_exception(EXEXIT);
- /* NOTREACHED */
-}
-
-#if ENABLE_ASH_BUILTIN_ECHO
-static int
-echocmd(int argc, char **argv)
-{
- return bb_echo(argv);
+ breakloop:
+ return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
+#undef RETURN
}
-#endif
+#endif /* NEW_xxreadtoken */
-#if ENABLE_ASH_BUILTIN_TEST
static int
-testcmd(int argc, char **argv)
+readtoken(void)
{
- return bb_test(argc, argv);
-}
+ int t;
+#if DEBUG
+ int alreadyseen = tokpushback;
#endif
-/*
- * Read a file containing shell functions.
- */
-static void
-readcmdfile(char *name)
-{
- setinputfile(name, INPUT_PUSH_FILE);
- cmdloop(0);
- popfile();
-}
-
-
-/* redir.c */
-
-/*
- * 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
+#if ENABLE_ASH_ALIAS
+ top:
#endif
-/*
- * Open a file in noclobber mode.
- * The code was copied from bash.
- */
-static int
-noclobberopen(const char *fname)
-{
- int r, fd;
- struct stat finfo, finfo2;
+ t = xxreadtoken();
/*
- * If the file exists and is a regular file, return an error
- * immediately.
+ * eat newlines
*/
- r = stat(fname, &finfo);
- if (r == 0 && S_ISREG(finfo.st_mode)) {
- errno = EEXIST;
- return -1;
+ if (checkkwd & CHKNL) {
+ while (t == TNL) {
+ parseheredoc();
+ t = xxreadtoken();
+ }
}
- /*
- * 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 (t != TWORD || quoteflag) {
+ goto out;
+ }
/*
- * 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.
+ * check for keywords
*/
- 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 int
-openhere(union node *redir)
-{
- int pip[2];
- size_t len = 0;
+ if (checkkwd & CHKKWD) {
+ const char *const *pp;
- if (pipe(pip) < 0)
- ash_msg_and_raise_error("Pipe call failed");
- if (redir->type == NHERE) {
- len = strlen(redir->nhere.doc->narg.text);
- if (len <= PIPESIZE) {
- full_write(pip[1], redir->nhere.doc->narg.text, len);
+ pp = findkwd(wordtext);
+ if (pp) {
+ lasttoken = t = pp - tokname_array;
+ TRACE(("keyword %s recognized\n", tokname(t)));
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);
+
+ if (checkkwd & CHKALIAS) {
+#if ENABLE_ASH_ALIAS
+ struct alias *ap;
+ ap = lookupalias(wordtext, 1);
+ if (ap != NULL) {
+ if (*ap->val) {
+ pushstring(ap->val, ap);
+ }
+ goto top;
+ }
#endif
- signal(SIGPIPE, SIG_DFL);
- if (redir->type == NHERE)
- full_write(pip[1], redir->nhere.doc->narg.text, len);
- else
- expandhere(redir->nhere.doc, pip[1]);
- _exit(0);
}
out:
- close(pip[1]);
- return pip[0];
+ checkkwd = 0;
+#if DEBUG
+ if (!alreadyseen)
+ TRACE(("token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
+ else
+ TRACE(("reread token %s %s\n", tokname(t), t == TWORD ? wordtext : ""));
+#endif
+ return t;
}
-static int
-openredirect(union node *redir)
+static char
+peektoken(void)
{
- char *fname;
- int f;
+ int t;
- switch (redir->nfile.type) {
- case NFROM:
- fname = redir->nfile.expfname;
- f = open(fname, O_RDONLY);
- if (f < 0)
- goto eopen;
- break;
- case NFROMTO:
- fname = redir->nfile.expfname;
- f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
- if (f < 0)
- goto ecreate;
- break;
- case NTO:
- /* Take care of noclobber mode. */
- if (Cflag) {
- fname = redir->nfile.expfname;
- f = noclobberopen(fname);
- if (f < 0)
- goto ecreate;
- break;
- }
- /* FALLTHROUGH */
- case NCLOBBER:
- fname = redir->nfile.expfname;
- f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
- if (f < 0)
- goto ecreate;
- break;
- case NAPPEND:
- fname = redir->nfile.expfname;
- f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
- if (f < 0)
- goto ecreate;
- break;
- default:
-#if DEBUG
- abort();
-#endif
- /* Fall through to eliminate warning. */
- case NTOFD:
- case NFROMFD:
- f = -1;
- break;
- case NHERE:
- case NXHERE:
- f = openhere(redir);
- break;
- }
+ t = readtoken();
+ tokpushback++;
+ return tokname_array[t][0];
+}
+
+/*
+ * 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;
- return f;
- ecreate:
- ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
- eopen:
- ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
+ tokpushback = 0;
+ doprompt = interact;
+ if (doprompt)
+ setprompt(doprompt);
+ needprompt = 0;
+ t = readtoken();
+ if (t == TEOF)
+ return NEOF;
+ if (t == TNL)
+ return NULL;
+ tokpushback++;
+ return list(1);
}
+/*
+ * Input any here documents.
+ */
static void
-dupredirect(union node *redir, int f)
+parseheredoc(void)
{
- int fd = redir->nfile.fd;
+ struct heredoc *here;
+ union node *n;
- if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
- if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
- copyfd(redir->ndup.dupfd, fd);
- }
- return;
- }
+ here = heredoclist;
+ heredoclist = 0;
- if (f != fd) {
- copyfd(f, fd);
- close(f);
+ while (here) {
+ if (needprompt) {
+ setprompt(2);
+ }
+ readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
+ here->eofmark, here->striptabs);
+ n = 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;
+ here = here->next;
}
}
/*
- * Process a list of redirection commands. If the REDIR_PUSH flag is set,
- * old file descriptors are stashed away so that the redirection can be
- * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
- * standard output, and the standard error if it becomes a duplicate of
- * stdout, is saved in memory.
+ * called by editline -- any expansions to the prompt
+ * should be added here.
*/
-static void
-redirect(union node *redir, int flags)
+#if ENABLE_ASH_EXPAND_PRMT
+static const char *
+expandstr(const char *ps)
{
- union node *n;
- struct redirtab *sv;
- int i;
- int fd;
- int newfd;
- int *p;
- nullredirs++;
- if (!redir) {
- return;
- }
- sv = NULL;
- INT_OFF;
- if (flags & REDIR_PUSH) {
- struct redirtab *q;
- q = ckmalloc(sizeof(struct redirtab));
- q->next = redirlist;
- redirlist = q;
- q->nullredirs = nullredirs - 1;
- for (i = 0; i < 10; i++)
- q->renamed[i] = EMPTY;
- nullredirs = 0;
- sv = q;
- }
- n = redir;
- do {
- fd = n->nfile.fd;
- if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
- && n->ndup.dupfd == fd)
- continue; /* redirect from/to same file descriptor */
+ union node n;
- newfd = openredirect(n);
- if (fd == newfd)
- continue;
- if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
- i = fcntl(fd, F_DUPFD, 10);
+ /* XXX Fix (char *) cast. */
+ setinputstring((char *)ps);
+ readtoken1(pgetc(), DQSYNTAX, nullstr, 0);
+ popfile();
- if (i == -1) {
- i = errno;
- if (i != EBADF) {
- close(newfd);
- errno = i;
- ash_msg_and_raise_error("%d: %m", fd);
- /* NOTREACHED */
- }
- } else {
- *p = i;
- close(fd);
- }
- } else {
- close(fd);
- }
- dupredirect(n, newfd);
- } while ((n = n->nfile.next));
- INT_ON;
- if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
- preverrout_fd = sv->renamed[2];
+ n.narg.type = NARG;
+ n.narg.next = NULL;
+ n.narg.text = wordtext;
+ n.narg.backquote = backquotelist;
+
+ expandarg(&n, NULL, 0);
+ return stackblock();
}
+#endif
/*
- * Undo the effects of the last redirection.
+ * Execute a command or commands contained in a string.
*/
-static void
-popredir(int drop)
+static int
+evalstring(char *s, int mask)
{
- struct redirtab *rp;
- int i;
+ union node *n;
+ struct stackmark smark;
+ int skip;
- if (--nullredirs >= 0)
- return;
- INT_OFF;
- rp = redirlist;
- for (i = 0; i < 10; i++) {
- if (rp->renamed[i] != EMPTY) {
- if (!drop) {
- close(i);
- copyfd(rp->renamed[i], i);
- }
- close(rp->renamed[i]);
- }
+ setinputstring(s);
+ setstackmark(&smark);
+
+ skip = 0;
+ while ((n = parsecmd(0)) != NEOF) {
+ evaltree(n, 0);
+ popstackmark(&smark);
+ skip = evalskip;
+ if (skip)
+ break;
}
- redirlist = rp->next;
- nullredirs = rp->nullredirs;
- free(rp);
- INT_ON;
-}
+ popfile();
-/*
- * Undo all redirections. Called on error or interrupt.
- */
+ skip &= mask;
+ evalskip = skip;
+ return skip;
+}
/*
- * Discard all saved file descriptors.
+ * The eval command.
*/
-static void
-clearredir(int drop)
+static int
+evalcmd(int argc, char **argv)
{
- for (;;) {
- nullredirs = 0;
- if (!redirlist)
- break;
- popredir(drop);
+ char *p;
+ char *concat;
+ char **ap;
+
+ if (argc > 1) {
+ p = argv[1];
+ if (argc > 2) {
+ STARTSTACKSTR(concat);
+ ap = argv + 2;
+ for (;;) {
+ concat = stack_putstr(p, concat);
+ p = *ap++;
+ if (p == NULL)
+ break;
+ STPUTC(' ', concat);
+ }
+ STPUTC('\0', concat);
+ p = grabstackstr(concat);
+ }
+ evalstring(p, ~SKIPEVAL);
+
}
+ return exitstatus;
}
-
/*
- * 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.
+ * Read and execute commands. "Top" is nonzero for the top level command
+ * loop; it turns on prompting if the shell is interactive.
*/
static int
-copyfd(int from, int to)
+cmdloop(int top)
{
- int newfd;
+ union node *n;
+ struct stackmark smark;
+ int inter;
+ int numeof = 0;
+
+ TRACE(("cmdloop(%d) called\n", top));
+ for (;;) {
+ int skip;
+
+ setstackmark(&smark);
+#if JOBS
+ if (jobctl)
+ showjobs(stderr, SHOW_CHANGED);
+#endif
+ inter = 0;
+ if (iflag && top) {
+ inter++;
+#if ENABLE_ASH_MAIL
+ chkmail();
+#endif
+ }
+ 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 (nflag == 0) {
+ job_warning = (job_warning == 2) ? 1 : 0;
+ numeof = 0;
+ evaltree(n, 0);
+ }
+ popstackmark(&smark);
+ skip = evalskip;
- newfd = fcntl(from, F_DUPFD, to);
- if (newfd < 0) {
- if (errno == EMFILE)
- return EMPTY;
- ash_msg_and_raise_error("%d: %m", from);
+ if (skip) {
+ evalskip = 0;
+ return skip & SKIPEVAL;
+ }
}
- return newfd;
+ return 0;
}
-
static int
-redirectsafe(union node *redir, int flags)
+dotcmd(int argc, char **argv)
{
- int err;
- volatile int saveint;
- struct jmploc *volatile savehandler = exception_handler;
- struct jmploc jmploc;
-
- SAVE_INT(saveint);
- err = setjmp(jmploc.loc) * 2;
- if (!err) {
- exception_handler = &jmploc;
- redirect(redir, flags);
- }
- exception_handler = savehandler;
- if (err && exception != EXERROR)
- longjmp(exception_handler->loc, 1);
- RESTORE_INT(saveint);
- return err;
-}
-
-/* show.c */
-
-#if 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 *);
+ struct strlist *sp;
+ volatile struct shparam saveparam;
+ int status = 0;
+ for (sp = cmdenviron; sp; sp = sp->next)
+ setvareq(xstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
-static void
-showtree(union node *n)
-{
- trputs("showtree called\n");
- shtree(n, 1, NULL, stdout);
-}
+ if (argc >= 2) { /* That's what SVR2 does */
+ char *fullname;
+ fullname = find_dot_file(argv[1]);
-static void
-shtree(union node *n, int ind, char *pfx, FILE *fp)
-{
- struct nodelist *lp;
- const char *s;
+ if (argc > 2) {
+ saveparam = shellparam;
+ shellparam.malloc = 0;
+ shellparam.nparam = argc - 2;
+ shellparam.p = argv + 2;
+ };
- if (n == NULL)
- return;
+ setinputfile(fullname, INPUT_PUSH_FILE);
+ commandname = fullname;
+ cmdloop(0);
+ popfile();
- 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;
+ if (argc > 2) {
+ freeparam(&shellparam);
+ shellparam = saveparam;
+ };
+ status = exitstatus;
}
+ return status;
}
-
-static void
-shcmd(union node *cmd, FILE *fp)
+static int
+exitcmd(int argc, char **argv)
{
- 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(' ');
- switch (np->nfile.type) {
- case NTO: s = ">"; dftfd = 1; break;
- case NCLOBBER: s = ">|"; dftfd = 1; break;
- case NAPPEND: s = ">>"; dftfd = 1; break;
- case NTOFD: s = ">&"; dftfd = 1; break;
- case NFROM: s = "<"; dftfd = 0; break;
- case NFROMFD: s = "<&"; dftfd = 0; break;
- case NFROMTO: s = "<>"; dftfd = 0; break;
- default: s = "*error*"; dftfd = 0; break;
- }
- if (np->nfile.fd != dftfd)
- fprintf(fp, "%d", np->nfile.fd);
- 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;
- }
+ if (stoppedjobs())
+ return 0;
+ if (argc > 1)
+ exitstatus = number(argv[1]);
+ raise_exception(EXEXIT);
+ /* NOTREACHED */
}
-
-static void
-sharg(union node *arg, FILE *fp)
+#if ENABLE_ASH_BUILTIN_ECHO
+static int
+echocmd(int argc, char **argv)
{
- char *p;
- struct nodelist *bqlist;
- int subtype;
-
- if (arg->type != NARG) {
- out1fmt("<node type %d>\n", arg->type);
- 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:
- out1fmt("<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;
- }
- }
+ return bb_echo(argv);
}
+#endif
+#if ENABLE_ASH_BUILTIN_TEST
+static int
+testcmd(int argc, char **argv)
+{
+ return bb_test(argc, argv);
+}
+#endif
+/*
+ * Read a file containing shell functions.
+ */
static void
-indent(int amount, char *pfx, FILE *fp)
+readcmdfile(char *name)
{
- int i;
-
- for (i = 0; i < amount; i++) {
- if (pfx && i == amount - 1)
- fputs(pfx, fp);
- putc('\t', fp);
- }
+ setinputfile(name, INPUT_PUSH_FILE);
+ cmdloop(0);
+ popfile();
}
+/* redir.c */
+
/*
- * Debugging stuff.
+ * 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
-static FILE *tracefile;
-
-
-static void
-trputc(int c)
+/*
+ * Open a file in noclobber mode.
+ * The code was copied from bash.
+ */
+static int
+noclobberopen(const char *fname)
{
- if (debug != 1)
- return;
- putc(c, tracefile);
-}
+ int r, fd;
+ struct stat finfo, finfo2;
-static void
-trace(const char *fmt, ...)
-{
- va_list va;
+ /*
+ * 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 (debug != 1)
- return;
- va_start(va, fmt);
- (void) vfprintf(tracefile, fmt, va);
- va_end(va);
-}
+ /*
+ * 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);
-static void
-tracev(const char *fmt, va_list va)
-{
- if (debug != 1)
- return;
- (void) vfprintf(tracefile, fmt, va);
-}
+ /* If the open failed, return the file descriptor right away. */
+ if (fd < 0)
+ return fd;
+ /*
+ * OK, the open succeeded, but the file may have been changed from a
+ * non-regular file to a regular file between the stat and the open.
+ * We are assuming that the O_EXCL open handles the case where FILENAME
+ * did not exist and is symlinked to an existing file between the stat
+ * and open.
+ */
-static void
-trputs(const char *s)
-{
- if (debug != 1)
- return;
- fputs(s, tracefile);
+ /*
+ * 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;
}
-static void
-trstring(char *s)
+/*
+ * 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 int
+openhere(union node *redir)
{
- char *p;
- char c;
+ int pip[2];
+ size_t len = 0;
- if (debug != 1)
- 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;
+ if (pipe(pip) < 0)
+ ash_msg_and_raise_error("Pipe call failed");
+ if (redir->type == NHERE) {
+ len = strlen(redir->nhere.doc->narg.text);
+ if (len <= PIPESIZE) {
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
+ goto out;
}
}
- putc('"', tracefile);
-}
-
-
-static void
-trargs(char **ap)
-{
- if (debug != 1)
- return;
- while (*ap) {
- trstring(*ap++);
- if (*ap)
- putc(' ', tracefile);
+ 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)
+ full_write(pip[1], redir->nhere.doc->narg.text, len);
else
- putc('\n', tracefile);
+ expandhere(redir->nhere.doc, pip[1]);
+ _exit(0);
}
+ out:
+ close(pip[1]);
+ return pip[0];
}
-
-static void
-opentrace(void)
+static int
+openredirect(union node *redir)
{
- char s[100];
-#ifdef O_APPEND
- int flags;
-#endif
+ char *fname;
+ int f;
- if (debug != 1) {
- if (tracefile)
- fflush(tracefile);
- /* leave open because libedit might be using it */
- return;
- }
- scopy("./trace", s);
- if (tracefile) {
- if (!freopen(s, "a", tracefile)) {
- fprintf(stderr, "Can't re-open %s\n", s);
- debug = 0;
- return;
- }
- } else {
- tracefile = fopen(s, "a");
- if (tracefile == NULL) {
- fprintf(stderr, "Can't open %s\n", s);
- debug = 0;
- return;
+ switch (redir->nfile.type) {
+ case NFROM:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDONLY);
+ if (f < 0)
+ goto eopen;
+ break;
+ case NFROMTO:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_RDWR|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NTO:
+ /* Take care of noclobber mode. */
+ if (Cflag) {
+ fname = redir->nfile.expfname;
+ f = noclobberopen(fname);
+ if (f < 0)
+ goto ecreate;
+ break;
}
+ /* FALLTHROUGH */
+ case NCLOBBER:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ case NAPPEND:
+ fname = redir->nfile.expfname;
+ f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
+ if (f < 0)
+ goto ecreate;
+ break;
+ default:
+#if DEBUG
+ abort();
+#endif
+ /* Fall through to eliminate warning. */
+ case NTOFD:
+ case NFROMFD:
+ f = -1;
+ break;
+ case NHERE:
+ case NXHERE:
+ f = openhere(redir);
+ break;
}
-#ifdef O_APPEND
- flags = fcntl(fileno(tracefile), F_GETFL, 0);
- if (flags >= 0)
- fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
-#endif
- setlinebuf(tracefile);
- fputs("\nTracing started.\n", tracefile);
-}
-#endif /* DEBUG */
-
-
-/* trap.c */
-
-/*
- * Sigmode records the current value of the signal handlers for the various
- * modes. A value of zero means that the current handler is not known.
- * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
- */
-
-#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 */
+ return f;
+ ecreate:
+ ash_msg_and_raise_error("cannot create %s: %s", fname, errmsg(errno, "Directory nonexistent"));
+ eopen:
+ ash_msg_and_raise_error("cannot open %s: %s", fname, errmsg(errno, "No such file"));
+}
-/*
- * The trap builtin.
- */
-static int
-trapcmd(int argc, char **argv)
+static void
+dupredirect(union node *redir, int f)
{
- char *action;
- char **ap;
- int signo;
-
- nextopt(nullstr);
- ap = argptr;
- if (!*ap) {
- for (signo = 0; signo < NSIG; signo++) {
- if (trap[signo] != NULL) {
- const char *sn;
+ int fd = redir->nfile.fd;
- sn = get_signame(signo);
- out1fmt("trap -- %s %s\n",
- single_quote(trap[signo]), sn);
- }
+ if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
+ if (redir->ndup.dupfd >= 0) { /* if not ">&-" */
+ copyfd(redir->ndup.dupfd, fd);
}
- return 0;
+ return;
}
- if (!ap[1])
- action = NULL;
- else
- action = *ap++;
- while (*ap) {
- signo = get_signum(*ap);
- if (signo < 0)
- ash_msg_and_raise_error("%s: bad trap", *ap);
- INT_OFF;
- if (action) {
- if (LONE_DASH(action))
- action = NULL;
- else
- action = ckstrdup(action);
- }
- if (trap[signo])
- free(trap[signo]);
- trap[signo] = action;
- if (signo != 0)
- setsignal(signo);
- INT_ON;
- ap++;
+
+ if (f != fd) {
+ copyfd(f, fd);
+ close(f);
}
- return 0;
}
/*
- * Clear traps on a fork.
+ * Process a list of redirection commands. If the REDIR_PUSH flag is set,
+ * old file descriptors are stashed away so that the redirection can be
+ * undone by calling popredir. If the REDIR_BACKQ flag is set, then the
+ * standard output, and the standard error if it becomes a duplicate of
+ * stdout, is saved in memory.
*/
static void
-clear_traps(void)
+redirect(union node *redir, int flags)
{
- char **tp;
+ union node *n;
+ struct redirtab *sv;
+ int i;
+ int fd;
+ int newfd;
+ int *p;
+ nullredirs++;
+ if (!redir) {
+ return;
+ }
+ sv = NULL;
+ INT_OFF;
+ if (flags & REDIR_PUSH) {
+ struct redirtab *q;
+ q = ckmalloc(sizeof(struct redirtab));
+ q->next = redirlist;
+ redirlist = q;
+ q->nullredirs = nullredirs - 1;
+ for (i = 0; i < 10; i++)
+ q->renamed[i] = EMPTY;
+ nullredirs = 0;
+ sv = q;
+ }
+ n = redir;
+ do {
+ fd = n->nfile.fd;
+ if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD)
+ && n->ndup.dupfd == fd)
+ continue; /* redirect from/to same file descriptor */
- for (tp = trap; tp < &trap[NSIG]; tp++) {
- if (*tp && **tp) { /* trap not NULL or SIG_IGN */
- INT_OFF;
- free(*tp);
- *tp = NULL;
- if (tp != &trap[0])
- setsignal(tp - trap);
- INT_ON;
+ newfd = openredirect(n);
+ if (fd == newfd)
+ continue;
+ if (sv && *(p = &sv->renamed[fd]) == EMPTY) {
+ i = fcntl(fd, F_DUPFD, 10);
+
+ if (i == -1) {
+ i = errno;
+ if (i != EBADF) {
+ close(newfd);
+ errno = i;
+ ash_msg_and_raise_error("%d: %m", fd);
+ /* NOTREACHED */
+ }
+ } else {
+ *p = i;
+ close(fd);
+ }
+ } else {
+ close(fd);
}
- }
+ dupredirect(n, newfd);
+ } while ((n = n->nfile.next));
+ INT_ON;
+ if (flags & REDIR_SAVEFD2 && sv && sv->renamed[2] >= 0)
+ preverrout_fd = sv->renamed[2];
}
/*
- * Set the signal handler for the specified signal. The routine figures
- * out what it should be set to.
+ * Undo the effects of the last redirection.
*/
static void
-setsignal(int signo)
+popredir(int drop)
{
- int action;
- char *t, tsig;
- struct sigaction act;
-
- t = trap[signo];
- if (t == 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:
-#if DEBUG
- if (debug)
- break;
-#endif
- /* FALLTHROUGH */
- case SIGTERM:
- if (iflag)
- action = S_IGN;
- break;
-#if JOBS
- case SIGTSTP:
- case SIGTTOU:
- if (mflag)
- action = S_IGN;
- break;
-#endif
- }
- }
+ struct redirtab *rp;
+ int i;
- t = &sigmode[signo - 1];
- tsig = *t;
- if (tsig == 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)
- ) {
- tsig = S_IGN; /* don't hard ignore these */
- } else
- tsig = S_HARD_IGN;
- } else {
- tsig = S_RESET; /* force to be set */
- }
- }
- if (tsig == S_HARD_IGN || tsig == action)
+ if (--nullredirs >= 0)
return;
- switch (action) {
- case S_CATCH:
- act.sa_handler = onsig;
- break;
- case S_IGN:
- act.sa_handler = SIG_IGN;
- break;
- default:
- act.sa_handler = SIG_DFL;
+ INT_OFF;
+ rp = redirlist;
+ for (i = 0; i < 10; i++) {
+ if (rp->renamed[i] != EMPTY) {
+ if (!drop) {
+ close(i);
+ copyfd(rp->renamed[i], i);
+ }
+ close(rp->renamed[i]);
+ }
}
- *t = action;
- act.sa_flags = 0;
- sigfillset(&act.sa_mask);
- sigaction(signo, &act, 0);
+ redirlist = rp->next;
+ nullredirs = rp->nullredirs;
+ free(rp);
+ INT_ON;
}
+/*
+ * Undo all redirections. Called on error or interrupt.
+ */
/*
- * Ignore a signal.
+ * Discard all saved file descriptors.
*/
static void
-ignoresig(int signo)
+clearredir(int drop)
{
- if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
- signal(signo, SIG_IGN);
+ for (;;) {
+ nullredirs = 0;
+ if (!redirlist)
+ break;
+ popredir(drop);
}
- sigmode[signo - 1] = S_HARD_IGN;
}
/*
- * Signal handler.
+ * 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
+copyfd(int from, int to)
+{
+ int newfd;
+
+ newfd = fcntl(from, F_DUPFD, to);
+ if (newfd < 0) {
+ if (errno == EMFILE)
+ return EMPTY;
+ ash_msg_and_raise_error("%d: %m", from);
+ }
+ return newfd;
+}
+
+static int
+redirectsafe(union node *redir, int flags)
+{
+ int err;
+ volatile int saveint;
+ struct jmploc *volatile savehandler = exception_handler;
+ struct jmploc jmploc;
+
+ SAVE_INT(saveint);
+ err = setjmp(jmploc.loc) * 2;
+ if (!err) {
+ exception_handler = &jmploc;
+ redirect(redir, flags);
+ }
+ exception_handler = savehandler;
+ if (err && exception != EXERROR)
+ longjmp(exception_handler->loc, 1);
+ RESTORE_INT(saveint);
+ return err;
+}
+
+/* show.c */
+
+#if 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
-onsig(int signo)
+showtree(union node *n)
{
- gotsig[signo - 1] = 1;
- pendingsigs = signo;
+ trputs("showtree called\n");
+ shtree(n, 1, NULL, stdout);
+}
- if (exsig || (signo == SIGINT && !trap[SIGINT])) {
- if (!suppressint)
- raise_interrupt();
- intpending = 1;
+static void
+shtree(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(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(' ');
+ switch (np->nfile.type) {
+ case NTO: s = ">"; dftfd = 1; break;
+ case NCLOBBER: s = ">|"; dftfd = 1; break;
+ case NAPPEND: s = ">>"; dftfd = 1; break;
+ case NTOFD: s = ">&"; dftfd = 1; break;
+ case NFROM: s = "<"; dftfd = 0; break;
+ case NFROMFD: s = "<&"; dftfd = 0; break;
+ case NFROMTO: s = "<>"; dftfd = 0; break;
+ default: s = "*error*"; dftfd = 0; break;
+ }
+ if (np->nfile.fd != dftfd)
+ fprintf(fp, "%d", np->nfile.fd);
+ 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;
+ }
+}
-/*
- * Called to execute a trap. Perhaps we should avoid entering new trap
- * handlers while we are executing a trap handler.
- */
-static int
-dotrap(void)
+static void
+sharg(union node *arg, FILE *fp)
{
char *p;
- char *q;
- int i;
- int savestatus;
- int skip = 0;
+ struct nodelist *bqlist;
+ int subtype;
- savestatus = exitstatus;
- pendingsigs = 0;
- xbarrier();
+ if (arg->type != NARG) {
+ out1fmt("<node type %d>\n", arg->type);
+ 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);
- for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
- if (!*q)
- continue;
- *q = '\0';
+ while (*p != '=')
+ putc(*p++, fp);
- p = trap[i + 1];
- if (!p)
- continue;
- skip = evalstring(p, SKIPEVAL);
- exitstatus = savestatus;
- if (skip)
+ 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:
+ out1fmt("<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;
+ }
}
+}
- return skip;
+
+static void
+indent(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);
+ }
}
/*
- * Controls whether the shell is interactive or not.
+ * Debugging stuff.
*/
+
+
+static FILE *tracefile;
+
+
static void
-setinteractive(int on)
+trputc(int c)
{
- static int is_interactive;
+ if (debug != 1)
+ return;
+ putc(c, tracefile);
+}
- if (++on == is_interactive)
+static void
+trace(const char *fmt, ...)
+{
+ va_list va;
+
+ if (debug != 1)
return;
- is_interactive = on;
- setsignal(SIGINT);
- setsignal(SIGQUIT);
- setsignal(SIGTERM);
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
- if (is_interactive > 1) {
- /* Looks like they want an interactive shell */
- static int do_banner;
+ va_start(va, fmt);
+ (void) vfprintf(tracefile, fmt, va);
+ va_end(va);
+}
- if (!do_banner) {
- out1fmt(
- "\n\n"
- "%s Built-in shell (ash)\n"
- "Enter 'help' for a list of built-in commands."
- "\n\n",
- BB_BANNER);
- do_banner++;
- }
- }
-#endif
+static void
+tracev(const char *fmt, va_list va)
+{
+ if (debug != 1)
+ return;
+ (void) vfprintf(tracefile, fmt, va);
}
-#if !ENABLE_FEATURE_SH_EXTRA_QUIET
-/*** List the available builtins ***/
+static void
+trputs(const char *s)
+{
+ if (debug != 1)
+ return;
+ fputs(s, tracefile);
+}
+
-static int helpcmd(int argc, char **argv)
+static void
+trstring(char *s)
{
- int col, i;
+ char *p;
+ char c;
- out1fmt("\nBuilt-in commands:\n-------------------\n");
- for (col = 0, i = 0; i < NUMBUILTINS; i++) {
- col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
- builtincmd[i].name + 1);
- if (col > 60) {
- out1fmt("\n");
- col = 0;
+ if (debug != 1)
+ 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;
}
}
-#if ENABLE_FEATURE_SH_STANDALONE_SHELL
- for (i = 0; i < NUM_APPLETS; i++) {
- col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
- if (col > 60) {
- out1fmt("\n");
- col = 0;
- }
+ putc('"', tracefile);
+}
+
+
+static void
+trargs(char **ap)
+{
+ if (debug != 1)
+ return;
+ while (*ap) {
+ trstring(*ap++);
+ if (*ap)
+ putc(' ', tracefile);
+ else
+ putc('\n', tracefile);
}
-#endif
- out1fmt("\n\n");
- return EXIT_SUCCESS;
}
-#endif /* FEATURE_SH_EXTRA_QUIET */
-/*
- * Called to exit the shell.
- */
static void
-exitshell(void)
+opentrace(void)
{
- struct jmploc loc;
- char *p;
- int status;
+ char s[100];
+#ifdef O_APPEND
+ int flags;
+#endif
- status = exitstatus;
- TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
- if (setjmp(loc.loc)) {
- if (exception == EXEXIT)
-/* dash bug: it just does _exit(exitstatus) here
- * but we have to do setjobctl(0) first!
- * (bug is still not fixed in dash-0.5.3 - if you run dash
- * under Midnight Commander, on exit from dash MC is backgrounded) */
- status = exitstatus;
- goto out;
+ if (debug != 1) {
+ if (tracefile)
+ fflush(tracefile);
+ /* leave open because libedit might be using it */
+ return;
}
- exception_handler = &loc;
- p = trap[0];
- if (p) {
- trap[0] = NULL;
- evalstring(p, 0);
+ scopy("./trace", s);
+ if (tracefile) {
+ if (!freopen(s, "a", tracefile)) {
+ fprintf(stderr, "Can't re-open %s\n", s);
+ debug = 0;
+ return;
+ }
+ } else {
+ tracefile = fopen(s, "a");
+ if (tracefile == NULL) {
+ fprintf(stderr, "Can't open %s\n", s);
+ debug = 0;
+ return;
+ }
}
- flush_stdout_stderr();
- out:
- setjobctl(0);
- _exit(status);
- /* NOTREACHED */
+#ifdef O_APPEND
+ flags = fcntl(fileno(tracefile), F_GETFL, 0);
+ if (flags >= 0)
+ fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
+#endif
+ setlinebuf(tracefile);
+ fputs("\nTracing started.\n", tracefile);
}
+#endif /* DEBUG */
-/* var.c */
-
-static struct var *vartab[VTABSIZE];
-static int vpcmp(const void *, const void *);
-static struct var **findvar(struct var **, const char *);
+/* trap.c */
/*
- * Initialize the variable symbol tables and import the environment
+ * Sigmode records the current value of the signal handlers for the various
+ * modes. A value of zero means that the current handler is not known.
+ * S_HARD_IGN indicates that the signal was ignored on entry to the shell,
*/
+#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 */
+
-#if ENABLE_ASH_GETOPTS
/*
- * Safe version of setvar, returns 1 on success 0 on failure.
+ * The trap builtin.
*/
static int
-setvarsafe(const char *name, const char *val, int flags)
+trapcmd(int argc, char **argv)
{
- int err;
- volatile int saveint;
- struct jmploc *volatile savehandler = exception_handler;
- struct jmploc jmploc;
+ char *action;
+ char **ap;
+ int signo;
- SAVE_INT(saveint);
- if (setjmp(jmploc.loc))
- err = 1;
- else {
- exception_handler = &jmploc;
- setvar(name, val, flags);
- err = 0;
+ nextopt(nullstr);
+ ap = argptr;
+ if (!*ap) {
+ for (signo = 0; signo < NSIG; signo++) {
+ if (trap[signo] != NULL) {
+ const char *sn;
+
+ sn = get_signame(signo);
+ out1fmt("trap -- %s %s\n",
+ single_quote(trap[signo]), sn);
+ }
+ }
+ return 0;
+ }
+ if (!ap[1])
+ action = NULL;
+ else
+ action = *ap++;
+ while (*ap) {
+ signo = get_signum(*ap);
+ if (signo < 0)
+ ash_msg_and_raise_error("%s: bad trap", *ap);
+ INT_OFF;
+ if (action) {
+ if (LONE_DASH(action))
+ action = NULL;
+ else
+ action = ckstrdup(action);
+ }
+ if (trap[signo])
+ free(trap[signo]);
+ trap[signo] = action;
+ if (signo != 0)
+ setsignal(signo);
+ INT_ON;
+ ap++;
}
- exception_handler = savehandler;
- RESTORE_INT(saveint);
- return err;
+ return 0;
}
-#endif
/*
- * 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.
+ * Clear traps on a fork.
*/
static void
-setvar(const char *name, const char *val, int flags)
+clear_traps(void)
{
- char *p, *q;
- size_t namelen;
- char *nameeq;
- size_t vallen;
+ char **tp;
- q = endofname(name);
- p = strchrnul(q, '=');
- namelen = p - name;
- if (!namelen || p != q)
- ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
- vallen = 0;
- if (val == NULL) {
- flags |= VUNSET;
- } else {
- vallen = strlen(val);
- }
- INT_OFF;
- nameeq = ckmalloc(namelen + vallen + 2);
- p = memcpy(nameeq, name, namelen) + namelen;
- if (val) {
- *p++ = '=';
- p = memcpy(p, val, vallen) + vallen;
+ for (tp = trap; tp < &trap[NSIG]; tp++) {
+ if (*tp && **tp) { /* trap not NULL or SIG_IGN */
+ INT_OFF;
+ free(*tp);
+ *tp = NULL;
+ if (tp != &trap[0])
+ setsignal(tp - trap);
+ INT_ON;
+ }
}
- *p = '\0';
- setvareq(nameeq, flags | VNOSAVE);
- INT_ON;
}
/*
- * 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.
- * Called with interrupts off.
+ * Set the signal handler for the specified signal. The routine figures
+ * out what it should be set to.
*/
static void
-setvareq(char *s, int flags)
+setsignal(int signo)
{
- struct var *vp, **vpp;
-
- vpp = hashvar(s);
- flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
- vp = *findvar(vpp, s);
- if (vp) {
- if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
- const char *n;
+ int action;
+ char *t, tsig;
+ struct sigaction act;
- if (flags & VNOSAVE)
- free(s);
- n = vp->text;
- ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
+ t = trap[signo];
+ if (t == 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:
+#if DEBUG
+ if (debug)
+ break;
+#endif
+ /* FALLTHROUGH */
+ case SIGTERM:
+ if (iflag)
+ action = S_IGN;
+ break;
+#if JOBS
+ case SIGTSTP:
+ case SIGTTOU:
+ if (mflag)
+ action = S_IGN;
+ break;
+#endif
}
+ }
- if (flags & VNOSET)
+ t = &sigmode[signo - 1];
+ tsig = *t;
+ if (tsig == 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)
+ ) {
+ tsig = S_IGN; /* don't hard ignore these */
+ } else
+ tsig = S_HARD_IGN;
+ } else {
+ tsig = S_RESET; /* force to be set */
+ }
+ }
+ if (tsig == S_HARD_IGN || tsig == action)
+ return;
+ switch (action) {
+ case S_CATCH:
+ act.sa_handler = onsig;
+ break;
+ case S_IGN:
+ act.sa_handler = SIG_IGN;
+ break;
+ default:
+ act.sa_handler = SIG_DFL;
+ }
+ *t = action;
+ act.sa_flags = 0;
+ sigfillset(&act.sa_mask);
+ sigaction(signo, &act, 0);
+}
- if (vp->func && (flags & VNOFUNC) == 0)
- (*vp->func)(strchrnul(s, '=') + 1);
-
- if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
- free((char*)vp->text);
- flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
- } else {
- if (flags & VNOSET)
- return;
- /* not found */
- vp = ckmalloc(sizeof(*vp));
- vp->next = *vpp;
- vp->func = NULL;
- *vpp = vp;
+/*
+ * Ignore a signal.
+ */
+static void
+ignoresig(int signo)
+{
+ if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
+ signal(signo, SIG_IGN);
}
- if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
- s = ckstrdup(s);
- vp->text = s;
- vp->flags = flags;
+ sigmode[signo - 1] = S_HARD_IGN;
}
/*
- * Process a linked list of variable assignments.
+ * Signal handler.
*/
static void
-listsetvar(struct strlist *list_set_var, int flags)
+onsig(int signo)
{
- struct strlist *lp = list_set_var;
+ gotsig[signo - 1] = 1;
+ pendingsigs = signo;
- if (!lp)
- return;
- INT_OFF;
- do {
- setvareq(lp->text, flags);
- } while ((lp = lp->next));
- INT_ON;
+ if (exsig || (signo == SIGINT && !trap[SIGINT])) {
+ if (!suppressint)
+ raise_interrupt();
+ intpending = 1;
+ }
}
/*
- * Find the value of a variable. Returns NULL if not set.
+ * Called to execute a trap. Perhaps we should avoid entering new trap
+ * handlers while we are executing a trap handler.
*/
-static char *
-lookupvar(const char *name)
+static int
+dotrap(void)
{
- struct var *v;
+ char *p;
+ char *q;
+ int i;
+ int savestatus;
+ int skip = 0;
- v = *findvar(hashvar(name), name);
- if (v) {
-#ifdef DYNAMIC_VAR
- /*
- * Dynamic variables are implemented roughly the same way they are
- * in bash. Namely, they're "special" so long as they aren't unset.
- * As soon as they're unset, they're no longer dynamic, and dynamic
- * lookup will no longer happen at that point. -- PFM.
- */
- if ((v->flags & VDYNAMIC))
- (*v->func)(NULL);
-#endif
- if (!(v->flags & VUNSET))
- return strchrnul(v->text, '=') + 1;
+ savestatus = exitstatus;
+ pendingsigs = 0;
+ xbarrier();
+
+ for (i = 0, q = gotsig; i < NSIG - 1; i++, q++) {
+ if (!*q)
+ continue;
+ *q = '\0';
+
+ p = trap[i + 1];
+ if (!p)
+ continue;
+ skip = evalstring(p, SKIPEVAL);
+ exitstatus = savestatus;
+ if (skip)
+ break;
}
- return NULL;
+ return skip;
}
-
/*
- * Search the environment of a builtin command.
+ * Controls whether the shell is interactive or not.
*/
-static char *
-bltinlookup(const char *name)
+static void
+setinteractive(int on)
{
- struct strlist *sp;
+ static int is_interactive;
+
+ if (++on == is_interactive)
+ return;
+ is_interactive = on;
+ setsignal(SIGINT);
+ setsignal(SIGQUIT);
+ setsignal(SIGTERM);
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+ if (is_interactive > 1) {
+ /* Looks like they want an interactive shell */
+ static int do_banner;
- for (sp = cmdenviron; sp; sp = sp->next) {
- if (varequal(sp->text, name))
- return strchrnul(sp->text, '=') + 1;
+ if (!do_banner) {
+ out1fmt(
+ "\n\n"
+ "%s Built-in shell (ash)\n"
+ "Enter 'help' for a list of built-in commands."
+ "\n\n",
+ BB_BANNER);
+ do_banner++;
+ }
}
- return lookupvar(name);
+#endif
}
+#if !ENABLE_FEATURE_SH_EXTRA_QUIET
+/*** List the available builtins ***/
-/*
- * Generate a list of variables satisfying the given conditions.
- */
-static char **
-listvars(int on, int off, char ***end)
+static int
+helpcmd(int argc, char **argv)
{
- struct var **vpp;
- struct var *vp;
- char **ep;
- int mask;
+ int col, i;
- STARTSTACKSTR(ep);
- vpp = vartab;
- mask = on | off;
- do {
- for (vp = *vpp; vp; vp = vp->next)
- if ((vp->flags & mask) == on) {
- if (ep == stackstrend())
- ep = growstackstr();
- *ep++ = (char *) vp->text;
- }
- } while (++vpp < vartab + VTABSIZE);
- if (ep == stackstrend())
- ep = growstackstr();
- if (end)
- *end = ep;
- *ep++ = NULL;
- return grabstackstr(ep);
+ out1fmt("\nBuilt-in commands:\n-------------------\n");
+ for (col = 0, i = 0; i < NUMBUILTINS; i++) {
+ col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
+ builtincmd[i].name + 1);
+ if (col > 60) {
+ out1fmt("\n");
+ col = 0;
+ }
+ }
+#if ENABLE_FEATURE_SH_STANDALONE_SHELL
+ for (i = 0; i < NUM_APPLETS; i++) {
+ col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), applets[i].name);
+ if (col > 60) {
+ out1fmt("\n");
+ col = 0;
+ }
+ }
+#endif
+ out1fmt("\n\n");
+ return EXIT_SUCCESS;
}
-
+#endif /* FEATURE_SH_EXTRA_QUIET */
/*
- * POSIX requires that 'set' (but not export or readonly) output the
- * variables in lexicographic order - by the locale's collating order (sigh).
- * Maybe we could keep them in an ordered balanced binary tree
- * instead of hashed lists.
- * For now just roll 'em through qsort for printing...
+ * Called to exit the shell.
*/
-static int
-showvars(const char *sep_prefix, int on, int off)
+static void
+exitshell(void)
{
- const char *sep;
- char **ep, **epend;
-
- ep = listvars(on, off, &epend);
- qsort(ep, epend - ep, sizeof(char *), vpcmp);
-
- sep = *sep_prefix ? spcstr : sep_prefix;
-
- for (; ep < epend; ep++) {
- const char *p;
- const char *q;
-
- p = strchrnul(*ep, '=');
- q = nullstr;
- if (*p)
- q = single_quote(++p);
+ struct jmploc loc;
+ char *p;
+ int status;
- out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
+ status = exitstatus;
+ TRACE(("pid %d, exitshell(%d)\n", getpid(), status));
+ if (setjmp(loc.loc)) {
+ if (exception == EXEXIT)
+/* dash bug: it just does _exit(exitstatus) here
+ * but we have to do setjobctl(0) first!
+ * (bug is still not fixed in dash-0.5.3 - if you run dash
+ * under Midnight Commander, on exit from dash MC is backgrounded) */
+ status = exitstatus;
+ goto out;
}
-
- return 0;
+ exception_handler = &loc;
+ p = trap[0];
+ if (p) {
+ trap[0] = NULL;
+ evalstring(p, 0);
+ }
+ flush_stdout_stderr();
+ out:
+ setjobctl(0);
+ _exit(status);
+ /* NOTREACHED */
}
}
-/*
- * Called after a function returns.
- * Interrupts must be off.
- */
-static void
-poplocalvars(void)
-{
- struct localvar *lvp;
- struct var *vp;
-
- while ((lvp = localvars) != NULL) {
- localvars = lvp->next;
- vp = lvp->vp;
- TRACE(("poplocalvar %s", vp ? vp->text : "-"));
- if (vp == NULL) { /* $- saved */
- memcpy(optlist, lvp->text, sizeof(optlist));
- free((char*)lvp->text);
- optschanged();
- } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) {
- unsetvar(vp->text);
- } else {
- if (vp->func)
- (*vp->func)(strchrnul(lvp->text, '=') + 1);
- if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
- free((char*)vp->text);
- vp->flags = lvp->flags;
- vp->text = lvp->text;
- }
- free(lvp);
- }
-}
-
-
/*
* 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
}
-/*
- * Unset the specified variable.
- */
-static int
-unsetvar(const char *s)
-{
- struct var **vpp;
- struct var *vp;
- int retval;
-
- vpp = findvar(hashvar(s), s);
- vp = *vpp;
- retval = 2;
- if (vp) {
- int flags = vp->flags;
-
- retval = 1;
- if (flags & VREADONLY)
- goto out;
-#ifdef DYNAMIC_VAR
- vp->flags &= ~VDYNAMIC;
-#endif
- if (flags & VUNSET)
- goto ok;
- if ((flags & VSTRFIXED) == 0) {
- INT_OFF;
- if ((flags & (VTEXTFIXED|VSTACK)) == 0)
- free((char*)vp->text);
- *vpp = vp->next;
- free(vp);
- INT_ON;
- } else {
- setvar(s, 0, 0);
- vp->flags &= ~VEXPORT;
- }
- ok:
- retval = 0;
- }
- out:
- return retval;
-}
-
-
-/*
- * 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];
-}
-
-
-/*
- * Compares two strings up to the first = or '\0'. The first
- * string must be terminated by '='; the second may be terminated by
- * either '=' or '\0'.
- */
-static int
-varcmp(const char *p, const char *q)
-{
- int c, d;
-
- while ((c = *p) == (d = *q)) {
- if (!c || c == '=')
- goto out;
- p++;
- q++;
- }
- if (c == '=')
- c = 0;
- if (d == '=')
- d = 0;
- out:
- return c - d;
-}
-
-static int
-vpcmp(const void *a, const void *b)
-{
- return varcmp(*(const char **)a, *(const char **)b);
-}
-
-static struct var **
-findvar(struct var **vpp, const char *name)
-{
- for (; *vpp; vpp = &(*vpp)->next) {
- if (varequal((*vpp)->text, name)) {
- break;
- }
- }
- return vpp;
-}
/* setmode.c */
#include <sys/times.h>
out1fmt("%.4o\n", mask);
}
} else {
- if (is_digit((unsigned char) *ap)) {
+ if (isdigit((unsigned char) *ap)) {
mask = 0;
do {
if (*ap >= '8' || *ap < '0')
lasttok = TOK_NUM;
continue;
}
- if (is_digit(arithval)) {
+ if (isdigit(arithval)) {
numstackptr->var = NULL;
#if ENABLE_ASH_MATH_SUPPORT_64
numstackptr->val = strtoll(expr, (char **) &expr, 0);