--- trunk/mkinitrd-magellan/busybox/shell/ash.c 2010/04/29 20:38:48 983 +++ trunk/mkinitrd-magellan/busybox/shell/ash.c 2010/05/30 11:32:42 984 @@ -2,31 +2,18 @@ /* * ash shell port for busybox * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Original BSD copyright notice is retained at the end of this file. + * * Copyright (c) 1989, 1991, 1993, 1994 * The Regents of the University of California. All rights reserved. * * Copyright (c) 1997-2005 Herbert Xu * was re-ported from NetBSD and debianized. * - * This code is derived from software contributed to Berkeley by - * Kenneth Almquist. - * * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. - * - * Original BSD copyright notice is retained at the end of this file. - */ - -/* - * rewrite arith.y to micro stack based cryptic algorithm by - * Copyright (c) 2001 Aaron Lehmann - * - * Modified by Paul Mundt (c) 2004 to support - * dynamic variables. - * - * Modified by Vladimir Oleynik (c) 2001-2005 to be - * used in busybox and size optimizations, - * rewrote arith (see notes to this), added locale support, - * rewrote dynamic variables. */ /* @@ -47,8 +34,6 @@ #define PROFILE 0 -#define IFS_BROKEN - #define JOBS ENABLE_ASH_JOB_CONTROL #if DEBUG @@ -61,8 +46,28 @@ #include #include #include -#if JOBS || ENABLE_ASH_READ_NCHARS -# include + +#include "shell_common.h" +#include "builtin_read.h" +#include "math.h" +#if ENABLE_ASH_RANDOM_SUPPORT +# include "random.h" +#else +# define CLEAR_RANDOM_T(rnd) ((void)0) +#endif + +#define SKIP_definitions 1 +#include "applet_tables.h" +#undef SKIP_definitions +#if NUM_APPLETS == 1 +/* STANDALONE does not make sense, and won't compile */ +# undef CONFIG_FEATURE_SH_STANDALONE +# undef ENABLE_FEATURE_SH_STANDALONE +# undef IF_FEATURE_SH_STANDALONE +# undef IF_NOT_FEATURE_SH_STANDALONE +# define ENABLE_FEATURE_SH_STANDALONE 0 +# define IF_FEATURE_SH_STANDALONE(...) +# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__ #endif #ifndef PIPE_BUF @@ -70,7 +75,7 @@ #endif #if defined(__uClinux__) -# error "Do not even bother, ash will not run on uClinux" +# error "Do not even bother, ash will not run on NOMMU machine" #endif @@ -98,14 +103,17 @@ "b" "notify", "u" "nounset", "\0" "vi" +#if ENABLE_ASH_BASH_COMPAT + ,"\0" "pipefail" +#endif #if DEBUG ,"\0" "nolog" ,"\0" "debug" #endif }; -#define optletters(n) optletters_optnames[(n)][0] -#define optnames(n) (&optletters_optnames[(n)][1]) +#define optletters(n) optletters_optnames[n][0] +#define optnames(n) (optletters_optnames[n] + 1) enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; @@ -114,12 +122,12 @@ static const char homestr[] ALIGN1 = "HOME"; static const char snlfmt[] ALIGN1 = "%s\n"; -static const char illnum[] ALIGN1 = "Illegal number: %s"; +static const char msg_illnum[] ALIGN1 = "Illegal number: %s"; /* * We enclose jmp_buf in a structure so that we can declare pointers to * jump locations. The global variable handler contains the location to - * jump to when an exception occurs, and the global variable exception + * jump to when an exception occurs, and the global variable exception_type * contains a code identifying the exception. To implement nested * exception handlers, the user should save the value of handler on entry * to an inner scope, set handler to point to a jmploc structure for the @@ -144,15 +152,11 @@ struct jmploc *exception_handler; -// disabled by vda: cannot understand how it was supposed to work - -// cannot fix bugs. That's why you have to explain your non-trivial designs! -// /* do we generate EXSIG events */ -// int exsig; /* counter */ - volatile int suppressint; /* counter */ - volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */ + volatile int suppress_int; /* counter */ + volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ /* last pending signal */ - volatile /*sig_atomic_t*/ smallint pendingsig; - smallint exception; /* kind of exception (0..5) */ + volatile /*sig_atomic_t*/ smallint pending_sig; + smallint exception_type; /* kind of exception (0..5) */ /* exceptions */ #define EXINT 0 /* SIGINT received */ #define EXERROR 1 /* a generic error */ @@ -179,33 +183,36 @@ #define bflag optlist[11] #define uflag optlist[12] #define viflag optlist[13] +#if ENABLE_ASH_BASH_COMPAT +# define pipefail optlist[14] +#else +# define pipefail 0 +#endif #if DEBUG -#define nolog optlist[14] -#define debug optlist[15] +# define nolog optlist[14 + ENABLE_ASH_BASH_COMPAT] +# define debug optlist[15 + ENABLE_ASH_BASH_COMPAT] #endif /* trap handler commands */ /* * 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, + * S_HARD_IGN indicates that the signal was ignored on entry to the shell. */ char sigmode[NSIG - 1]; -#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_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 */ /* indicates specified signal received */ - char gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ + uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ char *trap[NSIG]; + char **trap_ptr; /* used only by "trap hack" */ /* Rarely referenced stuff */ #if ENABLE_ASH_RANDOM_SUPPORT - /* Random number generators */ - int32_t random_galois_LFSR; /* Galois LFSR (fast but weak). signed! */ - uint32_t random_LCG; /* LCG (fast but weak) */ + random_t random_gen; #endif pid_t backgndpid; /* pid of last background process */ smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ @@ -219,19 +226,18 @@ #define physdir (G_misc.physdir ) #define arg0 (G_misc.arg0 ) #define exception_handler (G_misc.exception_handler) -#define exception (G_misc.exception ) -#define suppressint (G_misc.suppressint ) -#define intpending (G_misc.intpending ) -//#define exsig (G_misc.exsig ) -#define pendingsig (G_misc.pendingsig ) +#define exception_type (G_misc.exception_type ) +#define suppress_int (G_misc.suppress_int ) +#define pending_int (G_misc.pending_int ) +#define pending_sig (G_misc.pending_sig ) #define isloginsh (G_misc.isloginsh ) #define nullstr (G_misc.nullstr ) #define optlist (G_misc.optlist ) #define sigmode (G_misc.sigmode ) #define gotsig (G_misc.gotsig ) #define trap (G_misc.trap ) -#define random_galois_LFSR (G_misc.random_galois_LFSR) -#define random_LCG (G_misc.random_LCG ) +#define trap_ptr (G_misc.trap_ptr ) +#define random_gen (G_misc.random_gen ) #define backgndpid (G_misc.backgndpid ) #define job_warning (G_misc.job_warning) #define INIT_G_misc() do { \ @@ -239,6 +245,7 @@ barrier(); \ curdir = nullstr; \ physdir = nullstr; \ + trap_ptr = trap; \ } while (0) @@ -248,10 +255,10 @@ static void trace_vprintf(const char *fmt, va_list va); # define TRACE(param) trace_printf param # define TRACEV(param) trace_vprintf param -# define close(f) do { \ - int dfd = (f); \ +# define close(fd) do { \ + int dfd = (fd); \ if (close(dfd) < 0) \ - bb_error_msg("bug on %d: closing %d(%x)", \ + bb_error_msg("bug on %d: closing %d(0x%x)", \ __LINE__, dfd, dfd); \ } while (0) #else @@ -263,9 +270,6 @@ /* ============ Utility functions */ #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) -/* C99 say: "char" declaration may be signed or unsigned by default */ -#define signed_char2int(sc) ((int)(signed char)(sc)) - static int isdigit_str9(const char *str) { int maxlen = 9 + 1; /* max 9 digits: 999999999 */ @@ -283,14 +287,14 @@ * more fun than worrying about efficiency and portability. :-)) */ #define INT_OFF do { \ - suppressint++; \ + suppress_int++; \ xbarrier(); \ } while (0) /* * Called to raise an exception. Since C doesn't include exceptions, we * just do a longjmp to the exception handler. The type of exception is - * stored in the global variable "exception". + * stored in the global variable "exception_type". */ static void raise_exception(int) NORETURN; static void @@ -301,7 +305,7 @@ abort(); #endif INT_OFF; - exception = e; + exception_type = e; longjmp(exception_handler->loc, 1); } #if DEBUG @@ -322,24 +326,24 @@ static void raise_interrupt(void) { - int i; + int ex_type; - intpending = 0; + pending_int = 0; /* Signal is not automatically unmasked after it is raised, * do it ourself - unmask all signals */ sigprocmask_allsigs(SIG_UNBLOCK); - /* pendingsig = 0; - now done in onsig() */ + /* pending_sig = 0; - now done in onsig() */ - i = EXSIG; + ex_type = EXSIG; if (gotsig[SIGINT - 1] && !trap[SIGINT]) { if (!(rootshell && iflag)) { /* Kill ourself with SIGINT */ signal(SIGINT, SIG_DFL); raise(SIGINT); } - i = EXINT; + ex_type = EXINT; } - raise_exception(i); + raise_exception(ex_type); /* NOTREACHED */ } #if DEBUG @@ -349,79 +353,34 @@ } while (0) #endif -#if ENABLE_ASH_OPTIMIZE_FOR_SIZE -static void +static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void int_on(void) { - if (--suppressint == 0 && intpending) { + xbarrier(); + if (--suppress_int == 0 && pending_int) { raise_interrupt(); } } #define INT_ON int_on() -static void +static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void force_int_on(void) { - suppressint = 0; - if (intpending) + xbarrier(); + suppress_int = 0; + if (pending_int) raise_interrupt(); } #define FORCE_INT_ON force_int_on() -#else /* !ASH_OPTIMIZE_FOR_SIZE */ - -#define INT_ON do { \ - xbarrier(); \ - if (--suppressint == 0 && intpending) \ - raise_interrupt(); \ -} while (0) -#define FORCE_INT_ON do { \ - xbarrier(); \ - suppressint = 0; \ - if (intpending) \ - raise_interrupt(); \ -} while (0) -#endif /* !ASH_OPTIMIZE_FOR_SIZE */ - -#define SAVE_INT(v) ((v) = suppressint) +#define SAVE_INT(v) ((v) = suppress_int) #define RESTORE_INT(v) do { \ xbarrier(); \ - suppressint = (v); \ - if (suppressint == 0 && intpending) \ + suppress_int = (v); \ + if (suppress_int == 0 && pending_int) \ raise_interrupt(); \ } while (0) -/* - * Ignore a signal. Only one usage site - in forkchild() - */ -static void -ignoresig(int signo) -{ - if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { - signal(signo, SIG_IGN); - } - sigmode[signo - 1] = S_HARD_IGN; -} - -/* - * Signal handler. Only one usage site - in setsignal() - */ -static void -onsig(int signo) -{ - gotsig[signo - 1] = 1; - - if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { - if (!suppressint) { - pendingsig = 0; - raise_interrupt(); /* does not return */ - } - intpending = 1; - } else { - pendingsig = signo; - } -} - /* ============ Stdout/stderr output */ @@ -437,16 +396,7 @@ flush_stdout_stderr(void) { INT_OFF; - fflush(stdout); - fflush(stderr); - INT_ON; -} - -static void -flush_stderr(void) -{ - INT_OFF; - fflush(stderr); + fflush_all(); INT_ON; } @@ -499,22 +449,24 @@ out2str(const char *p) { outstr(p, stderr); - flush_stderr(); + flush_stdout_stderr(); } /* ============ Parser structures */ /* control characters in argument strings */ -#define CTLESC '\201' /* escape next character */ -#define CTLVAR '\202' /* variable defn */ -#define CTLENDVAR '\203' -#define CTLBACKQ '\204' +#define CTL_FIRST CTLESC +#define CTLESC ((unsigned char)'\201') /* escape next character */ +#define CTLVAR ((unsigned char)'\202') /* variable defn */ +#define CTLENDVAR ((unsigned char)'\203') +#define CTLBACKQ ((unsigned char)'\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 CTLARI ((unsigned char)'\206') /* arithmetic expression */ +#define CTLENDARI ((unsigned char)'\207') +#define CTLQUOTEMARK ((unsigned char)'\210') +#define CTL_LAST CTLQUOTEMARK /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ @@ -685,6 +637,12 @@ struct nnot nnot; }; +/* + * NODE_EOF is returned by parsecmd when it encounters an end of file. + * It must be distinct from NULL. + */ +#define NODE_EOF ((union node *) -1L) + struct nodelist { struct nodelist *next; union node *n; @@ -724,7 +682,7 @@ if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) - fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); va_start(va, fmt); vfprintf(tracefile, fmt, va); va_end(va); @@ -740,7 +698,7 @@ if (DEBUG_PID) fprintf(tracefile, "[%u] ", (int) getpid()); if (DEBUG_SIG) - fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); + fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); vfprintf(tracefile, fmt, va); } @@ -762,17 +720,17 @@ 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; + switch ((unsigned char)*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); @@ -782,8 +740,8 @@ putc(*p, tracefile); else { putc('\\', tracefile); - putc(*p >> 6 & 03, tracefile); - putc(*p >> 3 & 07, tracefile); + putc((*p >> 6) & 03, tracefile); + putc((*p >> 3) & 07, tracefile); putc(*p & 07, tracefile); } break; @@ -867,7 +825,7 @@ { char *p; struct nodelist *bqlist; - int subtype; + unsigned char subtype; if (arg->type != NARG) { out1fmt("\n", arg->type); @@ -875,7 +833,7 @@ } bqlist = arg->narg.backquote; for (p = arg->narg.text; *p; p++) { - switch (*p) { + switch ((unsigned char)*p) { case CTLESC: putc(*++p, fp); break; @@ -999,6 +957,12 @@ return; indent(ind, pfx, fp); + + if (n == NODE_EOF) { + fputs("", fp); + return; + } + switch (n->type) { case NSEMI: s = "; "; @@ -1021,7 +985,7 @@ break; case NPIPE: for (lp = n->npipe.cmdlist; lp; lp = lp->next) { - shcmd(lp->n, fp); + shtree(lp->n, 0, NULL, fp); if (lp->next) fputs(" | ", fp); } @@ -1042,7 +1006,7 @@ showtree(union node *n) { trace_puts("showtree called\n"); - shtree(n, 1, NULL, stdout); + shtree(n, 1, NULL, stderr); } #endif /* DEBUG */ @@ -1062,8 +1026,8 @@ struct strpush { struct strpush *prev; /* preceding string on stack */ - char *prevstring; - int prevnleft; + char *prev_string; + int prev_left_in_line; #if ENABLE_ASH_ALIAS struct alias *ap; /* if push was associated with an alias */ #endif @@ -1074,9 +1038,9 @@ struct parsefile *prev; /* preceding file on stack */ int linno; /* current line */ int fd; /* file descriptor (or -1 if string) */ - int nleft; /* number of chars left in this line */ - int lleft; /* number of chars left in this buffer */ - char *nextc; /* next char in buffer */ + int left_in_line; /* number of chars left in this line */ + int left_in_buffer; /* number of chars left in this buffer past the line */ + char *next_to_pgetc; /* next char in buffer */ char *buf; /* input buffer */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ @@ -1143,6 +1107,14 @@ va_end(ap); } +static void raise_error_syntax(const char *) NORETURN; +static void +raise_error_syntax(const char *msg) +{ + ash_msg_and_raise_error("syntax error: %s", msg); + /* NOTREACHED */ +} + static void ash_msg_and_raise(int, const char *, ...) NORETURN; static void ash_msg_and_raise(int cond, const char *msg, ...) @@ -1185,6 +1157,49 @@ /* ============ Memory allocation */ +#if 0 +/* I consider these wrappers nearly useless: + * ok, they return you to nearest exception handler, but + * how much memory do you leak in the process, making + * memory starvation worse? + */ +static void * +ckrealloc(void * p, size_t nbytes) +{ + p = realloc(p, nbytes); + if (!p) + ash_msg_and_raise_error(bb_msg_memory_exhausted); + return p; +} + +static void * +ckmalloc(size_t nbytes) +{ + return ckrealloc(NULL, nbytes); +} + +static void * +ckzalloc(size_t nbytes) +{ + return memset(ckmalloc(nbytes), 0, nbytes); +} + +static char * +ckstrdup(const char *s) +{ + char *p = strdup(s); + if (!p) + ash_msg_and_raise_error(bb_msg_memory_exhausted); + return p; +} +#else +/* Using bbox equivalents. They exit if out of memory */ +# define ckrealloc xrealloc +# define ckmalloc xmalloc +# define ckzalloc xzalloc +# define ckstrdup xstrdup +#endif + /* * It appears that grabstackstr() will barf with such alignments * because stalloc() will return a string allocated in a new stackblock. @@ -1194,7 +1209,7 @@ /* Most machines require the value returned from malloc to be aligned * in some way. The following macro will get this right * on many machines. */ - SHELL_SIZE = sizeof(union {int i; char *cp; double d; }) - 1, + SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1, /* Minimum size of a block */ MINSIZE = SHELL_ALIGN(504), }; @@ -1240,43 +1255,10 @@ herefd = -1; \ } while (0) + #define stackblock() ((void *)g_stacknxt) #define stackblocksize() g_stacknleft - -static void * -ckrealloc(void * p, size_t nbytes) -{ - p = realloc(p, nbytes); - if (!p) - ash_msg_and_raise_error(bb_msg_memory_exhausted); - return p; -} - -static void * -ckmalloc(size_t nbytes) -{ - return ckrealloc(NULL, nbytes); -} - -static void * -ckzalloc(size_t nbytes) -{ - return memset(ckmalloc(nbytes), 0, nbytes); -} - -/* - * Make a copy of a string in safe storage. - */ -static char * -ckstrdup(const char *s) -{ - char *p = strdup(s); - if (!p) - ash_msg_and_raise_error(bb_msg_memory_exhausted); - return p; -} - /* * Parse trees for commands are allocated in lifo order, so we use a stack * to make this more efficient, and also to avoid all sorts of exception @@ -1581,7 +1563,7 @@ number(const char *s) { if (!is_number(s)) - ash_msg_and_raise_error(illnum, s); + ash_msg_and_raise_error(msg_illnum, s); return atoi(s); } @@ -1611,21 +1593,21 @@ STADJUST(q - p, p); - len = strspn(s, "'"); - if (!len) + if (*s != '\'') break; + len = 0; + do len++; while (*++s == '\''); q = p = makestrspace(len + 3, p); *q++ = '"'; - q = (char *)memcpy(q, s, len) + len; + q = (char *)memcpy(q, s - len, len) + len; *q++ = '"'; - s += len; STADJUST(q - p, p); } while (*s); - USTPUTC(0, p); + USTPUTC('\0', p); return stackblock(); } @@ -1723,14 +1705,14 @@ } #if ENABLE_ASH_GETOPTS -static void getoptsreset(const char *value); +static void FAST_FUNC getoptsreset(const char *value); #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 */ + void (*func)(const char *) FAST_FUNC; /* function to be called when */ /* the variable gets set/unset */ }; @@ -1757,23 +1739,16 @@ # define VDYNAMIC 0 #endif -#ifdef IFS_BROKEN -static const char defifsvar[] ALIGN1 = "IFS= \t\n"; -#define defifs (defifsvar + 4) -#else -static const char defifs[] ALIGN1 = " \t\n"; -#endif - /* Need to be before varinit_data[] */ #if ENABLE_LOCALE_SUPPORT -static void +static void FAST_FUNC change_lc_all(const char *value) { if (value && *value != '\0') setlocale(LC_ALL, value); } -static void +static void FAST_FUNC change_lc_ctype(const char *value) { if (value && *value != '\0') @@ -1782,23 +1757,19 @@ #endif #if ENABLE_ASH_MAIL static void chkmail(void); -static void changemail(const char *); +static void changemail(const char *) FAST_FUNC; #endif -static void changepath(const char *); +static void changepath(const char *) FAST_FUNC; #if ENABLE_ASH_RANDOM_SUPPORT -static void change_random(const char *); +static void change_random(const char *) FAST_FUNC; #endif static const struct { int flags; const char *text; - void (*func)(const char *); + void (*func)(const char *) FAST_FUNC; } varinit_data[] = { -#ifdef IFS_BROKEN { VSTRFIXED|VTEXTFIXED , defifsvar , NULL }, -#else - { VSTRFIXED|VTEXTFIXED|VUNSET, "IFS\0" , NULL }, -#endif #if ENABLE_ASH_MAIL { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL\0" , changemail }, { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH\0", changemail }, @@ -1898,7 +1869,7 @@ #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) #if ENABLE_ASH_GETOPTS -static void +static void FAST_FUNC getoptsreset(const char *value) { shellparam.optind = number(value); @@ -1910,7 +1881,7 @@ * Return of a legal variable name (a letter or underscore followed by zero or * more letters, underscores, and digits). */ -static char * +static char* FAST_FUNC endofname(const char *name) { char *p; @@ -2017,7 +1988,7 @@ /* * Find the value of a variable. Returns NULL if not set. */ -static char * +static const char* FAST_FUNC lookupvar(const char *name) { struct var *v; @@ -2043,7 +2014,7 @@ /* * Search the environment of a builtin command. */ -static char * +static const char * bltinlookup(const char *name) { struct strlist *sp; @@ -2140,6 +2111,12 @@ INT_ON; } +static void FAST_FUNC +setvar2(const char *name, const char *val) +{ + setvar(name, val, 0); +} + #if ENABLE_ASH_GETOPTS /* * Safe version of setvar, returns 1 on success 0 on failure. @@ -2261,17 +2238,17 @@ /* ============ 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 + * of the path before the first call; path_advance will update + * this value as it proceeds. Successive calls to path_advance 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 const char *pathopt; /* set by path_advance */ static char * -padvance(const char **path, const char *name) +path_advance(const char **path, const char *name) { const char *p; char *q; @@ -2376,8 +2353,6 @@ #define CD_PHYSICAL 1 #define CD_PRINT 2 -static int docd(const char *, int); - static int cdopt(void) { @@ -2385,7 +2360,7 @@ int i, j; j = 'L'; - while ((i = nextopt("LP"))) { + while ((i = nextopt("LP")) != '\0') { if (i != j) { flags ^= CD_PHYSICAL; j = i; @@ -2529,7 +2504,7 @@ return err; } -static int +static int FAST_FUNC cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { const char *dest; @@ -2575,7 +2550,7 @@ } do { c = *path; - p = padvance(&path, dest); + p = path_advance(&path, dest); if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { if (c && c != ':') flags |= CD_PRINT; @@ -2593,7 +2568,7 @@ return 0; } -static int +static int FAST_FUNC pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int flags; @@ -2634,421 +2609,387 @@ #define CSPCL 13 /* these terminate a word */ #define CIGN 14 /* character should be ignored */ +#define PEOF 256 #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 +# define PEOA 257 #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 */ -#define PSSYNTAX 4 /* prompt */ +#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE -#if ENABLE_ASH_OPTIMIZE_FOR_SIZE -#define USE_SIT_FUNCTION +#if ENABLE_SH_MATH_SUPPORT +# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12)) +#else +# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8)) #endif - -#if ENABLE_ASH_MATH_SUPPORT -static const char S_I_T[][4] = { +static const uint16_t S_I_T[] = { #if ENABLE_ASH_ALIAS - { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */ + SIT_ITEM(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 ... */ + SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */ + SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */ + SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */ + SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */ + SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */ + SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */ + SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */ + SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */ + SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */ + SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */ + SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */ +#if !USE_SIT_FUNCTION + SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */ + SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */ + SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */ #endif +#undef SIT_ITEM }; -#else -static const char S_I_T[][3] = { +/* Constants below must match table above */ +enum { #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 ... */ + CSPCL_CIGN_CIGN_CIGN , /* 0 */ #endif + CSPCL_CWORD_CWORD_CWORD , /* 1 */ + CNL_CNL_CNL_CNL , /* 2 */ + 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 */ + CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */ + CWORD_CWORD_CWORD_CWORD , /* 13 */ + CCTL_CCTL_CCTL_CCTL , /* 14 */ }; -#endif /* ASH_MATH_SUPPORT */ -#ifdef USE_SIT_FUNCTION +/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF, + * caller must ensure proper cast on it if c is *char_ptr! + */ +/* Values for syntax param */ +#define BASESYNTAX 0 /* not in quotes */ +#define DQSYNTAX 1 /* in double quotes */ +#define SQSYNTAX 2 /* in single quotes */ +#define ARISYNTAX 3 /* in arithmetic */ +#define PSSYNTAX 4 /* prompt. never passed to SIT() */ + +#if USE_SIT_FUNCTION static int SIT(int c, int syntax) { static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; -#if ENABLE_ASH_ALIAS - static const char syntax_index_table[] ALIGN1 = { +# if ENABLE_ASH_ALIAS + static const uint8_t syntax_index_table[] ALIGN1 = { 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[] ALIGN1 = { +# else + static const uint8_t syntax_index_table[] ALIGN1 = { 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 +# endif const char *s; int indx; - if (c == PEOF) /* 2^8+2 */ + if (c == PEOF) return CENDFILE; -#if ENABLE_ASH_ALIAS - if (c == PEOA) /* 2^8+1 */ +# if ENABLE_ASH_ALIAS + if (c == PEOA) indx = 0; else -#endif - - if ((unsigned char)c >= (unsigned char)(CTLESC) - && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) - ) { - return CCTL; - } else { +# endif + { + /* Cast is purely for paranoia here, + * just in case someone passed signed char to us */ + if ((unsigned char)c >= CTL_FIRST + && (unsigned char)c <= CTL_LAST + ) { + return CCTL; + } s = strchrnul(spec_symbls, c); if (*s == '\0') return CWORD; indx = syntax_index_table[s - spec_symbls]; } - return S_I_T[indx][syntax]; + return (S_I_T[indx] >> (syntax*4)) & 0xf; } #else /* !USE_SIT_FUNCTION */ -#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] = { +static const uint8_t syntax_index_table[] = { /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */ - /* 0 PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, -#if ENABLE_ASH_ALIAS - /* 1 PEOA */ CSPCL_CIGN_CIGN_CIGN, -#endif - /* 2 -128 0x80 */ CWORD_CWORD_CWORD_CWORD, - /* 3 -127 CTLESC */ CCTL_CCTL_CCTL_CCTL, - /* 4 -126 CTLVAR */ CCTL_CCTL_CCTL_CCTL, - /* 5 -125 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, - /* 6 -124 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, - /* 7 -123 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, - /* 8 -122 CTLARI */ CCTL_CCTL_CCTL_CCTL, - /* 9 -121 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, - /* 10 -120 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, - /* 11 -119 */ CWORD_CWORD_CWORD_CWORD, - /* 12 -118 */ CWORD_CWORD_CWORD_CWORD, - /* 13 -117 */ CWORD_CWORD_CWORD_CWORD, - /* 14 -116 */ CWORD_CWORD_CWORD_CWORD, - /* 15 -115 */ CWORD_CWORD_CWORD_CWORD, - /* 16 -114 */ CWORD_CWORD_CWORD_CWORD, - /* 17 -113 */ CWORD_CWORD_CWORD_CWORD, - /* 18 -112 */ CWORD_CWORD_CWORD_CWORD, - /* 19 -111 */ CWORD_CWORD_CWORD_CWORD, - /* 20 -110 */ CWORD_CWORD_CWORD_CWORD, - /* 21 -109 */ CWORD_CWORD_CWORD_CWORD, - /* 22 -108 */ CWORD_CWORD_CWORD_CWORD, - /* 23 -107 */ CWORD_CWORD_CWORD_CWORD, - /* 24 -106 */ CWORD_CWORD_CWORD_CWORD, - /* 25 -105 */ CWORD_CWORD_CWORD_CWORD, - /* 26 -104 */ CWORD_CWORD_CWORD_CWORD, - /* 27 -103 */ CWORD_CWORD_CWORD_CWORD, - /* 28 -102 */ CWORD_CWORD_CWORD_CWORD, - /* 29 -101 */ CWORD_CWORD_CWORD_CWORD, - /* 30 -100 */ CWORD_CWORD_CWORD_CWORD, - /* 31 -99 */ CWORD_CWORD_CWORD_CWORD, - /* 32 -98 */ CWORD_CWORD_CWORD_CWORD, - /* 33 -97 */ CWORD_CWORD_CWORD_CWORD, - /* 34 -96 */ CWORD_CWORD_CWORD_CWORD, - /* 35 -95 */ CWORD_CWORD_CWORD_CWORD, - /* 36 -94 */ CWORD_CWORD_CWORD_CWORD, - /* 37 -93 */ CWORD_CWORD_CWORD_CWORD, - /* 38 -92 */ CWORD_CWORD_CWORD_CWORD, - /* 39 -91 */ CWORD_CWORD_CWORD_CWORD, - /* 40 -90 */ CWORD_CWORD_CWORD_CWORD, - /* 41 -89 */ CWORD_CWORD_CWORD_CWORD, - /* 42 -88 */ CWORD_CWORD_CWORD_CWORD, - /* 43 -87 */ CWORD_CWORD_CWORD_CWORD, - /* 44 -86 */ CWORD_CWORD_CWORD_CWORD, - /* 45 -85 */ CWORD_CWORD_CWORD_CWORD, - /* 46 -84 */ CWORD_CWORD_CWORD_CWORD, - /* 47 -83 */ CWORD_CWORD_CWORD_CWORD, - /* 48 -82 */ CWORD_CWORD_CWORD_CWORD, - /* 49 -81 */ CWORD_CWORD_CWORD_CWORD, - /* 50 -80 */ CWORD_CWORD_CWORD_CWORD, - /* 51 -79 */ CWORD_CWORD_CWORD_CWORD, - /* 52 -78 */ CWORD_CWORD_CWORD_CWORD, - /* 53 -77 */ CWORD_CWORD_CWORD_CWORD, - /* 54 -76 */ CWORD_CWORD_CWORD_CWORD, - /* 55 -75 */ CWORD_CWORD_CWORD_CWORD, - /* 56 -74 */ CWORD_CWORD_CWORD_CWORD, - /* 57 -73 */ CWORD_CWORD_CWORD_CWORD, - /* 58 -72 */ CWORD_CWORD_CWORD_CWORD, - /* 59 -71 */ CWORD_CWORD_CWORD_CWORD, - /* 60 -70 */ CWORD_CWORD_CWORD_CWORD, - /* 61 -69 */ CWORD_CWORD_CWORD_CWORD, - /* 62 -68 */ CWORD_CWORD_CWORD_CWORD, - /* 63 -67 */ CWORD_CWORD_CWORD_CWORD, - /* 64 -66 */ CWORD_CWORD_CWORD_CWORD, - /* 65 -65 */ CWORD_CWORD_CWORD_CWORD, - /* 66 -64 */ CWORD_CWORD_CWORD_CWORD, - /* 67 -63 */ CWORD_CWORD_CWORD_CWORD, - /* 68 -62 */ CWORD_CWORD_CWORD_CWORD, - /* 69 -61 */ CWORD_CWORD_CWORD_CWORD, - /* 70 -60 */ CWORD_CWORD_CWORD_CWORD, - /* 71 -59 */ CWORD_CWORD_CWORD_CWORD, - /* 72 -58 */ CWORD_CWORD_CWORD_CWORD, - /* 73 -57 */ CWORD_CWORD_CWORD_CWORD, - /* 74 -56 */ CWORD_CWORD_CWORD_CWORD, - /* 75 -55 */ CWORD_CWORD_CWORD_CWORD, - /* 76 -54 */ CWORD_CWORD_CWORD_CWORD, - /* 77 -53 */ CWORD_CWORD_CWORD_CWORD, - /* 78 -52 */ CWORD_CWORD_CWORD_CWORD, - /* 79 -51 */ CWORD_CWORD_CWORD_CWORD, - /* 80 -50 */ CWORD_CWORD_CWORD_CWORD, - /* 81 -49 */ CWORD_CWORD_CWORD_CWORD, - /* 82 -48 */ CWORD_CWORD_CWORD_CWORD, - /* 83 -47 */ CWORD_CWORD_CWORD_CWORD, - /* 84 -46 */ CWORD_CWORD_CWORD_CWORD, - /* 85 -45 */ CWORD_CWORD_CWORD_CWORD, - /* 86 -44 */ CWORD_CWORD_CWORD_CWORD, - /* 87 -43 */ CWORD_CWORD_CWORD_CWORD, - /* 88 -42 */ CWORD_CWORD_CWORD_CWORD, - /* 89 -41 */ CWORD_CWORD_CWORD_CWORD, - /* 90 -40 */ CWORD_CWORD_CWORD_CWORD, - /* 91 -39 */ CWORD_CWORD_CWORD_CWORD, - /* 92 -38 */ CWORD_CWORD_CWORD_CWORD, - /* 93 -37 */ CWORD_CWORD_CWORD_CWORD, - /* 94 -36 */ CWORD_CWORD_CWORD_CWORD, - /* 95 -35 */ CWORD_CWORD_CWORD_CWORD, - /* 96 -34 */ CWORD_CWORD_CWORD_CWORD, - /* 97 -33 */ CWORD_CWORD_CWORD_CWORD, - /* 98 -32 */ CWORD_CWORD_CWORD_CWORD, - /* 99 -31 */ CWORD_CWORD_CWORD_CWORD, - /* 100 -30 */ CWORD_CWORD_CWORD_CWORD, - /* 101 -29 */ CWORD_CWORD_CWORD_CWORD, - /* 102 -28 */ CWORD_CWORD_CWORD_CWORD, - /* 103 -27 */ CWORD_CWORD_CWORD_CWORD, - /* 104 -26 */ CWORD_CWORD_CWORD_CWORD, - /* 105 -25 */ CWORD_CWORD_CWORD_CWORD, - /* 106 -24 */ CWORD_CWORD_CWORD_CWORD, - /* 107 -23 */ CWORD_CWORD_CWORD_CWORD, - /* 108 -22 */ CWORD_CWORD_CWORD_CWORD, - /* 109 -21 */ CWORD_CWORD_CWORD_CWORD, - /* 110 -20 */ CWORD_CWORD_CWORD_CWORD, - /* 111 -19 */ CWORD_CWORD_CWORD_CWORD, - /* 112 -18 */ CWORD_CWORD_CWORD_CWORD, - /* 113 -17 */ CWORD_CWORD_CWORD_CWORD, - /* 114 -16 */ CWORD_CWORD_CWORD_CWORD, - /* 115 -15 */ CWORD_CWORD_CWORD_CWORD, - /* 116 -14 */ CWORD_CWORD_CWORD_CWORD, - /* 117 -13 */ CWORD_CWORD_CWORD_CWORD, - /* 118 -12 */ CWORD_CWORD_CWORD_CWORD, - /* 119 -11 */ CWORD_CWORD_CWORD_CWORD, - /* 120 -10 */ CWORD_CWORD_CWORD_CWORD, - /* 121 -9 */ CWORD_CWORD_CWORD_CWORD, - /* 122 -8 */ CWORD_CWORD_CWORD_CWORD, - /* 123 -7 */ CWORD_CWORD_CWORD_CWORD, - /* 124 -6 */ CWORD_CWORD_CWORD_CWORD, - /* 125 -5 */ CWORD_CWORD_CWORD_CWORD, - /* 126 -4 */ CWORD_CWORD_CWORD_CWORD, - /* 127 -3 */ CWORD_CWORD_CWORD_CWORD, - /* 128 -2 */ CWORD_CWORD_CWORD_CWORD, - /* 129 -1 */ CWORD_CWORD_CWORD_CWORD, - /* 130 0 */ CWORD_CWORD_CWORD_CWORD, - /* 131 1 */ CWORD_CWORD_CWORD_CWORD, - /* 132 2 */ CWORD_CWORD_CWORD_CWORD, - /* 133 3 */ CWORD_CWORD_CWORD_CWORD, - /* 134 4 */ CWORD_CWORD_CWORD_CWORD, - /* 135 5 */ CWORD_CWORD_CWORD_CWORD, - /* 136 6 */ CWORD_CWORD_CWORD_CWORD, - /* 137 7 */ CWORD_CWORD_CWORD_CWORD, - /* 138 8 */ CWORD_CWORD_CWORD_CWORD, - /* 139 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, - /* 140 10 "\n" */ CNL_CNL_CNL_CNL, - /* 141 11 */ CWORD_CWORD_CWORD_CWORD, - /* 142 12 */ CWORD_CWORD_CWORD_CWORD, - /* 143 13 */ CWORD_CWORD_CWORD_CWORD, - /* 144 14 */ CWORD_CWORD_CWORD_CWORD, - /* 145 15 */ CWORD_CWORD_CWORD_CWORD, - /* 146 16 */ CWORD_CWORD_CWORD_CWORD, - /* 147 17 */ CWORD_CWORD_CWORD_CWORD, - /* 148 18 */ CWORD_CWORD_CWORD_CWORD, - /* 149 19 */ CWORD_CWORD_CWORD_CWORD, - /* 150 20 */ CWORD_CWORD_CWORD_CWORD, - /* 151 21 */ CWORD_CWORD_CWORD_CWORD, - /* 152 22 */ CWORD_CWORD_CWORD_CWORD, - /* 153 23 */ CWORD_CWORD_CWORD_CWORD, - /* 154 24 */ CWORD_CWORD_CWORD_CWORD, - /* 155 25 */ CWORD_CWORD_CWORD_CWORD, - /* 156 26 */ CWORD_CWORD_CWORD_CWORD, - /* 157 27 */ CWORD_CWORD_CWORD_CWORD, - /* 158 28 */ CWORD_CWORD_CWORD_CWORD, - /* 159 29 */ CWORD_CWORD_CWORD_CWORD, - /* 160 30 */ CWORD_CWORD_CWORD_CWORD, - /* 161 31 */ CWORD_CWORD_CWORD_CWORD, - /* 162 32 " " */ CSPCL_CWORD_CWORD_CWORD, - /* 163 33 "!" */ CWORD_CCTL_CCTL_CWORD, - /* 164 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, - /* 165 35 "#" */ CWORD_CWORD_CWORD_CWORD, - /* 166 36 "$" */ CVAR_CVAR_CWORD_CVAR, - /* 167 37 "%" */ CWORD_CWORD_CWORD_CWORD, - /* 168 38 "&" */ CSPCL_CWORD_CWORD_CWORD, - /* 169 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, - /* 170 40 "(" */ CSPCL_CWORD_CWORD_CLP, - /* 171 41 ")" */ CSPCL_CWORD_CWORD_CRP, - /* 172 42 "*" */ CWORD_CCTL_CCTL_CWORD, - /* 173 43 "+" */ CWORD_CWORD_CWORD_CWORD, - /* 174 44 "," */ CWORD_CWORD_CWORD_CWORD, - /* 175 45 "-" */ CWORD_CCTL_CCTL_CWORD, - /* 176 46 "." */ CWORD_CWORD_CWORD_CWORD, - /* 177 47 "/" */ CWORD_CCTL_CCTL_CWORD, - /* 178 48 "0" */ CWORD_CWORD_CWORD_CWORD, - /* 179 49 "1" */ CWORD_CWORD_CWORD_CWORD, - /* 180 50 "2" */ CWORD_CWORD_CWORD_CWORD, - /* 181 51 "3" */ CWORD_CWORD_CWORD_CWORD, - /* 182 52 "4" */ CWORD_CWORD_CWORD_CWORD, - /* 183 53 "5" */ CWORD_CWORD_CWORD_CWORD, - /* 184 54 "6" */ CWORD_CWORD_CWORD_CWORD, - /* 185 55 "7" */ CWORD_CWORD_CWORD_CWORD, - /* 186 56 "8" */ CWORD_CWORD_CWORD_CWORD, - /* 187 57 "9" */ CWORD_CWORD_CWORD_CWORD, - /* 188 58 ":" */ CWORD_CCTL_CCTL_CWORD, - /* 189 59 ";" */ CSPCL_CWORD_CWORD_CWORD, - /* 190 60 "<" */ CSPCL_CWORD_CWORD_CWORD, - /* 191 61 "=" */ CWORD_CCTL_CCTL_CWORD, - /* 192 62 ">" */ CSPCL_CWORD_CWORD_CWORD, - /* 193 63 "?" */ CWORD_CCTL_CCTL_CWORD, - /* 194 64 "@" */ CWORD_CWORD_CWORD_CWORD, - /* 195 65 "A" */ CWORD_CWORD_CWORD_CWORD, - /* 196 66 "B" */ CWORD_CWORD_CWORD_CWORD, - /* 197 67 "C" */ CWORD_CWORD_CWORD_CWORD, - /* 198 68 "D" */ CWORD_CWORD_CWORD_CWORD, - /* 199 69 "E" */ CWORD_CWORD_CWORD_CWORD, - /* 200 70 "F" */ CWORD_CWORD_CWORD_CWORD, - /* 201 71 "G" */ CWORD_CWORD_CWORD_CWORD, - /* 202 72 "H" */ CWORD_CWORD_CWORD_CWORD, - /* 203 73 "I" */ CWORD_CWORD_CWORD_CWORD, - /* 204 74 "J" */ CWORD_CWORD_CWORD_CWORD, - /* 205 75 "K" */ CWORD_CWORD_CWORD_CWORD, - /* 206 76 "L" */ CWORD_CWORD_CWORD_CWORD, - /* 207 77 "M" */ CWORD_CWORD_CWORD_CWORD, - /* 208 78 "N" */ CWORD_CWORD_CWORD_CWORD, - /* 209 79 "O" */ CWORD_CWORD_CWORD_CWORD, - /* 210 80 "P" */ CWORD_CWORD_CWORD_CWORD, - /* 211 81 "Q" */ CWORD_CWORD_CWORD_CWORD, - /* 212 82 "R" */ CWORD_CWORD_CWORD_CWORD, - /* 213 83 "S" */ CWORD_CWORD_CWORD_CWORD, - /* 214 84 "T" */ CWORD_CWORD_CWORD_CWORD, - /* 215 85 "U" */ CWORD_CWORD_CWORD_CWORD, - /* 216 86 "V" */ CWORD_CWORD_CWORD_CWORD, - /* 217 87 "W" */ CWORD_CWORD_CWORD_CWORD, - /* 218 88 "X" */ CWORD_CWORD_CWORD_CWORD, - /* 219 89 "Y" */ CWORD_CWORD_CWORD_CWORD, - /* 220 90 "Z" */ CWORD_CWORD_CWORD_CWORD, - /* 221 91 "[" */ CWORD_CCTL_CCTL_CWORD, - /* 222 92 "\" */ CBACK_CBACK_CCTL_CBACK, - /* 223 93 "]" */ CWORD_CCTL_CCTL_CWORD, - /* 224 94 "^" */ CWORD_CWORD_CWORD_CWORD, - /* 225 95 "_" */ CWORD_CWORD_CWORD_CWORD, - /* 226 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, - /* 227 97 "a" */ CWORD_CWORD_CWORD_CWORD, - /* 228 98 "b" */ CWORD_CWORD_CWORD_CWORD, - /* 229 99 "c" */ CWORD_CWORD_CWORD_CWORD, - /* 230 100 "d" */ CWORD_CWORD_CWORD_CWORD, - /* 231 101 "e" */ CWORD_CWORD_CWORD_CWORD, - /* 232 102 "f" */ CWORD_CWORD_CWORD_CWORD, - /* 233 103 "g" */ CWORD_CWORD_CWORD_CWORD, - /* 234 104 "h" */ CWORD_CWORD_CWORD_CWORD, - /* 235 105 "i" */ CWORD_CWORD_CWORD_CWORD, - /* 236 106 "j" */ CWORD_CWORD_CWORD_CWORD, - /* 237 107 "k" */ CWORD_CWORD_CWORD_CWORD, - /* 238 108 "l" */ CWORD_CWORD_CWORD_CWORD, - /* 239 109 "m" */ CWORD_CWORD_CWORD_CWORD, - /* 240 110 "n" */ CWORD_CWORD_CWORD_CWORD, - /* 241 111 "o" */ CWORD_CWORD_CWORD_CWORD, - /* 242 112 "p" */ CWORD_CWORD_CWORD_CWORD, - /* 243 113 "q" */ CWORD_CWORD_CWORD_CWORD, - /* 244 114 "r" */ CWORD_CWORD_CWORD_CWORD, - /* 245 115 "s" */ CWORD_CWORD_CWORD_CWORD, - /* 246 116 "t" */ CWORD_CWORD_CWORD_CWORD, - /* 247 117 "u" */ CWORD_CWORD_CWORD_CWORD, - /* 248 118 "v" */ CWORD_CWORD_CWORD_CWORD, - /* 249 119 "w" */ CWORD_CWORD_CWORD_CWORD, - /* 250 120 "x" */ CWORD_CWORD_CWORD_CWORD, - /* 251 121 "y" */ CWORD_CWORD_CWORD_CWORD, - /* 252 122 "z" */ CWORD_CWORD_CWORD_CWORD, - /* 253 123 "{" */ CWORD_CWORD_CWORD_CWORD, - /* 254 124 "|" */ CSPCL_CWORD_CWORD_CWORD, - /* 255 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, - /* 256 126 "~" */ CWORD_CCTL_CCTL_CWORD, - /* 257 127 */ CWORD_CWORD_CWORD_CWORD, + /* 0 */ CWORD_CWORD_CWORD_CWORD, + /* 1 */ CWORD_CWORD_CWORD_CWORD, + /* 2 */ CWORD_CWORD_CWORD_CWORD, + /* 3 */ CWORD_CWORD_CWORD_CWORD, + /* 4 */ CWORD_CWORD_CWORD_CWORD, + /* 5 */ CWORD_CWORD_CWORD_CWORD, + /* 6 */ CWORD_CWORD_CWORD_CWORD, + /* 7 */ CWORD_CWORD_CWORD_CWORD, + /* 8 */ CWORD_CWORD_CWORD_CWORD, + /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD, + /* 10 "\n" */ CNL_CNL_CNL_CNL, + /* 11 */ CWORD_CWORD_CWORD_CWORD, + /* 12 */ CWORD_CWORD_CWORD_CWORD, + /* 13 */ CWORD_CWORD_CWORD_CWORD, + /* 14 */ CWORD_CWORD_CWORD_CWORD, + /* 15 */ CWORD_CWORD_CWORD_CWORD, + /* 16 */ CWORD_CWORD_CWORD_CWORD, + /* 17 */ CWORD_CWORD_CWORD_CWORD, + /* 18 */ CWORD_CWORD_CWORD_CWORD, + /* 19 */ CWORD_CWORD_CWORD_CWORD, + /* 20 */ CWORD_CWORD_CWORD_CWORD, + /* 21 */ CWORD_CWORD_CWORD_CWORD, + /* 22 */ CWORD_CWORD_CWORD_CWORD, + /* 23 */ CWORD_CWORD_CWORD_CWORD, + /* 24 */ CWORD_CWORD_CWORD_CWORD, + /* 25 */ CWORD_CWORD_CWORD_CWORD, + /* 26 */ CWORD_CWORD_CWORD_CWORD, + /* 27 */ CWORD_CWORD_CWORD_CWORD, + /* 28 */ CWORD_CWORD_CWORD_CWORD, + /* 29 */ CWORD_CWORD_CWORD_CWORD, + /* 30 */ CWORD_CWORD_CWORD_CWORD, + /* 31 */ CWORD_CWORD_CWORD_CWORD, + /* 32 " " */ CSPCL_CWORD_CWORD_CWORD, + /* 33 "!" */ CWORD_CCTL_CCTL_CWORD, + /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD, + /* 35 "#" */ CWORD_CWORD_CWORD_CWORD, + /* 36 "$" */ CVAR_CVAR_CWORD_CVAR, + /* 37 "%" */ CWORD_CWORD_CWORD_CWORD, + /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD, + /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD, + /* 40 "(" */ CSPCL_CWORD_CWORD_CLP, + /* 41 ")" */ CSPCL_CWORD_CWORD_CRP, + /* 42 "*" */ CWORD_CCTL_CCTL_CWORD, + /* 43 "+" */ CWORD_CWORD_CWORD_CWORD, + /* 44 "," */ CWORD_CWORD_CWORD_CWORD, + /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, + /* 46 "." */ CWORD_CWORD_CWORD_CWORD, + /* 47 "/" */ CWORD_CCTL_CCTL_CWORD, + /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, + /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, + /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, + /* 51 "3" */ CWORD_CWORD_CWORD_CWORD, + /* 52 "4" */ CWORD_CWORD_CWORD_CWORD, + /* 53 "5" */ CWORD_CWORD_CWORD_CWORD, + /* 54 "6" */ CWORD_CWORD_CWORD_CWORD, + /* 55 "7" */ CWORD_CWORD_CWORD_CWORD, + /* 56 "8" */ CWORD_CWORD_CWORD_CWORD, + /* 57 "9" */ CWORD_CWORD_CWORD_CWORD, + /* 58 ":" */ CWORD_CCTL_CCTL_CWORD, + /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD, + /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD, + /* 61 "=" */ CWORD_CCTL_CCTL_CWORD, + /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD, + /* 63 "?" */ CWORD_CCTL_CCTL_CWORD, + /* 64 "@" */ CWORD_CWORD_CWORD_CWORD, + /* 65 "A" */ CWORD_CWORD_CWORD_CWORD, + /* 66 "B" */ CWORD_CWORD_CWORD_CWORD, + /* 67 "C" */ CWORD_CWORD_CWORD_CWORD, + /* 68 "D" */ CWORD_CWORD_CWORD_CWORD, + /* 69 "E" */ CWORD_CWORD_CWORD_CWORD, + /* 70 "F" */ CWORD_CWORD_CWORD_CWORD, + /* 71 "G" */ CWORD_CWORD_CWORD_CWORD, + /* 72 "H" */ CWORD_CWORD_CWORD_CWORD, + /* 73 "I" */ CWORD_CWORD_CWORD_CWORD, + /* 74 "J" */ CWORD_CWORD_CWORD_CWORD, + /* 75 "K" */ CWORD_CWORD_CWORD_CWORD, + /* 76 "L" */ CWORD_CWORD_CWORD_CWORD, + /* 77 "M" */ CWORD_CWORD_CWORD_CWORD, + /* 78 "N" */ CWORD_CWORD_CWORD_CWORD, + /* 79 "O" */ CWORD_CWORD_CWORD_CWORD, + /* 80 "P" */ CWORD_CWORD_CWORD_CWORD, + /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD, + /* 82 "R" */ CWORD_CWORD_CWORD_CWORD, + /* 83 "S" */ CWORD_CWORD_CWORD_CWORD, + /* 84 "T" */ CWORD_CWORD_CWORD_CWORD, + /* 85 "U" */ CWORD_CWORD_CWORD_CWORD, + /* 86 "V" */ CWORD_CWORD_CWORD_CWORD, + /* 87 "W" */ CWORD_CWORD_CWORD_CWORD, + /* 88 "X" */ CWORD_CWORD_CWORD_CWORD, + /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD, + /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD, + /* 91 "[" */ CWORD_CCTL_CCTL_CWORD, + /* 92 "\" */ CBACK_CBACK_CCTL_CBACK, + /* 93 "]" */ CWORD_CCTL_CCTL_CWORD, + /* 94 "^" */ CWORD_CWORD_CWORD_CWORD, + /* 95 "_" */ CWORD_CWORD_CWORD_CWORD, + /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE, + /* 97 "a" */ CWORD_CWORD_CWORD_CWORD, + /* 98 "b" */ CWORD_CWORD_CWORD_CWORD, + /* 99 "c" */ CWORD_CWORD_CWORD_CWORD, + /* 100 "d" */ CWORD_CWORD_CWORD_CWORD, + /* 101 "e" */ CWORD_CWORD_CWORD_CWORD, + /* 102 "f" */ CWORD_CWORD_CWORD_CWORD, + /* 103 "g" */ CWORD_CWORD_CWORD_CWORD, + /* 104 "h" */ CWORD_CWORD_CWORD_CWORD, + /* 105 "i" */ CWORD_CWORD_CWORD_CWORD, + /* 106 "j" */ CWORD_CWORD_CWORD_CWORD, + /* 107 "k" */ CWORD_CWORD_CWORD_CWORD, + /* 108 "l" */ CWORD_CWORD_CWORD_CWORD, + /* 109 "m" */ CWORD_CWORD_CWORD_CWORD, + /* 110 "n" */ CWORD_CWORD_CWORD_CWORD, + /* 111 "o" */ CWORD_CWORD_CWORD_CWORD, + /* 112 "p" */ CWORD_CWORD_CWORD_CWORD, + /* 113 "q" */ CWORD_CWORD_CWORD_CWORD, + /* 114 "r" */ CWORD_CWORD_CWORD_CWORD, + /* 115 "s" */ CWORD_CWORD_CWORD_CWORD, + /* 116 "t" */ CWORD_CWORD_CWORD_CWORD, + /* 117 "u" */ CWORD_CWORD_CWORD_CWORD, + /* 118 "v" */ CWORD_CWORD_CWORD_CWORD, + /* 119 "w" */ CWORD_CWORD_CWORD_CWORD, + /* 120 "x" */ CWORD_CWORD_CWORD_CWORD, + /* 121 "y" */ CWORD_CWORD_CWORD_CWORD, + /* 122 "z" */ CWORD_CWORD_CWORD_CWORD, + /* 123 "{" */ CWORD_CWORD_CWORD_CWORD, + /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD, + /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR, + /* 126 "~" */ CWORD_CCTL_CCTL_CWORD, + /* 127 del */ CWORD_CWORD_CWORD_CWORD, + /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD, + /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL, + /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL, + /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL, + /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL, + /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL, + /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL, + /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL, + /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL, + /* 137 */ CWORD_CWORD_CWORD_CWORD, + /* 138 */ CWORD_CWORD_CWORD_CWORD, + /* 139 */ CWORD_CWORD_CWORD_CWORD, + /* 140 */ CWORD_CWORD_CWORD_CWORD, + /* 141 */ CWORD_CWORD_CWORD_CWORD, + /* 142 */ CWORD_CWORD_CWORD_CWORD, + /* 143 */ CWORD_CWORD_CWORD_CWORD, + /* 144 */ CWORD_CWORD_CWORD_CWORD, + /* 145 */ CWORD_CWORD_CWORD_CWORD, + /* 146 */ CWORD_CWORD_CWORD_CWORD, + /* 147 */ CWORD_CWORD_CWORD_CWORD, + /* 148 */ CWORD_CWORD_CWORD_CWORD, + /* 149 */ CWORD_CWORD_CWORD_CWORD, + /* 150 */ CWORD_CWORD_CWORD_CWORD, + /* 151 */ CWORD_CWORD_CWORD_CWORD, + /* 152 */ CWORD_CWORD_CWORD_CWORD, + /* 153 */ CWORD_CWORD_CWORD_CWORD, + /* 154 */ CWORD_CWORD_CWORD_CWORD, + /* 155 */ CWORD_CWORD_CWORD_CWORD, + /* 156 */ CWORD_CWORD_CWORD_CWORD, + /* 157 */ CWORD_CWORD_CWORD_CWORD, + /* 158 */ CWORD_CWORD_CWORD_CWORD, + /* 159 */ CWORD_CWORD_CWORD_CWORD, + /* 160 */ CWORD_CWORD_CWORD_CWORD, + /* 161 */ CWORD_CWORD_CWORD_CWORD, + /* 162 */ CWORD_CWORD_CWORD_CWORD, + /* 163 */ CWORD_CWORD_CWORD_CWORD, + /* 164 */ CWORD_CWORD_CWORD_CWORD, + /* 165 */ CWORD_CWORD_CWORD_CWORD, + /* 166 */ CWORD_CWORD_CWORD_CWORD, + /* 167 */ CWORD_CWORD_CWORD_CWORD, + /* 168 */ CWORD_CWORD_CWORD_CWORD, + /* 169 */ CWORD_CWORD_CWORD_CWORD, + /* 170 */ CWORD_CWORD_CWORD_CWORD, + /* 171 */ CWORD_CWORD_CWORD_CWORD, + /* 172 */ CWORD_CWORD_CWORD_CWORD, + /* 173 */ CWORD_CWORD_CWORD_CWORD, + /* 174 */ CWORD_CWORD_CWORD_CWORD, + /* 175 */ CWORD_CWORD_CWORD_CWORD, + /* 176 */ CWORD_CWORD_CWORD_CWORD, + /* 177 */ CWORD_CWORD_CWORD_CWORD, + /* 178 */ CWORD_CWORD_CWORD_CWORD, + /* 179 */ CWORD_CWORD_CWORD_CWORD, + /* 180 */ CWORD_CWORD_CWORD_CWORD, + /* 181 */ CWORD_CWORD_CWORD_CWORD, + /* 182 */ CWORD_CWORD_CWORD_CWORD, + /* 183 */ CWORD_CWORD_CWORD_CWORD, + /* 184 */ CWORD_CWORD_CWORD_CWORD, + /* 185 */ CWORD_CWORD_CWORD_CWORD, + /* 186 */ CWORD_CWORD_CWORD_CWORD, + /* 187 */ CWORD_CWORD_CWORD_CWORD, + /* 188 */ CWORD_CWORD_CWORD_CWORD, + /* 189 */ CWORD_CWORD_CWORD_CWORD, + /* 190 */ CWORD_CWORD_CWORD_CWORD, + /* 191 */ CWORD_CWORD_CWORD_CWORD, + /* 192 */ CWORD_CWORD_CWORD_CWORD, + /* 193 */ CWORD_CWORD_CWORD_CWORD, + /* 194 */ CWORD_CWORD_CWORD_CWORD, + /* 195 */ CWORD_CWORD_CWORD_CWORD, + /* 196 */ CWORD_CWORD_CWORD_CWORD, + /* 197 */ CWORD_CWORD_CWORD_CWORD, + /* 198 */ CWORD_CWORD_CWORD_CWORD, + /* 199 */ CWORD_CWORD_CWORD_CWORD, + /* 200 */ CWORD_CWORD_CWORD_CWORD, + /* 201 */ CWORD_CWORD_CWORD_CWORD, + /* 202 */ CWORD_CWORD_CWORD_CWORD, + /* 203 */ CWORD_CWORD_CWORD_CWORD, + /* 204 */ CWORD_CWORD_CWORD_CWORD, + /* 205 */ CWORD_CWORD_CWORD_CWORD, + /* 206 */ CWORD_CWORD_CWORD_CWORD, + /* 207 */ CWORD_CWORD_CWORD_CWORD, + /* 208 */ CWORD_CWORD_CWORD_CWORD, + /* 209 */ CWORD_CWORD_CWORD_CWORD, + /* 210 */ CWORD_CWORD_CWORD_CWORD, + /* 211 */ CWORD_CWORD_CWORD_CWORD, + /* 212 */ CWORD_CWORD_CWORD_CWORD, + /* 213 */ CWORD_CWORD_CWORD_CWORD, + /* 214 */ CWORD_CWORD_CWORD_CWORD, + /* 215 */ CWORD_CWORD_CWORD_CWORD, + /* 216 */ CWORD_CWORD_CWORD_CWORD, + /* 217 */ CWORD_CWORD_CWORD_CWORD, + /* 218 */ CWORD_CWORD_CWORD_CWORD, + /* 219 */ CWORD_CWORD_CWORD_CWORD, + /* 220 */ CWORD_CWORD_CWORD_CWORD, + /* 221 */ CWORD_CWORD_CWORD_CWORD, + /* 222 */ CWORD_CWORD_CWORD_CWORD, + /* 223 */ CWORD_CWORD_CWORD_CWORD, + /* 224 */ CWORD_CWORD_CWORD_CWORD, + /* 225 */ CWORD_CWORD_CWORD_CWORD, + /* 226 */ CWORD_CWORD_CWORD_CWORD, + /* 227 */ CWORD_CWORD_CWORD_CWORD, + /* 228 */ CWORD_CWORD_CWORD_CWORD, + /* 229 */ CWORD_CWORD_CWORD_CWORD, + /* 230 */ CWORD_CWORD_CWORD_CWORD, + /* 231 */ CWORD_CWORD_CWORD_CWORD, + /* 232 */ CWORD_CWORD_CWORD_CWORD, + /* 233 */ CWORD_CWORD_CWORD_CWORD, + /* 234 */ CWORD_CWORD_CWORD_CWORD, + /* 235 */ CWORD_CWORD_CWORD_CWORD, + /* 236 */ CWORD_CWORD_CWORD_CWORD, + /* 237 */ CWORD_CWORD_CWORD_CWORD, + /* 238 */ CWORD_CWORD_CWORD_CWORD, + /* 239 */ CWORD_CWORD_CWORD_CWORD, + /* 230 */ CWORD_CWORD_CWORD_CWORD, + /* 241 */ CWORD_CWORD_CWORD_CWORD, + /* 242 */ CWORD_CWORD_CWORD_CWORD, + /* 243 */ CWORD_CWORD_CWORD_CWORD, + /* 244 */ CWORD_CWORD_CWORD_CWORD, + /* 245 */ CWORD_CWORD_CWORD_CWORD, + /* 246 */ CWORD_CWORD_CWORD_CWORD, + /* 247 */ CWORD_CWORD_CWORD_CWORD, + /* 248 */ CWORD_CWORD_CWORD_CWORD, + /* 249 */ CWORD_CWORD_CWORD_CWORD, + /* 250 */ CWORD_CWORD_CWORD_CWORD, + /* 251 */ CWORD_CWORD_CWORD_CWORD, + /* 252 */ CWORD_CWORD_CWORD_CWORD, + /* 253 */ CWORD_CWORD_CWORD_CWORD, + /* 254 */ CWORD_CWORD_CWORD_CWORD, + /* 255 */ CWORD_CWORD_CWORD_CWORD, + /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE, +# if ENABLE_ASH_ALIAS + /* PEOA */ CSPCL_CIGN_CIGN_CIGN, +# endif }; -#define SIT(c, syntax) (S_I_T[(int)syntax_index_table[((int)c)+SYNBASE]][syntax]) +# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf) -#endif /* USE_SIT_FUNCTION */ +#endif /* !USE_SIT_FUNCTION */ /* ============ Alias handling */ @@ -3196,7 +3137,7 @@ /* * TODO - sort output */ -static int +static int FAST_FUNC aliascmd(int argc UNUSED_PARAM, char **argv) { char *n, *v; @@ -3231,7 +3172,7 @@ return ret; } -static int +static int FAST_FUNC unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int i; @@ -3258,14 +3199,14 @@ /* ============ jobs.c */ /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ -#define FORK_FG 0 -#define FORK_BG 1 +#define FORK_FG 0 +#define FORK_BG 1 #define FORK_NOJOB 2 /* mode flags for showjob(s) */ -#define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ -#define SHOW_PID 0x04 /* include process pid */ -#define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ +#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ +#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ +#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ /* * A job structure contains information about a job. A job is either a @@ -3273,11 +3214,10 @@ * latter case, pidlist will be non-NULL, and will point to a -1 terminated * array of pids. */ - struct procstat { - pid_t pid; /* process id */ - int status; /* last process status from wait() */ - char *cmd; /* text of command being run */ + pid_t ps_pid; /* process id */ + int ps_status; /* last process status from wait() */ + char *ps_cmd; /* text of command being run */ }; struct job { @@ -3303,9 +3243,6 @@ }; static struct job *makejob(/*union node *,*/ int); -#if !JOBS -#define forkshell(job, node, mode) forkshell(job, mode) -#endif static int forkshell(struct job *, union node *, int); static int waitforjob(struct job *); @@ -3318,87 +3255,129 @@ #endif /* + * Ignore a signal. + */ +static void +ignoresig(int signo) +{ + /* Avoid unnecessary system calls. Is it already SIG_IGNed? */ + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + /* No, need to do it */ + signal(signo, SIG_IGN); + } + sigmode[signo - 1] = S_HARD_IGN; +} + +/* + * Signal handler. Only one usage site - in setsignal() + */ +static void +onsig(int signo) +{ + gotsig[signo - 1] = 1; + + if (signo == SIGINT && !trap[SIGINT]) { + if (!suppress_int) { + pending_sig = 0; + raise_interrupt(); /* does not return */ + } + pending_int = 1; + } else { + pending_sig = signo; + } +} + +/* * Set the signal handler for the specified signal. The routine figures * out what it should be set to. */ static void setsignal(int signo) { - int action; - char *t, tsig; + char *t; + char cur_act, new_act; struct sigaction act; t = trap[signo]; - action = S_IGN; - if (t == NULL) - action = S_DFL; - else if (*t != '\0') - action = S_CATCH; - if (rootshell && action == S_DFL) { + new_act = S_DFL; + if (t != NULL) { /* trap for this sig is set */ + new_act = S_CATCH; + if (t[0] == '\0') /* trap is "": ignore this sig */ + new_act = S_IGN; + } + + if (rootshell && new_act == S_DFL) { switch (signo) { case SIGINT: if (iflag || minusc || sflag == 0) - action = S_CATCH; + new_act = S_CATCH; break; case SIGQUIT: #if DEBUG if (debug) break; #endif - /* FALLTHROUGH */ + /* man bash: + * "In all cases, bash ignores SIGQUIT. Non-builtin + * commands run by bash have signal handlers + * set to the values inherited by the shell + * from its parent". */ + new_act = S_IGN; + break; case SIGTERM: if (iflag) - action = S_IGN; + new_act = S_IGN; break; #if JOBS case SIGTSTP: case SIGTTOU: if (mflag) - action = S_IGN; + new_act = S_IGN; break; #endif } } +//TODO: if !rootshell, we reset SIGQUIT to DFL, +//whereas we have to restore it to what shell got on entry +//from the parent. See comment above t = &sigmode[signo - 1]; - tsig = *t; - if (tsig == 0) { - /* - * current setting unknown - */ - if (sigaction(signo, NULL, &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. - */ + cur_act = *t; + if (cur_act == 0) { + /* current setting is not yet known */ + if (sigaction(signo, NULL, &act)) { + /* pretend it worked; maybe we should give a warning, + * but other shells don't. We don't alter sigmode, + * so we retry every time. + * btw, in Linux it never fails. --vda */ return; } - tsig = S_RESET; /* force to be set */ if (act.sa_handler == SIG_IGN) { - tsig = S_HARD_IGN; + cur_act = S_HARD_IGN; if (mflag && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU) ) { - tsig = S_IGN; /* don't hard ignore these */ + cur_act = S_IGN; /* don't hard ignore these */ } } } - if (tsig == S_HARD_IGN || tsig == action) + if (cur_act == S_HARD_IGN || cur_act == new_act) return; + act.sa_handler = SIG_DFL; - switch (action) { + switch (new_act) { case S_CATCH: act.sa_handler = onsig; + act.sa_flags = 0; /* matters only if !DFL and !IGN */ + sigfillset(&act.sa_mask); /* ditto */ break; case S_IGN: act.sa_handler = SIG_IGN; break; } - *t = action; - act.sa_flags = 0; - sigfillset(&act.sa_mask); sigaction_set(signo, &act); + + *t = new_act; } /* mode flags for set_curjob */ @@ -3491,7 +3470,7 @@ { struct job *jp; struct job *found; - const char *err_msg = "No such job: %s"; + const char *err_msg = "%s: no such job"; unsigned num; int c; const char *p; @@ -3527,7 +3506,6 @@ } if (is_number(p)) { -// TODO: number() instead? It does error checking... num = atoi(p); if (num < njobs) { jp = jobtab + num - 1; @@ -3543,11 +3521,9 @@ p++; } - found = 0; - while (1) { - if (!jp) - goto err; - if (match(jp->ps[0].cmd, p)) { + found = NULL; + while (jp) { + if (match(jp->ps[0].ps_cmd, p)) { if (found) goto err; found = jp; @@ -3555,6 +3531,9 @@ } jp = jp->prev_job; } + if (!found) + goto err; + jp = found; gotit: #if JOBS @@ -3578,8 +3557,8 @@ INT_OFF; for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { - if (ps->cmd != nullstr) - free(ps->cmd); + if (ps->ps_cmd != nullstr) + free(ps->ps_cmd); } if (jp->ps != &jp->ps0) free(jp->ps); @@ -3673,7 +3652,7 @@ doing_jobctl = on; } -static int +static int FAST_FUNC killcmd(int argc, char **argv) { int i = 1; @@ -3681,7 +3660,7 @@ do { if (argv[i][0] == '%') { struct job *jp = getjob(argv[i], 0); - unsigned pid = jp->ps[0].pid; + unsigned pid = jp->ps[0].ps_pid; /* Enough space for ' -NNN' */ argv[i] = alloca(sizeof(int)*3 + 3); /* kill_main has matching code to expect @@ -3695,15 +3674,15 @@ } static void -showpipe(struct job *jp, FILE *out) +showpipe(struct job *jp /*, FILE *out*/) { - struct procstat *sp; - struct procstat *spend; + struct procstat *ps; + struct procstat *psend; - spend = jp->ps + jp->nprocs; - for (sp = jp->ps + 1; sp < spend; sp++) - fprintf(out, " | %s", sp->cmd); - outcslow('\n', out); + psend = jp->ps + jp->nprocs; + for (ps = jp->ps + 1; ps < psend; ps++) + printf(" | %s", ps->ps_cmd); + outcslow('\n', stdout); flush_stdout_stderr(); } @@ -3720,15 +3699,15 @@ if (jp->state == JOBDONE) goto out; jp->state = JOBRUNNING; - pgid = jp->ps->pid; + pgid = jp->ps[0].ps_pid; if (mode == FORK_FG) xtcsetpgrp(ttyfd, pgid); killpg(pgid, SIGCONT); ps = jp->ps; i = jp->nprocs; do { - if (WIFSTOPPED(ps->status)) { - ps->status = -1; + if (WIFSTOPPED(ps->ps_status)) { + ps->ps_status = -1; } ps++; } while (--i); @@ -3738,26 +3717,24 @@ return status; } -static int +static int FAST_FUNC fg_bgcmd(int argc UNUSED_PARAM, char **argv) { struct job *jp; - FILE *out; int mode; int retval; mode = (**argv == 'f') ? FORK_FG : FORK_BG; nextopt(nullstr); argv = argptr; - out = stdout; do { jp = getjob(*argv, 1); if (mode == FORK_BG) { set_curjob(jp, CUR_RUNNING); - fprintf(out, "[%d] ", jobno(jp)); + printf("[%d] ", jobno(jp)); } - outstr(jp->ps->cmd, out); - showpipe(jp, out); + out1str(jp->ps[0].ps_cmd); + showpipe(jp /*, stdout*/); retval = restartjob(jp, mode); } while (*argv && *++argv); return retval; @@ -3816,47 +3793,43 @@ /* Do a wait system call. If job control is compiled in, we accept * stopped processes. wait_flags may have WNOHANG, preventing blocking. * NB: _not_ safe_waitpid, we need to detect EINTR */ - pid = waitpid(-1, &status, - (doing_jobctl ? (wait_flags | WUNTRACED) : wait_flags)); - TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", pid, status, errno, strerror(errno))); - - if (pid <= 0) { - /* If we were doing blocking wait and (probably) got EINTR, - * check for pending sigs received while waiting. - * (NB: can be moved into callers if needed) */ - if (wait_flags == DOWAIT_BLOCK && pendingsig) - raise_exception(EXSIG); + if (doing_jobctl) + wait_flags |= WUNTRACED; + pid = waitpid(-1, &status, wait_flags); + TRACE(("wait returns pid=%d, status=0x%x, errno=%d(%s)\n", + pid, status, errno, strerror(errno))); + if (pid <= 0) return pid; - } + INT_OFF; thisjob = NULL; for (jp = curjob; jp; jp = jp->prev_job) { - struct procstat *sp; - struct procstat *spend; + struct procstat *ps; + struct procstat *psend; if (jp->state == JOBDONE) continue; state = JOBDONE; - spend = jp->ps + jp->nprocs; - sp = jp->ps; + ps = jp->ps; + psend = ps + jp->nprocs; do { - if (sp->pid == pid) { + if (ps->ps_pid == pid) { TRACE(("Job %d: changing status of proc %d " "from 0x%x to 0x%x\n", - jobno(jp), pid, sp->status, status)); - sp->status = status; + jobno(jp), pid, ps->ps_status, status)); + ps->ps_status = status; thisjob = jp; } - if (sp->status == -1) + if (ps->ps_status == -1) state = JOBRUNNING; #if JOBS if (state == JOBRUNNING) continue; - if (WIFSTOPPED(sp->status)) { - jp->stopstatus = sp->status; + if (WIFSTOPPED(ps->ps_status)) { + jp->stopstatus = ps->ps_status; state = JOBSTOPPED; } #endif - } while (++sp < spend); + } while (++ps < psend); if (thisjob) goto gotjob; } @@ -3899,6 +3872,15 @@ return pid; } +static int +blocking_wait_with_raise_on_sig(struct job *job) +{ + pid_t pid = dowait(DOWAIT_BLOCK, job); + if (pid <= 0 && pending_sig) + raise_exception(EXSIG); + return pid; +} + #if JOBS static void showjob(FILE *out, struct job *jp, int mode) @@ -3911,9 +3893,9 @@ ps = jp->ps; - if (mode & SHOW_PGID) { + if (mode & SHOW_ONLY_PGID) { /* jobs -p */ /* just output process (group) id of pipeline */ - fprintf(out, "%d\n", ps->pid); + fprintf(out, "%d\n", ps->ps_pid); return; } @@ -3921,12 +3903,12 @@ indent_col = col; if (jp == curjob) - s[col - 2] = '+'; + s[col - 3] = '+'; else if (curjob && jp == curjob->prev_job) - s[col - 2] = '-'; + s[col - 3] = '-'; - if (mode & SHOW_PID) - col += fmtstr(s + col, 16, "%d ", ps->pid); + if (mode & SHOW_PIDS) + col += fmtstr(s + col, 16, "%d ", ps->ps_pid); psend = ps + jp->nprocs; @@ -3934,30 +3916,37 @@ strcpy(s + col, "Running"); col += sizeof("Running") - 1; } else { - int status = psend[-1].status; + int status = psend[-1].ps_status; if (jp->state == JOBSTOPPED) status = jp->stopstatus; col += sprint_status(s + col, status, 0); } + /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ + /* This loop either prints " | | " line + * or prints several "PID | " lines, + * depending on SHOW_PIDS bit. + * We do not print status of individual processes + * between PID and . bash does it, but not very well: + * first line shows overall job status, not process status, + * making it impossible to know 1st process status. + */ goto start; - do { /* for each process */ - col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3; + s[0] = '\0'; + col = 33; + if (mode & SHOW_PIDS) + col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; start: - fprintf(out, "%s%*c%s", - s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd + fprintf(out, "%s%*c%s%s", + s, + 33 - col >= 0 ? 33 - col : 0, ' ', + ps == jp->ps ? "" : "| ", + ps->ps_cmd ); - if (!(mode & SHOW_PID)) { - showpipe(jp, out); - break; - } - if (++ps == psend) { - outcslow('\n', out); - break; - } - } while (1); + } while (++ps != psend); + outcslow('\n', out); jp->changed = 0; @@ -3976,9 +3965,9 @@ { struct job *jp; - TRACE(("showjobs(%x) called\n", mode)); + TRACE(("showjobs(0x%x) called\n", mode)); - /* If not even one job changed, there is nothing to do */ + /* Handle all finished jobs */ while (dowait(DOWAIT_NONBLOCK, NULL) > 0) continue; @@ -3989,38 +3978,49 @@ } } -static int +static int FAST_FUNC jobscmd(int argc UNUSED_PARAM, char **argv) { int mode, m; mode = 0; - while ((m = nextopt("lp"))) { + while ((m = nextopt("lp")) != '\0') { if (m == 'l') - mode = SHOW_PID; + mode |= SHOW_PIDS; else - mode = SHOW_PGID; + mode |= SHOW_ONLY_PGID; } argv = argptr; if (*argv) { do - showjob(stdout, getjob(*argv,0), mode); + showjob(stdout, getjob(*argv, 0), mode); while (*++argv); - } else + } else { showjobs(stdout, mode); + } return 0; } #endif /* JOBS */ +/* Called only on finished or stopped jobs (no members are running) */ static int getstatus(struct job *job) { int status; int retval; + struct procstat *ps; + + /* Fetch last member's status */ + ps = job->ps + job->nprocs - 1; + status = ps->ps_status; + if (pipefail) { + /* "set -o pipefail" mode: use last _nonzero_ status */ + while (status == 0 && --ps >= job->ps) + status = ps->ps_status; + } - status = job->ps[job->nprocs - 1].status; retval = WEXITSTATUS(status); if (!WIFEXITED(status)) { #if JOBS @@ -4037,21 +4037,19 @@ } retval += 128; } - TRACE(("getstatus: job %d, nproc %d, status %x, retval %x\n", + TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n", jobno(job), job->nprocs, status, retval)); return retval; } -static int +static int FAST_FUNC waitcmd(int argc UNUSED_PARAM, char **argv) { struct job *job; int retval; struct job *jp; -// exsig++; -// xbarrier(); - if (pendingsig) + if (pending_sig) raise_exception(EXSIG); nextopt(nullstr); @@ -4070,7 +4068,14 @@ jp->waited = 1; jp = jp->prev_job; } - dowait(DOWAIT_BLOCK, NULL); + /* man bash: + * "When bash is waiting for an asynchronous command via + * the wait builtin, the reception of a signal for which a trap + * has been set will cause the wait builtin to return immediately + * with an exit status greater than 128, immediately after which + * the trap is executed." + * Do we do it that way? */ + blocking_wait_with_raise_on_sig(NULL); } } @@ -4082,7 +4087,7 @@ while (1) { if (!job) goto repeat; - if (job->ps[job->nprocs - 1].pid == pid) + if (job->ps[job->nprocs - 1].ps_pid == pid) break; job = job->prev_job; } @@ -4090,11 +4095,10 @@ job = getjob(*argv, 0); /* loop until process terminated or stopped */ while (job->state == JOBRUNNING) - dowait(DOWAIT_BLOCK, NULL); + blocking_wait_with_raise_on_sig(NULL); job->waited = 1; retval = getstatus(job); - repeat: - ; + repeat: ; } while (*++argv); ret: @@ -4202,18 +4206,20 @@ static const char vstype[VSTYPE + 1][3] = { "", "}", "-", "+", "?", "=", "%", "%%", "#", "##" - USE_ASH_BASH_COMPAT(, ":", "/", "//") + IF_ASH_BASH_COMPAT(, ":", "/", "//") }; const char *p, *str; - char c, cc[2] = " "; + char cc[2]; char *nextc; - int subtype = 0; + unsigned char c; + unsigned char subtype = 0; int quoted = 0; + cc[1] = '\0'; nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc); p = s; - while ((c = *p++) != 0) { + while ((c = *p++) != '\0') { str = NULL; switch (c) { case CTLESC: @@ -4241,7 +4247,7 @@ case CTLBACKQ+CTLQUOTE: str = "\"$(...)\""; goto dostr; -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT case CTLARI: str = "$(("; goto dostr; @@ -4281,10 +4287,11 @@ if (!str) continue; dostr: - while ((c = *str++)) { + while ((c = *str++) != '\0') { USTPUTC(c, nextc); } - } + } /* while *p++ not NUL */ + if (quoted & 1) { USTPUTC('"', nextc); } @@ -4358,11 +4365,12 @@ cmdputs("if "); cmdtxt(n->nif.test); cmdputs("; then "); - n = n->nif.ifpart; if (n->nif.elsepart) { - cmdtxt(n); + cmdtxt(n->nif.ifpart); cmdputs("; else "); n = n->nif.elsepart; + } else { + n = n->nif.ifpart; } p = "; fi"; goto dotail; @@ -4499,9 +4507,11 @@ for (tp = trap; tp < &trap[NSIG]; tp++) { if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ INT_OFF; - free(*tp); + if (trap_ptr == trap) + free(*tp); + /* else: it "belongs" to trap_ptr vector, don't free */ *tp = NULL; - if (tp != &trap[0]) + if ((tp - trap) != 0) setsignal(tp - trap); INT_ON; } @@ -4512,8 +4522,8 @@ static void closescript(void); /* Called after fork(), in child */ -static void -forkchild(struct job *jp, /*union node *n,*/ int mode) +static NOINLINE void +forkchild(struct job *jp, union node *n, int mode) { int oldlvl; @@ -4521,7 +4531,58 @@ oldlvl = shlvl; shlvl++; + /* man bash: "Non-builtin commands run by bash have signal handlers + * set to the values inherited by the shell from its parent". + * Do we do it correctly? */ + closescript(); + + if (mode == FORK_NOJOB /* is it `xxx` ? */ + && n && n->type == NCMD /* is it single cmd? */ + /* && n->ncmd.args->type == NARG - always true? */ + && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0 + && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */ + /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */ + ) { + TRACE(("Trap hack\n")); + /* Awful hack for `trap` or $(trap). + * + * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html + * contains an example where "trap" is executed in a subshell: + * + * save_traps=$(trap) + * ... + * eval "$save_traps" + * + * Standard does not say that "trap" in subshell shall print + * parent shell's traps. It only says that its output + * must have suitable form, but then, in the above example + * (which is not supposed to be normative), it implies that. + * + * bash (and probably other shell) does implement it + * (traps are reset to defaults, but "trap" still shows them), + * but as a result, "trap" logic is hopelessly messed up: + * + * # trap + * trap -- 'echo Ho' SIGWINCH <--- we have a handler + * # (trap) <--- trap is in subshell - no output (correct, traps are reset) + * # true | trap <--- trap is in subshell - no output (ditto) + * # echo `true | trap` <--- in subshell - output (but traps are reset!) + * trap -- 'echo Ho' SIGWINCH + * # echo `(trap)` <--- in subshell in subshell - output + * trap -- 'echo Ho' SIGWINCH + * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! + * trap -- 'echo Ho' SIGWINCH + * + * The rules when to forget and when to not forget traps + * get really complex and nonsensical. + * + * Our solution: ONLY bare $(trap) or `trap` is special. + */ + /* Save trap handler strings for trap builtin to print */ + trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap)); + /* Fall through into clearing traps */ + } clear_traps(); #if JOBS /* do job control only in root shell */ @@ -4532,9 +4593,9 @@ if (jp->nprocs == 0) pgrp = getpid(); else - pgrp = jp->ps[0].pid; - /* This can fail because we are doing it in the parent also */ - (void)setpgid(0, pgrp); + pgrp = jp->ps[0].ps_pid; + /* this can fail because we are doing it in the parent also */ + setpgid(0, pgrp); if (mode == FORK_FG) xtcsetpgrp(ttyfd, pgrp); setsignal(SIGTSTP); @@ -4542,19 +4603,42 @@ } else #endif if (mode == FORK_BG) { + /* man bash: "When job control is not in effect, + * asynchronous commands ignore SIGINT and SIGQUIT" */ ignoresig(SIGINT); ignoresig(SIGQUIT); if (jp->nprocs == 0) { close(0); if (open(bb_dev_null, O_RDONLY) != 0) - ash_msg_and_raise_error("can't open %s", bb_dev_null); + ash_msg_and_raise_error("can't open '%s'", bb_dev_null); } } - if (!oldlvl && iflag) { - setsignal(SIGINT); + if (!oldlvl) { + if (iflag) { /* why if iflag only? */ + setsignal(SIGINT); + setsignal(SIGTERM); + } + /* man bash: + * "In all cases, bash ignores SIGQUIT. Non-builtin + * commands run by bash have signal handlers + * set to the values inherited by the shell + * from its parent". + * Take care of the second rule: */ setsignal(SIGQUIT); - setsignal(SIGTERM); } +#if JOBS + if (n && n->type == NCMD + && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0 + ) { + TRACE(("Job hack\n")); + /* "jobs": we do not want to clear job list for it, + * instead we remove only _its_ own_ job from job list. + * This makes "jobs .... | cat" more useful. + */ + freejob(curjob); + return; + } +#endif for (jp = curjob; jp; jp = jp->prev_job) freejob(jp); jobless = 0; @@ -4581,7 +4665,7 @@ if (jp->nprocs == 0) pgrp = pid; else - pgrp = jp->ps[0].pid; + pgrp = jp->ps[0].ps_pid; /* This can fail because we are doing it in the child also */ setpgid(pid, pgrp); } @@ -4592,12 +4676,12 @@ } if (jp) { struct procstat *ps = &jp->ps[jp->nprocs++]; - ps->pid = pid; - ps->status = -1; - ps->cmd = nullstr; + ps->ps_pid = pid; + ps->ps_status = -1; + ps->ps_cmd = nullstr; #if JOBS if (doing_jobctl && n) - ps->cmd = commandtext(n); + ps->ps_cmd = commandtext(n); #endif } } @@ -4615,22 +4699,24 @@ freejob(jp); ash_msg_and_raise_error("can't fork"); } - if (pid == 0) - forkchild(jp, /*n,*/ mode); - else + if (pid == 0) { + CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ + forkchild(jp, n, mode); + } else { forkparent(jp, n, mode, pid); + } return pid; } /* * Wait for job to finish. * - * Under job control we have the problem that while a child process is - * running interrupts generated by the user are sent to the child but not - * to the shell. This means that an infinite loop started by an inter- - * active user may be hard to kill. With job control turned off, an - * interactive user may place an interactive program inside a loop. If - * the interactive program catches interrupts, the user doesn't want + * Under job control we have the problem that while a child process + * is running interrupts generated by the user are sent to the child + * but not to the shell. This means that an infinite loop started by + * an interactive user may be hard to kill. With job control turned off, + * an interactive user may place an interactive program inside a loop. + * If the interactive program catches interrupts, the user doesn't want * these interrupts to also abort the loop. The approach we take here * is to have the shell ignore interrupt signals while waiting for a * foreground process to terminate, and then send itself an interrupt @@ -4648,9 +4734,43 @@ int st; TRACE(("waitforjob(%%%d) called\n", jobno(jp))); + + INT_OFF; while (jp->state == JOBRUNNING) { + /* In non-interactive shells, we _can_ get + * a keyboard signal here and be EINTRed, + * but we just loop back, waiting for command to complete. + * + * man bash: + * "If bash is waiting for a command to complete and receives + * a signal for which a trap has been set, the trap + * will not be executed until the command completes." + * + * Reality is that even if trap is not set, bash + * will not act on the signal until command completes. + * Try this. sleep5intoff.c: + * #include + * #include + * int main() { + * sigset_t set; + * sigemptyset(&set); + * sigaddset(&set, SIGINT); + * sigaddset(&set, SIGQUIT); + * sigprocmask(SIG_BLOCK, &set, NULL); + * sleep(5); + * return 0; + * } + * $ bash -c './sleep5intoff; echo hi' + * ^C^C^C^C <--- pressing ^C once a second + * $ _ + * $ bash -c './sleep5intoff; echo hi' + * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT) + * $ _ + */ dowait(DOWAIT_BLOCK, jp); } + INT_ON; + st = getstatus(jp); #if JOBS if (jp->jobctl) { @@ -4786,12 +4906,10 @@ if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { /* child */ close(pip[0]); - signal(SIGINT, SIG_IGN); - signal(SIGQUIT, SIG_IGN); - signal(SIGHUP, SIG_IGN); -#ifdef SIGTSTP - signal(SIGTSTP, SIG_IGN); -#endif + ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); + ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); + ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); + ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); signal(SIGPIPE, SIG_DFL); if (redir->type == NHERE) full_write(pip[1], redir->nhere.doc->narg.text, len); @@ -4911,7 +5029,7 @@ struct redirtab *next; int nullredirs; int pair_count; - struct two_fd_t two_fd[0]; + struct two_fd_t two_fd[]; }; #define redirlist (G_var.redirlist) @@ -4990,7 +5108,7 @@ do { sv_pos++; #if ENABLE_ASH_BASH_COMPAT - if (redir->nfile.type == NTO2) + if (tmp->nfile.type == NTO2) sv_pos++; #endif tmp = tmp->nfile.next; @@ -5168,7 +5286,7 @@ redirect(redir, flags); } exception_handler = savehandler; - if (err && exception != EXERROR) + if (err && exception_type != EXERROR) longjmp(exception_handler->loc, 1); RESTORE_INT(saveint); return err; @@ -5180,17 +5298,33 @@ * We have to deal with backquotes, shell variables, and file metacharacters. */ -#if ENABLE_ASH_MATH_SUPPORT_64 -typedef int64_t arith_t; -#define arith_t_type long long -#else -typedef long arith_t; -#define arith_t_type long -#endif +#if ENABLE_SH_MATH_SUPPORT +static arith_t +ash_arith(const char *s) +{ + arith_eval_hooks_t math_hooks; + arith_t result; + int errcode = 0; + + math_hooks.lookupvar = lookupvar; + math_hooks.setvar = setvar2; + math_hooks.endofname = endofname; + + INT_OFF; + result = arith(s, &errcode, &math_hooks); + if (errcode < 0) { + if (errcode == -3) + ash_msg_and_raise_error("exponent less than 0"); + if (errcode == -2) + ash_msg_and_raise_error("divide by zero"); + if (errcode == -5) + ash_msg_and_raise_error("expression recursion loop detected"); + raise_error_syntax(s); + } + INT_ON; -#if ENABLE_ASH_MATH_SUPPORT -static arith_t dash_arith(const char *); -static arith_t arith(const char *expr, int *perrcode); + return result; +} #endif /* @@ -5206,7 +5340,7 @@ #define EXP_WORD 0x80 /* expand word in parameter expansion */ #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ /* - * _rmescape() flags + * rmescape() flags */ #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ @@ -5250,11 +5384,7 @@ int len; expdest = makestrspace(32, expdest); -#if ENABLE_ASH_MATH_SUPPORT_64 - len = fmtstr(expdest, 32, "%lld", (long long) num); -#else - len = fmtstr(expdest, 32, "%ld", num); -#endif + len = fmtstr(expdest, 32, arith_t_fmt, num); STADJUST(len, expdest); return len; } @@ -5264,7 +5394,7 @@ { size_t esc = 0; - while (p > start && *--p == CTLESC) { + while (p > start && (unsigned char)*--p == CTLESC) { esc++; } return esc; @@ -5274,19 +5404,19 @@ * Remove any CTLESC characters from a string. */ static char * -_rmescapes(char *str, int flag) +rmescapes(char *str, int flag) { static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; char *p, *q, *r; unsigned inquotes; - int notescaped; - int globbing; + unsigned protect_against_glob; + unsigned globbing; p = strpbrk(str, qchars); - if (!p) { + if (!p) return str; - } + q = p; r = str; if (flag & RMESCAPE_ALLOC) { @@ -5305,28 +5435,33 @@ q = (char *)memcpy(q, str, len) + len; } } + inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; globbing = flag & RMESCAPE_GLOB; - notescaped = globbing; + protect_against_glob = globbing; while (*p) { - if (*p == CTLQUOTEMARK) { + if ((unsigned char)*p == CTLQUOTEMARK) { +// TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 +// (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? +// Note: both inquotes and protect_against_glob only affect whether +// CTLESC, gets converted to or to \ inquotes = ~inquotes; p++; - notescaped = globbing; + protect_against_glob = globbing; continue; } if (*p == '\\') { /* naked back slash */ - notescaped = 0; + protect_against_glob = 0; goto copy; } - if (*p == CTLESC) { + if ((unsigned char)*p == CTLESC) { p++; - if (notescaped && inquotes && *p != '/') { + if (protect_against_glob && inquotes && *p != '/') { *q++ = '\\'; } } - notescaped = globbing; + protect_against_glob = globbing; copy: *q++ = *p++; } @@ -5337,8 +5472,6 @@ } return r; } -#define rmescapes(p) _rmescapes((p), 0) - #define pmatch(a, b) !fnmatch((a), (b), 0) /* @@ -5353,7 +5486,7 @@ if (quoted) { flag |= RMESCAPE_QUOTED; } - return _rmescapes((char *)pattern, flag); + return rmescapes((char *)pattern, flag); } /* @@ -5364,14 +5497,17 @@ { char *q = expdest; - q = makestrspace(len * 2, q); + q = makestrspace(quotes ? len * 2 : len, q); while (len--) { - int c = signed_char2int(*p++); - if (!c) + unsigned char c = *p++; + if (c == '\0') continue; - if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) - USTPUTC(CTLESC, q); + if (quotes) { + int n = SIT(c, syntax); + if (n == CCTL || n == CBACK) + USTPUTC(CTLESC, q); + } USTPUTC(c, q); } @@ -5448,13 +5584,13 @@ } static char * -exptilde(char *startp, char *p, int flag) +exptilde(char *startp, char *p, int flags) { - char c; + unsigned char c; char *name; struct passwd *pw; const char *home; - int quotes = flag & (EXP_FULL | EXP_CASE); + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); int startloc; name = p + 1; @@ -5466,7 +5602,7 @@ case CTLQUOTEMARK: return startp; case ':': - if (flag & EXP_VARTILDE) + if (flags & EXP_VARTILDE) goto done; break; case '/': @@ -5514,7 +5650,7 @@ #define EV_EXIT 01 /* exit after evaluating tree */ static void evaltree(union node *, int); -static void +static void FAST_FUNC evalbackcmd(union node *n, struct backcmd *result) { int saveherefd; @@ -5523,9 +5659,8 @@ result->buf = NULL; result->nleft = 0; result->jp = NULL; - if (n == NULL) { + if (n == NULL) goto out; - } saveherefd = herefd; herefd = -1; @@ -5619,7 +5754,7 @@ stackblock() + startloc)); } -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT /* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. @@ -5632,7 +5767,7 @@ int flag; int len; - /* ifsfree(); */ + /* ifsfree(); */ /* * This routine is slightly over-complicated for @@ -5646,7 +5781,7 @@ do { int esc; - while (*p != CTLARI) { + while ((unsigned char)*p != CTLARI) { p--; #if DEBUG if (p < start) { @@ -5672,9 +5807,9 @@ expdest = p; if (quotes) - rmescapes(p + 2); + rmescapes(p + 2, 0); - len = cvtnum(dash_arith(p + 2)); + len = cvtnum(ash_arith(p + 2)); if (flag != '"') recordregion(begoff, begoff + len, 0); @@ -5682,7 +5817,7 @@ #endif /* argstr needs it */ -static char *evalvar(char *p, int flag, struct strlist *var_str_list); +static char *evalvar(char *p, int flags, struct strlist *var_str_list); /* * Perform variable and command substitution. If EXP_FULL is set, output CTLESC @@ -5694,7 +5829,7 @@ * for correct expansion of "B=$A" word. */ static void -argstr(char *p, int flag, struct strlist *var_str_list) +argstr(char *p, int flags, struct strlist *var_str_list) { static const char spclchars[] ALIGN1 = { '=', @@ -5705,49 +5840,52 @@ CTLVAR, CTLBACKQ, CTLBACKQ | CTLQUOTE, -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT CTLENDARI, #endif - 0 + '\0' }; const char *reject = spclchars; - int c; - int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ - int breakall = flag & EXP_WORD; + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ + int breakall = flags & EXP_WORD; int inquotes; size_t length; int startloc; - if (!(flag & EXP_VARTILDE)) { + if (!(flags & EXP_VARTILDE)) { reject += 2; - } else if (flag & EXP_VARTILDE2) { + } else if (flags & EXP_VARTILDE2) { reject++; } inquotes = 0; length = 0; - if (flag & EXP_TILDE) { + if (flags & EXP_TILDE) { char *q; - flag &= ~EXP_TILDE; + flags &= ~EXP_TILDE; tilde: q = p; - if (*q == CTLESC && (flag & EXP_QWORD)) + if (*q == CTLESC && (flags & EXP_QWORD)) q++; if (*q == '~') - p = exptilde(p, q, flag); + p = exptilde(p, q, flags); } start: startloc = expdest - (char *)stackblock(); for (;;) { + unsigned char c; + length += strcspn(p + length, reject); c = p[length]; - if (c && (!(c & 0x80) -#if ENABLE_ASH_MATH_SUPPORT - || c == CTLENDARI + if (c) { + if (!(c & 0x80) +#if ENABLE_SH_MATH_SUPPORT + || c == CTLENDARI #endif - )) { - /* c == '=' || c == ':' || c == CTLENDARI */ - length++; + ) { + /* c == '=' || c == ':' || c == CTLENDARI */ + length++; + } } if (length > 0) { int newloc; @@ -5765,11 +5903,11 @@ case '\0': goto breakloop; case '=': - if (flag & EXP_VARTILDE2) { + if (flags & EXP_VARTILDE2) { p--; continue; } - flag |= EXP_VARTILDE2; + flags |= EXP_VARTILDE2; reject++; /* fall through */ case ':': @@ -5788,15 +5926,13 @@ goto breakloop; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ - if ( - !inquotes && - !memcmp(p, dolatstr, 4) && - (p[4] == CTLQUOTEMARK || ( - p[4] == CTLENDVAR && - p[5] == CTLQUOTEMARK - )) + if (!inquotes + && memcmp(p, dolatstr, 4) == 0 + && ( p[4] == CTLQUOTEMARK + || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) + ) ) { - p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; + p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; goto start; } inquotes = !inquotes; @@ -5812,15 +5948,15 @@ length++; goto addquote; case CTLVAR: - p = evalvar(p, flag, var_str_list); + p = evalvar(p, flags, var_str_list); goto start; case CTLBACKQ: - c = 0; + c = '\0'; case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c, quotes); argbackq = argbackq->next; goto start; -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT case CTLENDARI: p--; expari(quotes); @@ -5895,7 +6031,7 @@ *loc2 = c; if (match) // if (!match) return loc; - if (quotes && *loc == CTLESC) + if (quotes && (unsigned char)*loc == CTLESC) loc++; loc++; loc2++; @@ -5947,11 +6083,12 @@ tail = nullstr; msg = "parameter not set"; if (umsg) { - if (*end == CTLENDVAR) { + if ((unsigned char)*end == CTLENDVAR) { if (varflags & VSNUL) tail = " or null"; - } else + } else { msg = umsg; + } } ash_msg_and_raise_error("%.*s: %s%s", end - var - 1, var, msg, tail); } @@ -5994,9 +6131,9 @@ char *startp; char *loc; char *rmesc, *rmescend; - USE_ASH_BASH_COMPAT(char *repl = NULL;) - USE_ASH_BASH_COMPAT(char null = '\0';) - USE_ASH_BASH_COMPAT(int pos, len, orig_len;) + IF_ASH_BASH_COMPAT(char *repl = NULL;) + IF_ASH_BASH_COMPAT(char null = '\0';) + IF_ASH_BASH_COMPAT(int pos, len, orig_len;) int saveherefd = herefd; int amount, workloc, resetloc; int zero; @@ -6020,17 +6157,17 @@ #if ENABLE_ASH_BASH_COMPAT case VSSUBSTR: loc = str = stackblock() + strloc; -// TODO: number() instead? It does error checking... - pos = atoi(loc); + /* Read POS in ${var:POS:LEN} */ + pos = atoi(loc); /* number(loc) errors out on "1:4" */ len = str - startp - 1; /* *loc != '\0', guaranteed by parser */ if (quotes) { char *ptr; - /* We must adjust the length by the number of escapes we find. */ + /* Adjust the length by the number of escapes */ for (ptr = startp; ptr < (str - 1); ptr++) { - if (*ptr == CTLESC) { + if ((unsigned char)*ptr == CTLESC) { len--; ptr++; } @@ -6039,15 +6176,22 @@ orig_len = len; if (*loc++ == ':') { -// TODO: number() instead? It does error checking... - len = atoi(loc); + /* ${var::LEN} */ + len = number(loc); } else { + /* Skip POS in ${var:POS:LEN} */ len = orig_len; - while (*loc && *loc != ':') + while (*loc && *loc != ':') { + /* TODO? + * bash complains on: var=qwe; echo ${var:1a:123} + if (!isdigit(*loc)) + ash_msg_and_raise_error(msg_illnum, str); + */ loc++; - if (*loc++ == ':') -// TODO: number() instead? It does error checking... - len = atoi(loc); + } + if (*loc++ == ':') { + len = number(loc); + } } if (pos >= orig_len) { pos = 0; @@ -6057,11 +6201,11 @@ len = orig_len - pos; for (str = startp; pos; str++, pos--) { - if (quotes && *str == CTLESC) + if (quotes && (unsigned char)*str == CTLESC) str++; } for (loc = startp; len; len--) { - if (quotes && *str == CTLESC) + if (quotes && (unsigned char)*str == CTLESC) *loc++ = *str++; *loc++ = *str++; } @@ -6082,7 +6226,7 @@ * stack will need rebasing, and we'll need to remove our work * areas each time */ - USE_ASH_BASH_COMPAT(restart:) + IF_ASH_BASH_COMPAT(restart:) amount = expdest - ((char *)stackblock() + resetloc); STADJUST(-amount, expdest); @@ -6091,7 +6235,7 @@ rmesc = startp; rmescend = (char *)stackblock() + strloc; if (quotes) { - rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); + rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); if (rmesc != startp) { rmescend = expdest; startp = (char *)stackblock() + startloc; @@ -6125,7 +6269,7 @@ /* No match, advance */ restart_detect = stackblock(); STPUTC(*idx, expdest); - if (quotes && *idx == CTLESC) { + if (quotes && (unsigned char)*idx == CTLESC) { idx++; len++; STPUTC(*idx, expdest); @@ -6140,13 +6284,14 @@ if (subtype == VSREPLACEALL) { while (idx < loc) { - if (quotes && *idx == CTLESC) + if (quotes && (unsigned char)*idx == CTLESC) idx++; idx++; rmesc++; } - } else + } else { idx = loc; + } for (loc = repl; *loc; loc++) { restart_detect = stackblock(); @@ -6207,26 +6352,30 @@ /* * Add the value of a specialized variable to the stack string. + * name parameter (examples): + * ash -c 'echo $1' name:'1=' + * ash -c 'echo $qwe' name:'qwe=' + * ash -c 'echo $$' name:'$=' + * ash -c 'echo ${$}' name:'$=' + * ash -c 'echo ${$##q}' name:'$=q' + * ash -c 'echo ${#$}' name:'$=' + * note: examples with bad shell syntax: + * ash -c 'echo ${#$1}' name:'$=1' + * ash -c 'echo ${#1#}' name:'1=#' */ -static ssize_t +static NOINLINE ssize_t varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) { + const char *p; int num; - char *p; int i; - int sep = 0; int sepq = 0; ssize_t len = 0; - char **ap; - int syntax; - int quoted = varflags & VSQUOTE; int subtype = varflags & VSTYPE; - int quotes = flags & (EXP_FULL | EXP_CASE); - - if (quoted && (flags & EXP_FULL)) - sep = 1 << CHAR_BIT; + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); + int quoted = varflags & VSQUOTE; + int syntax = quoted ? DQSYNTAX : BASESYNTAX; - syntax = quoted ? DQSYNTAX : BASESYNTAX; switch (*name) { case '$': num = rootpid; @@ -6243,30 +6392,42 @@ return -1; numvar: len = cvtnum(num); - break; + goto check_1char_name; case '-': - p = makestrspace(NOPTS, expdest); + expdest = makestrspace(NOPTS, expdest); for (i = NOPTS - 1; i >= 0; i--) { if (optlist[i]) { - USTPUTC(optletters(i), p); + USTPUTC(optletters(i), expdest); len++; } } - expdest = p; + check_1char_name: +#if 0 + /* handles cases similar to ${#$1} */ + if (name[2] != '\0') + raise_error_syntax("bad substitution"); +#endif break; - case '@': - if (sep) + case '@': { + char **ap; + int sep; + + if (quoted && (flags & EXP_FULL)) { + /* note: this is not meant as PEOF value */ + sep = 1 << CHAR_BIT; goto param; + } /* fall through */ case '*': - sep = ifsset() ? signed_char2int(ifsval()[0]) : ' '; - if (quotes && (SIT(sep, syntax) == CCTL || SIT(sep, syntax) == CBACK)) + sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; + i = SIT(sep, syntax); + if (quotes && (i == CCTL || i == CBACK)) sepq = 1; param: ap = shellparam.p; if (!ap) return -1; - while ((p = *ap++)) { + while ((p = *ap++) != NULL) { size_t partlen; partlen = strlen(p); @@ -6285,11 +6446,14 @@ q = expdest; if (sepq) STPUTC(CTLESC, q); + /* note: may put NUL despite sep != 0 + * (see sep = 1 << CHAR_BIT above) */ STPUTC(sep, q); expdest = q; } } return len; + } /* case '@' and '*' */ case '0': case '1': case '2': @@ -6300,8 +6464,7 @@ case '7': case '8': case '9': -// TODO: number() instead? It does error checking... - num = atoi(name); + num = atoi(name); /* number(name) fails on ${N#str} etc */ if (num < 0 || num > shellparam.nparam) return -1; p = num ? shellparam.p[num - 1] : arg0; @@ -6322,7 +6485,8 @@ break; eq++; if (name_len == (unsigned)(eq - str) - && strncmp(str, name, name_len) == 0) { + && strncmp(str, name, name_len) == 0 + ) { p = eq; /* goto value; - WRONG! */ /* think "A=1 A=2 B=$A" */ @@ -6353,7 +6517,7 @@ * input string. */ static char * -evalvar(char *p, int flag, struct strlist *var_str_list) +evalvar(char *p, int flags, struct strlist *var_str_list) { char varflags; char subtype; @@ -6364,7 +6528,7 @@ int startloc; ssize_t varlen; - varflags = *p++; + varflags = (unsigned char) *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; var = p; @@ -6373,7 +6537,7 @@ p = strchr(p, '=') + 1; again: - varlen = varvalue(var, varflags, flag, var_str_list); + varlen = varvalue(var, varflags, flags, var_str_list); if (varflags & VSNUL) varlen--; @@ -6386,8 +6550,8 @@ vsplus: if (varlen < 0) { argstr( - p, flag | EXP_TILDE | - (quoted ? EXP_QWORD : EXP_WORD), + p, flags | EXP_TILDE | + (quoted ? EXP_QWORD : EXP_WORD), var_str_list ); goto end; @@ -6459,7 +6623,8 @@ patloc = expdest - (char *)stackblock(); if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, startloc, varflags, - /* quotes: */ flag & (EXP_FULL | EXP_CASE), +//TODO: | EXP_REDIR too? All other such places do it too + /* quotes: */ flags & (EXP_FULL | EXP_CASE), var_str_list) ) { int amount = expdest - ( @@ -6477,7 +6642,7 @@ if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { - char c = *p++; + unsigned char c = *p++; if (c == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { @@ -6525,7 +6690,7 @@ ifsspc = 0; while (p < string + ifsp->endoff) { q = p; - if (*p == CTLESC) + if ((unsigned char)*p == CTLESC) p++; if (!strchr(ifs, *p)) { p++; @@ -6551,7 +6716,7 @@ break; } q = p; - if (*p == CTLESC) + if ((unsigned char)*p == CTLESC) p++; if (strchr(ifs, *p) == NULL) { p = q; @@ -6713,7 +6878,7 @@ p++; if (*p == '.') matchdot++; - while (!intpending && (dp = readdir(dirp)) != NULL) { + while (!pending_int && (dp = readdir(dirp)) != NULL) { if (dp->d_name[0] == '.' && !matchdot) continue; if (pmatch(start, dp->d_name)) { @@ -6834,7 +6999,7 @@ */ nometa: *exparg.lastp = str; - rmescapes(str->text); + rmescapes(str->text, 0); exparg.lastp = &str->next; } else { *exparg.lastp = NULL; @@ -6882,7 +7047,7 @@ expandmeta(exparg.list /*, flag*/); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ - rmescapes(p); + rmescapes(p, 0); sp = stzalloc(sizeof(*sp)); sp->text = p; *exparg.lastp = sp; @@ -6943,7 +7108,7 @@ struct builtincmd { const char *name; - int (*builtin)(int, char **); + int (*builtin)(int, char **) FAST_FUNC; /* unsigned flags; */ }; #define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1) @@ -7008,7 +7173,7 @@ static void -tryexec(USE_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) +tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) char *cmd, char **argv, char **envp) { int repeated = 0; @@ -7080,13 +7245,13 @@ || (applet_no = find_applet_by_name(argv[0])) >= 0 #endif ) { - tryexec(USE_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); + tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) argv[0], argv, envp); e = errno; } else { e = ENOENT; - while ((cmdname = padvance(&path, argv[0])) != NULL) { + while ((cmdname = path_advance(&path, argv[0])) != NULL) { if (--idx < 0 && pathopt == NULL) { - tryexec(USE_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); + tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) e = errno; } @@ -7107,8 +7272,8 @@ break; } exitstatus = exerrno; - TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", - argv[0], e, suppressint)); + TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", + argv[0], e, suppress_int)); ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); /* NOTREACHED */ } @@ -7123,7 +7288,7 @@ idx = cmdp->param.index; path = pathval(); do { - name = padvance(&path, cmdp->cmdname); + name = path_advance(&path, cmdp->cmdname); stunalloc(name); } while (--idx >= 0); out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); @@ -7237,7 +7402,7 @@ cmdp->rehash = 0; } -static int +static int FAST_FUNC hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct tblentry **pp; @@ -7307,7 +7472,7 @@ * pathval() still returns the old value at this point. * Called with interrupts off. */ -static void +static void FAST_FUNC changepath(const char *new) { const char *old; @@ -7495,7 +7660,7 @@ p = command; } else { do { - p = padvance(&path, command); + p = path_advance(&path, command); stunalloc(p); } while (--j >= 0); } @@ -7535,11 +7700,11 @@ return 127; } out: - outstr("\n", stdout); + out1str("\n"); return 0; } -static int +static int FAST_FUNC typecmd(int argc UNUSED_PARAM, char **argv) { int i = 1; @@ -7558,7 +7723,7 @@ } #if ENABLE_ASH_CMDCMD -static int +static int FAST_FUNC commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int c; @@ -7598,7 +7763,7 @@ #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ #define EV_BACKCMD 04 /* command executing within back quotes */ -static const short nodesize[N_NUMBER] = { +static const uint8_t nodesize[N_NUMBER] = { [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)), [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)), @@ -7881,57 +8046,60 @@ INT_ON; } -static int evalskip; /* set if we are skipping commands */ -/* reasons for skipping commands (see comment on breakcmd routine) */ +/* Reasons for skipping commands (see comment on breakcmd routine) */ #define SKIPBREAK (1 << 0) #define SKIPCONT (1 << 1) #define SKIPFUNC (1 << 2) #define SKIPFILE (1 << 3) #define SKIPEVAL (1 << 4) +static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ static int skipcount; /* number of levels to skip */ static int funcnest; /* depth of function calls */ static int loopnest; /* current loop nesting level */ -/* forward decl way out to parsing code - dotrap needs it */ +/* Forward decl way out to parsing code - dotrap needs it */ static int evalstring(char *s, int mask); -/* - * Called to execute a trap. Perhaps we should avoid entering new trap - * handlers while we are executing a trap handler. +/* Called to execute a trap. + * Single callsite - at the end of evaltree(). + * If we return non-zero, exaltree raises EXEXIT exception. + * + * Perhaps we should avoid entering new trap handlers + * while we are executing a trap handler. [is it a TODO?] */ static int dotrap(void) { - char *p; - char *q; - int i; - int savestatus; - int skip; + uint8_t *g; + int sig; + uint8_t savestatus; savestatus = exitstatus; - pendingsig = 0; + pending_sig = 0; xbarrier(); TRACE(("dotrap entered\n")); - for (i = 1, q = gotsig; i < NSIG; i++, q++) { - if (!*q) - continue; + for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { + int want_exexit; + char *t; - p = trap[i]; + if (*g == 0) + continue; + t = trap[sig]; /* non-trapped SIGINT is handled separately by raise_interrupt, * don't upset it by resetting gotsig[SIGINT-1] */ - if (i == SIGINT && !p) + if (sig == SIGINT && !t) continue; - TRACE(("sig %d is active, will run handler '%s'\n", i, p)); - *q = '\0'; - if (!p) + TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); + *g = 0; + if (!t) continue; - skip = evalstring(p, SKIPEVAL); + want_exexit = evalstring(t, SKIPEVAL); exitstatus = savestatus; - if (skip) { - TRACE(("dotrap returns %d\n", skip)); - return skip; + if (want_exexit) { + TRACE(("dotrap returns %d\n", want_exexit)); + return want_exexit; } } @@ -7970,19 +8138,21 @@ TRACE(("evaltree(NULL) called\n")); goto out1; } - TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); + TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); exception_handler = &jmploc; { int err = setjmp(jmploc.loc); if (err) { /* if it was a signal, check for trap handlers */ - if (exception == EXSIG) { - TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", exception, err)); + if (exception_type == EXSIG) { + TRACE(("exception %d (EXSIG) in evaltree, err=%d\n", + exception_type, err)); goto out; } /* continue on the way out */ - TRACE(("exception %d in evaltree, propagating err=%d\n", exception, err)); + TRACE(("exception %d in evaltree, propagating err=%d\n", + exception_type, err)); exception_handler = savehandler; longjmp(exception_handler->loc, err); } @@ -7992,7 +8162,7 @@ default: #if DEBUG out1fmt("Node type = %d\n", n->type); - fflush(stdout); + fflush_all(); break; #endif case NNOT: @@ -8085,7 +8255,7 @@ out1: if (checkexit & exitstatus) evalskip |= SKIPEVAL; - else if (pendingsig && dotrap()) + else if (pending_sig && dotrap()) goto exexit; if (flags & EV_EXIT) { @@ -8371,12 +8541,13 @@ static smallint did_banner; if (!did_banner) { - out1fmt( - "\n\n" - "%s built-in shell (ash)\n" + /* note: ash and hush share this string */ + out1fmt("\n\n%s %s\n" "Enter 'help' for a list of built-in commands." "\n\n", - bb_banner); + bb_banner, + "built-in shell (ash)" + ); did_banner = 1; } } @@ -8416,7 +8587,7 @@ while ((lvp = localvars) != NULL) { localvars = lvp->next; vp = lvp->vp; - TRACE(("poplocalvar %s", vp ? vp->text : "-")); + TRACE(("poplocalvar %s\n", vp ? vp->text : "-")); if (vp == NULL) { /* $- saved */ memcpy(optlist, lvp->text, sizeof(optlist)); free((char*)lvp->text); @@ -8564,7 +8735,7 @@ /* * The "local" command. */ -static int +static int FAST_FUNC localcmd(int argc UNUSED_PARAM, char **argv) { char *name; @@ -8576,19 +8747,19 @@ return 0; } -static int +static int FAST_FUNC falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 1; } -static int +static int FAST_FUNC truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { return 0; } -static int +static int FAST_FUNC execcmd(int argc UNUSED_PARAM, char **argv) { if (argv[1]) { @@ -8603,7 +8774,7 @@ /* * The return command. */ -static int +static int FAST_FUNC returncmd(int argc UNUSED_PARAM, char **argv) { /* @@ -8615,28 +8786,28 @@ } /* Forward declarations for builtintab[] */ -static int breakcmd(int, char **); -static int dotcmd(int, char **); -static int evalcmd(int, char **); -static int exitcmd(int, char **); -static int exportcmd(int, char **); +static int breakcmd(int, char **) FAST_FUNC; +static int dotcmd(int, char **) FAST_FUNC; +static int evalcmd(int, char **) FAST_FUNC; +static int exitcmd(int, char **) FAST_FUNC; +static int exportcmd(int, char **) FAST_FUNC; #if ENABLE_ASH_GETOPTS -static int getoptscmd(int, char **); +static int getoptscmd(int, char **) FAST_FUNC; #endif #if !ENABLE_FEATURE_SH_EXTRA_QUIET -static int helpcmd(int, char **); +static int helpcmd(int, char **) FAST_FUNC; #endif -#if ENABLE_ASH_MATH_SUPPORT -static int letcmd(int, char **); +#if ENABLE_SH_MATH_SUPPORT +static int letcmd(int, char **) FAST_FUNC; #endif -static int readcmd(int, char **); -static int setcmd(int, char **); -static int shiftcmd(int, char **); -static int timescmd(int, char **); -static int trapcmd(int, char **); -static int umaskcmd(int, char **); -static int unsetcmd(int, char **); -static int ulimitcmd(int, char **); +static int readcmd(int, char **) FAST_FUNC; +static int setcmd(int, char **) FAST_FUNC; +static int shiftcmd(int, char **) FAST_FUNC; +static int timescmd(int, char **) FAST_FUNC; +static int trapcmd(int, char **) FAST_FUNC; +static int umaskcmd(int, char **) FAST_FUNC; +static int unsetcmd(int, char **) FAST_FUNC; +static int ulimitcmd(int, char **) FAST_FUNC; #define BUILTIN_NOSPEC "0" #define BUILTIN_SPECIAL "1" @@ -8647,21 +8818,16 @@ #define BUILTIN_REG_ASSG "6" #define BUILTIN_SPEC_REG_ASSG "7" -/* We do not handle [[ expr ]] bashism bash-compatibly, - * we make it a synonym of [ expr ]. - * Basically, word splitting and pathname expansion should NOT be performed - * Examples: - * no word splitting: a="a b"; [[ $a = "a b" ]]; echo $? should print "0" - * no pathname expansion: [[ /bin/m* = "/bin/m*" ]]; echo $? should print "0" - * Additional operators: - * || and && should work as -o and -a - * =~ regexp match - * Apart from the above, [[ expr ]] should work as [ expr ] - */ - -#define echocmd echo_main -#define printfcmd printf_main -#define testcmd test_main +/* Stubs for calling non-FAST_FUNC's */ +#if ENABLE_ASH_BUILTIN_ECHO +static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } +#endif +#if ENABLE_ASH_BUILTIN_PRINTF +static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } +#endif +#if ENABLE_ASH_BUILTIN_TEST +static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } +#endif /* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { @@ -8708,7 +8874,7 @@ { BUILTIN_REGULAR "jobs", jobscmd }, { BUILTIN_REGULAR "kill", killcmd }, #endif -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT { BUILTIN_NOSPEC "let", letcmd }, #endif { BUILTIN_ASSIGN "local", localcmd }, @@ -8784,7 +8950,7 @@ return 0; return *q == '='; } -static int +static int FAST_FUNC bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { /* Preserve exitstatus of a previous possible redirection @@ -8913,7 +9079,7 @@ for (;;) { find_command(argv[0], &cmdentry, cmd_flag, path); if (cmdentry.cmdtype == CMDUNKNOWN) { - flush_stderr(); + flush_stdout_stderr(); status = 127; goto bail; } @@ -9003,14 +9169,14 @@ if (evalbltin(cmdentry.u.cmd, argc, argv)) { int exit_status; - int i = exception; + int i = exception_type; if (i == EXEXIT) goto raise; exit_status = 2; if (i == EXINT) exit_status = 128 + SIGINT; if (i == EXSIG) - exit_status = 128 + pendingsig; + exit_status = 128 + pending_sig; exitstatus = exit_status; if (i == EXINT || spclbltin > 0) { raise: @@ -9064,7 +9230,6 @@ exitstatus |= ferror(stdout); clearerr(stdout); commandname = savecmdname; -// exsig = 0; exception_handler = savehandler; return i; @@ -9109,13 +9274,13 @@ * be an error to break out of more loops than exist, but it isn't * in the standard shell so we don't make it one here. */ -static int +static int FAST_FUNC breakcmd(int argc UNUSED_PARAM, char **argv) { int n = argv[1] ? number(argv[1]) : 1; if (n <= 0) - ash_msg_and_raise_error(illnum, argv[1]); + ash_msg_and_raise_error(msg_illnum, argv[1]); if (n > loopnest) n = loopnest; if (n > 0) { @@ -9136,19 +9301,48 @@ INPUT_NOFILE_OK = 2, }; -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 */ - static smallint checkkwd; /* values of checkkwd variable */ #define CHKALIAS 0x1 #define CHKKWD 0x2 #define CHKNL 0x4 +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +#if !ENABLE_ASH_ALIAS +#define pushstring(s, ap) pushstring(s) +#endif +static void +pushstring(char *s, struct alias *ap) +{ + struct strpush *sp; + int len; + + len = strlen(s); + INT_OFF; + if (g_parsefile->strpush) { + sp = ckzalloc(sizeof(*sp)); + sp->prev = g_parsefile->strpush; + } else { + sp = &(g_parsefile->basestrpush); + } + g_parsefile->strpush = sp; + sp->prev_string = g_parsefile->next_to_pgetc; + sp->prev_left_in_line = g_parsefile->left_in_line; +#if ENABLE_ASH_ALIAS + sp->ap = ap; + if (ap) { + ap->flag |= ALIASINUSE; + sp->string = s; + } +#endif + g_parsefile->next_to_pgetc = s; + g_parsefile->left_in_line = len; + INT_ON; +} + static void popstring(void) { @@ -9157,7 +9351,9 @@ INT_OFF; #if ENABLE_ASH_ALIAS if (sp->ap) { - if (parsenextc[-1] == ' ' || parsenextc[-1] == '\t') { + if (g_parsefile->next_to_pgetc[-1] == ' ' + || g_parsefile->next_to_pgetc[-1] == '\t' + ) { checkkwd |= CHKALIAS; } if (sp->string != sp->ap->val) { @@ -9169,21 +9365,26 @@ } } #endif - parsenextc = sp->prevstring; - parsenleft = sp->prevnleft; + g_parsefile->next_to_pgetc = sp->prev_string; + g_parsefile->left_in_line = sp->prev_left_in_line; g_parsefile->strpush = sp->prev; if (sp != &(g_parsefile->basestrpush)) free(sp); INT_ON; } +//FIXME: BASH_COMPAT with "...&" does TWO pungetc(): +//it peeks whether it is &>, and then pushes back both chars. +//This function needs to save last *next_to_pgetc to buf[0] +//to make two pungetc() reliable. Currently, +// pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... static int preadfd(void) { int nr; char *buf = g_parsefile->buf; - parsenextc = buf; + g_parsefile->next_to_pgetc = buf; #if ENABLE_FEATURE_EDITING retry: if (!iflag || g_parsefile->fd != STDIN_FILENO) @@ -9234,9 +9435,10 @@ * Refill the input buffer and return the next input character: * * 1) If a string was pushed back on the input, pop it; - * 2) If an EOF was pushed back (parsenleft < -BIGNUM) or we are reading - * from a string so we can't refill the buffer, return EOF. - * 3) If the is more stuff in this buffer, use it else call read to fill it. + * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM) + * or we are reading from a string so we can't refill the buffer, + * return EOF. + * 3) If there is more stuff in this buffer, use it else call read to fill it. * 4) Process input up to the next newline, deleting nul characters. */ //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) @@ -9249,8 +9451,10 @@ while (g_parsefile->strpush) { #if ENABLE_ASH_ALIAS - if (parsenleft == -1 && g_parsefile->strpush->ap - && parsenextc[-1] != ' ' && parsenextc[-1] != '\t' + if (g_parsefile->left_in_line == -1 + && g_parsefile->strpush->ap + && g_parsefile->next_to_pgetc[-1] != ' ' + && g_parsefile->next_to_pgetc[-1] != '\t' ) { pgetc_debug("preadbuffer PEOA"); return PEOA; @@ -9258,43 +9462,51 @@ #endif popstring(); /* try "pgetc" now: */ - pgetc_debug("internal pgetc at %d:%p'%s'", parsenleft, parsenextc, parsenextc); - if (--parsenleft >= 0) - return signed_char2int(*parsenextc++); + pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); + if (--g_parsefile->left_in_line >= 0) + return (unsigned char)(*g_parsefile->next_to_pgetc++); } - /* on both branches above parsenleft < 0. + /* on both branches above g_parsefile->left_in_line < 0. * "pgetc" needs refilling. */ - /* -90 is -BIGNUM. Below we use -99 to mark "EOF on read", - * pungetc() may decrement it a few times. -90 is enough. + /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read", + * pungetc() may increment it a few times. + * Assuming it won't increment it to less than -90. */ - if (parsenleft < -90 || g_parsefile->buf == NULL) { + if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) { pgetc_debug("preadbuffer PEOF1"); - /* even in failure keep them in lock step, - * for correct pungetc. */ - parsenextc++; + /* even in failure keep left_in_line and next_to_pgetc + * in lock step, for correct multi-layer pungetc. + * left_in_line was decremented before preadbuffer(), + * must inc next_to_pgetc: */ + g_parsefile->next_to_pgetc++; return PEOF; } - more = parselleft; + more = g_parsefile->left_in_buffer; if (more <= 0) { flush_stdout_stderr(); again: more = preadfd(); if (more <= 0) { - parselleft = parsenleft = -99; + /* don't try reading again */ + g_parsefile->left_in_line = -99; pgetc_debug("preadbuffer PEOF2"); - parsenextc++; + g_parsefile->next_to_pgetc++; return PEOF; } } /* Find out where's the end of line. - * Set parsenleft/parselleft acordingly. + * Set g_parsefile->left_in_line + * and g_parsefile->left_in_buffer acordingly. * NUL chars are deleted. */ - q = parsenextc; + q = g_parsefile->next_to_pgetc; for (;;) { char c; @@ -9306,61 +9518,72 @@ } else { q++; if (c == '\n') { - parsenleft = q - parsenextc - 1; + g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; break; } } if (more <= 0) { - parsenleft = q - parsenextc - 1; - if (parsenleft < 0) + g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1; + if (g_parsefile->left_in_line < 0) goto again; break; } } - parselleft = more; + g_parsefile->left_in_buffer = more; if (vflag) { char save = *q; *q = '\0'; - out2str(parsenextc); + out2str(g_parsefile->next_to_pgetc); *q = save; } - pgetc_debug("preadbuffer at %d:%p'%s'", parsenleft, parsenextc, parsenextc); - return signed_char2int(*parsenextc++); + pgetc_debug("preadbuffer at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); + return (unsigned char)*g_parsefile->next_to_pgetc++; } -#define pgetc_as_macro() (--parsenleft >= 0 ? signed_char2int(*parsenextc++) : preadbuffer()) +#define pgetc_as_macro() \ + (--g_parsefile->left_in_line >= 0 \ + ? (unsigned char)*g_parsefile->next_to_pgetc++ \ + : preadbuffer() \ + ) static int pgetc(void) { - pgetc_debug("pgetc at %d:%p'%s'", parsenleft, parsenextc, parsenextc); + pgetc_debug("pgetc_fast at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); return pgetc_as_macro(); } #if ENABLE_ASH_OPTIMIZE_FOR_SIZE -#define pgetc_fast() pgetc() +# define pgetc_fast() pgetc() #else -#define pgetc_fast() pgetc_as_macro() +# define pgetc_fast() pgetc_as_macro() #endif -/* - * Same as pgetc(), but ignores PEOA. - */ #if ENABLE_ASH_ALIAS static int -pgetc2(void) +pgetc_without_PEOA(void) { int c; do { + pgetc_debug("pgetc_fast at %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); c = pgetc_fast(); } while (c == PEOA); return c; } #else -#define pgetc2() pgetc() +# define pgetc_without_PEOA() pgetc() #endif /* @@ -9374,7 +9597,7 @@ int c; while (--nleft > 0) { - c = pgetc2(); + c = pgetc_without_PEOA(); if (c == PEOF) { if (p == line) return NULL; @@ -9395,45 +9618,12 @@ static void pungetc(void) { - parsenleft++; - parsenextc--; - pgetc_debug("pushed back to %d:%p'%s'", parsenleft, parsenextc, parsenextc); -} - -/* - * Push a string back onto the input at this current parsefile level. - * We handle aliases this way. - */ -#if !ENABLE_ASH_ALIAS -#define pushstring(s, ap) pushstring(s) -#endif -static void -pushstring(char *s, struct alias *ap) -{ - struct strpush *sp; - int len; - - len = strlen(s); - INT_OFF; - if (g_parsefile->strpush) { - sp = ckzalloc(sizeof(*sp)); - sp->prev = g_parsefile->strpush; - } else { - sp = &(g_parsefile->basestrpush); - } - g_parsefile->strpush = sp; - sp->prevstring = parsenextc; - sp->prevnleft = parsenleft; -#if ENABLE_ASH_ALIAS - sp->ap = ap; - if (ap) { - ap->flag |= ALIASINUSE; - sp->string = s; - } -#endif - parsenextc = s; - parsenleft = len; - INT_ON; + g_parsefile->left_in_line++; + g_parsefile->next_to_pgetc--; + pgetc_debug("pushed back to %d:%p'%s'", + g_parsefile->left_in_line, + g_parsefile->next_to_pgetc, + g_parsefile->next_to_pgetc); } /* @@ -9445,10 +9635,6 @@ { struct parsefile *pf; - g_parsefile->nleft = parsenleft; - g_parsefile->lleft = parselleft; - g_parsefile->nextc = parsenextc; - g_parsefile->linno = plinno; pf = ckzalloc(sizeof(*pf)); pf->prev = g_parsefile; pf->fd = -1; @@ -9470,10 +9656,6 @@ popstring(); g_parsefile = pf->prev; free(pf); - parsenleft = g_parsefile->nleft; - parselleft = g_parsefile->lleft; - parsenextc = g_parsefile->nextc; - plinno = g_parsefile->linno; INT_ON; } @@ -9516,8 +9698,9 @@ g_parsefile->fd = fd; if (g_parsefile->buf == NULL) g_parsefile->buf = ckmalloc(IBUFSIZ); - parselleft = parsenleft = 0; - plinno = 1; + g_parsefile->left_in_buffer = 0; + g_parsefile->left_in_line = 0; + g_parsefile->linno = 1; } /* @@ -9535,7 +9718,7 @@ if (fd < 0) { if (flags & INPUT_NOFILE_OK) goto out; - ash_msg_and_raise_error("can't open %s", fname); + ash_msg_and_raise_error("can't open '%s'", fname); } if (fd < 10) { fd2 = copyfd(fd, 10); @@ -9558,10 +9741,10 @@ { INT_OFF; pushfile(); - parsenextc = string; - parsenleft = strlen(string); + g_parsefile->next_to_pgetc = string; + g_parsefile->left_in_line = strlen(string); g_parsefile->buf = NULL; - plinno = 1; + g_parsefile->linno = 1; INT_ON; } @@ -9599,7 +9782,7 @@ setstackmark(&smark); mpath = mpathset() ? mpathval() : mailval(); for (mtp = mailtime; mtp < mailtime + MAXMBOXES; mtp++) { - p = padvance(&mpath, nullstr); + p = path_advance(&mpath, nullstr); if (p == NULL) break; if (*p == '\0') @@ -9627,7 +9810,7 @@ popstackmark(&smark); } -static void +static void FAST_FUNC changemail(const char *val UNUSED_PARAM) { mail_var_path_changed = 1; @@ -9783,7 +9966,7 @@ /* * The shift builtin command. */ -static int +static int FAST_FUNC shiftcmd(int argc UNUSED_PARAM, char **argv) { int n; @@ -9845,7 +10028,7 @@ /* * The set command builtin. */ -static int +static int FAST_FUNC setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int retval; @@ -9866,37 +10049,21 @@ } #if ENABLE_ASH_RANDOM_SUPPORT -static void +static void FAST_FUNC change_random(const char *value) { - /* Galois LFSR parameter */ - /* Taps at 32 31 29 1: */ - enum { MASK = 0x8000000b }; - /* Another example - taps at 32 31 30 10: */ - /* MASK = 0x00400007 */ + uint32_t t; if (value == NULL) { /* "get", generate */ - uint32_t t; - - /* LCG has period of 2^32 and alternating lowest bit */ - random_LCG = 1664525 * random_LCG + 1013904223; - /* Galois LFSR has period of 2^32-1 = 3 * 5 * 17 * 257 * 65537 */ - t = (random_galois_LFSR << 1); - if (random_galois_LFSR < 0) /* if we just shifted 1 out of msb... */ - t ^= MASK; - random_galois_LFSR = t; - /* Both are weak, combining them gives better randomness - * and ~2^64 period. & 0x7fff is probably bash compat - * for $RANDOM range. Combining with subtraction is - * just for fun. + and ^ would work equally well. */ - t = (t - random_LCG) & 0x7fff; + t = next_random(&random_gen); /* set without recursion */ setvar(vrandom.text, utoa(t), VNOFUNC); vrandom.flags &= ~VNOFUNC; } else { /* set/reset */ - random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10); + t = strtoul(value, NULL, 10); + INIT_RANDOM_T(&random_gen, (t ? t : 1), t); } } #endif @@ -9996,7 +10163,7 @@ * be processed in the current argument. If shellparam.optnext is NULL, * then it's the first time getopts has been called. */ -static int +static int FAST_FUNC getoptscmd(int argc, char **argv) { char **optbase; @@ -10041,20 +10208,6 @@ static struct nodelist *backquotelist; static union node *redirnode; static struct heredoc *heredoc; -/* - * 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. - */ -#define NEOF ((union node *)&tokpushback) - -static void raise_error_syntax(const char *) NORETURN; -static void -raise_error_syntax(const char *msg) -{ - ash_msg_and_raise_error("syntax error: %s", msg); - /* NOTREACHED */ -} /* * Called when an unexpected token is read during the parse. The argument @@ -10068,7 +10221,7 @@ char msg[64]; int l; - l = sprintf(msg, "%s unexpected", tokname(lasttoken)); + l = sprintf(msg, "unexpected %s", tokname(lasttoken)); if (token >= 0) sprintf(msg + l, " (expecting %s)", tokname(token)); raise_error_syntax(msg); @@ -10260,17 +10413,15 @@ * or backquotes). */ static int -noexpand(char *text) +noexpand(const char *text) { - char *p; - char c; + unsigned char c; - p = text; - while ((c = *p++) != '\0') { + while ((c = *text++) != '\0') { if (c == CTLQUOTEMARK) continue; if (c == CTLESC) - p++; + text++; else if (SIT(c, BASESYNTAX) == CCTL) return 0; } @@ -10294,7 +10445,7 @@ TRACE(("Here document %d\n", n->type)); if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) raise_error_syntax("illegal eof marker for << redirection"); - rmescapes(wordtext); + rmescapes(wordtext, 0); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) @@ -10656,7 +10807,7 @@ * If eofmark is NULL, read a word or a redirection symbol. If eofmark * is not NULL, read a here document. In the latter case, eofmark is the * word which marks the end of the document and striptabs is true if - * leading tabs should be stripped from the document. The argument firstc + * leading tabs should be stripped from the document. The argument c * is the first character of the input token or document. * * Because C does not have internal subroutines, I have simulated them @@ -10670,10 +10821,10 @@ #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} #define PARSEARITH() {goto parsearith; parsearith_return:;} static int -readtoken1(int firstc, int syntax, char *eofmark, int striptabs) +readtoken1(int c, int syntax, char *eofmark, int striptabs) { /* NB: syntax parameter fits into smallint */ - int c = firstc; + /* c parameter is an unsigned char or PEOF or PEOA */ char *out; int len; char line[EOFMARKLEN + 1]; @@ -10690,7 +10841,7 @@ int parenlevel; /* levels of parens in arithmetic */ int dqvarnest; /* levels of variables expansion within double quotes */ - USE_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) + IF_ASH_BASH_COMPAT(smallint bash_dollar_squote = 0;) #if __GNUC__ /* Avoid longjmp clobbering */ @@ -10705,7 +10856,7 @@ (void) &prevsyntax; (void) &syntax; #endif - startlinno = plinno; + startlinno = g_parsefile->linno; bqlist = NULL; quotef = 0; oldstyle = 0; @@ -10733,7 +10884,7 @@ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); - plinno++; + g_parsefile->linno++; if (doprompt) setprompt(2); c = pgetc(); @@ -10756,7 +10907,7 @@ USTPUTC(c, out); break; case CBACK: /* backslash */ - c = pgetc2(); + c = pgetc_without_PEOA(); if (c == PEOF) { USTPUTC(CTLESC, out); USTPUTC('\\', out); @@ -10796,7 +10947,7 @@ dblquote = 1; goto quotemark; case CENDQUOTE: - USE_ASH_BASH_COMPAT(bash_dollar_squote = 0;) + IF_ASH_BASH_COMPAT(bash_dollar_squote = 0;) if (eofmark != NULL && arinest == 0 && varnest == 0 ) { @@ -10824,7 +10975,7 @@ USTPUTC(c, out); } break; -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT case CLP: /* '(' in arithmetic */ parenlevel++; USTPUTC(c, out); @@ -10870,9 +11021,7 @@ #endif goto endword; /* exit outer loop */ } -#if ENABLE_ASH_ALIAS - if (c != PEOA) -#endif + IF_ASH_ALIAS(if (c != PEOA)) USTPUTC(c, out); } @@ -10880,14 +11029,14 @@ } /* for (;;) */ } endword: -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT if (syntax == ARISYNTAX) raise_error_syntax("missing '))'"); #endif if (syntax != BASESYNTAX && !parsebackquote && eofmark == NULL) raise_error_syntax("unterminated quoted string"); if (varnest != 0) { - startlinno = plinno; + startlinno = g_parsefile->linno; /* { */ raise_error_syntax("missing '}'"); } @@ -10895,7 +11044,7 @@ len = out - (char *)stackblock(); out = stackblock(); if (eofmark == NULL) { - if ((c == '>' || c == '<' USE_ASH_BASH_COMPAT( || c == 0x100 + '>')) + if ((c == '>' || c == '<' IF_ASH_BASH_COMPAT( || c == 0x100 + '>')) && quotef == 0 ) { if (isdigit_str9(out)) { @@ -10924,13 +11073,12 @@ checkend: { if (eofmark) { #if ENABLE_ASH_ALIAS - if (c == PEOA) { - c = pgetc2(); - } + if (c == PEOA) + c = pgetc_without_PEOA(); #endif if (striptabs) { while (c == '\t') { - c = pgetc2(); + c = pgetc_without_PEOA(); } } if (c == *eofmark) { @@ -10942,7 +11090,7 @@ continue; if (*p == '\n' && *q == '\0') { c = PEOF; - plinno++; + g_parsefile->linno++; needprompt = doprompt; } else { pushstring(line, NULL); @@ -11038,14 +11186,14 @@ (((unsigned)(c) - 33 < 32) \ && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1)) parsesub: { - int subtype; + unsigned char subtype; int typeloc; int flags; char *p; static const char types[] ALIGN1 = "}-+?="; c = pgetc(); - if (c <= PEOA_OR_PEOF + if (c > 255 /* PEOA or PEOF */ || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) ) { #if ENABLE_ASH_BASH_COMPAT @@ -11057,7 +11205,7 @@ pungetc(); } else if (c == '(') { /* $(command) or $((arith)) */ if (pgetc() == '(') { -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT PARSEARITH(); #else raise_error_syntax("you disabled math support for $((arith)) syntax"); @@ -11082,11 +11230,11 @@ } else subtype = 0; } - if (c > PEOA_OR_PEOF && is_name(c)) { + if (c <= 255 /* not PEOA or PEOF */ && is_name(c)) { do { STPUTC(c, out); c = pgetc(); - } while (c > PEOA_OR_PEOF && is_in_name(c)); + } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); } else if (isdigit(c)) { do { STPUTC(c, out); @@ -11099,6 +11247,8 @@ badsub: raise_error_syntax("bad substitution"); } + if (c != '}' && subtype == VSLENGTH) + goto badsub; STPUTC('=', out); flags = 0; @@ -11148,7 +11298,7 @@ } if (dblquote || arinest) flags |= VSQUOTE; - *((char *)stackblock() + typeloc) = subtype | flags; + ((unsigned char *)stackblock())[typeloc] = subtype | flags; if (subtype != VSNORMAL) { varnest++; if (dblquote || arinest) { @@ -11218,7 +11368,7 @@ case '\\': pc = pgetc(); if (pc == '\n') { - plinno++; + g_parsefile->linno++; if (doprompt) setprompt(2); /* @@ -11230,22 +11380,22 @@ continue; } if (pc != '\\' && pc != '`' && pc != '$' - && (!dblquote || pc != '"')) + && (!dblquote || pc != '"') + ) { STPUTC('\\', pout); - if (pc > PEOA_OR_PEOF) { + } + if (pc <= 255 /* not PEOA or PEOF */) { break; } /* fall through */ case PEOF: -#if ENABLE_ASH_ALIAS - case PEOA: -#endif - startlinno = plinno; + IF_ASH_ALIAS(case PEOA:) + startlinno = g_parsefile->linno; raise_error_syntax("EOF in backquote substitution"); case '\n': - plinno++; + g_parsefile->linno++; needprompt = doprompt; break; @@ -11312,7 +11462,7 @@ goto parsebackq_newreturn; } -#if ENABLE_ASH_MATH_SUPPORT +#if ENABLE_SH_MATH_SUPPORT /* * Parse an arithmetic expansion (indicate start of one and set state) */ @@ -11386,10 +11536,10 @@ if (needprompt) { setprompt(2); } - startlinno = plinno; + startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ c = pgetc_fast(); - if (c == ' ' || c == '\t' USE_ASH_ALIAS( || c == PEOA)) + if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) continue; if (c == '#') { @@ -11401,7 +11551,7 @@ pungetc(); break; /* return readtoken1(...) */ } - startlinno = ++plinno; + startlinno = ++g_parsefile->linno; if (doprompt) setprompt(2); } else { @@ -11410,7 +11560,7 @@ p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; if (c != PEOF) { if (c == '\n') { - plinno++; + g_parsefile->linno++; needprompt = doprompt; } @@ -11452,14 +11602,12 @@ if (needprompt) { setprompt(2); } - startlinno = plinno; + startlinno = g_parsefile->linno; for (;;) { /* until token or start of word found */ c = pgetc_fast(); switch (c) { case ' ': case '\t': -#if ENABLE_ASH_ALIAS - case PEOA: -#endif + IF_ASH_ALIAS(case PEOA:) continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF) @@ -11468,7 +11616,7 @@ continue; case '\\': if (pgetc() == '\n') { - startlinno = ++plinno; + startlinno = ++g_parsefile->linno; if (doprompt) setprompt(2); continue; @@ -11476,7 +11624,7 @@ pungetc(); goto breakloop; case '\n': - plinno++; + g_parsefile->linno++; needprompt = doprompt; RETURN(TNL); case PEOF: @@ -11586,8 +11734,8 @@ } /* - * Read and parse a command. Returns NEOF on end of file. (NULL is a - * valid parse tree indicating a blank line.) + * Read and parse a command. Returns NODE_EOF on end of file. + * (NULL is a valid parse tree indicating a blank line.) */ static union node * parsecmd(int interact) @@ -11601,7 +11749,7 @@ needprompt = 0; t = readtoken(); if (t == TEOF) - return NEOF; + return NODE_EOF; if (t == TNL) return NULL; tokpushback = 1; @@ -11646,7 +11794,8 @@ { union node n; - /* XXX Fix (char *) cast. */ + /* XXX Fix (char *) cast. It _is_ a bug. ps is variable's value, + * and token processing _can_ alter it (delete NULs etc). */ setinputstring((char *)ps); readtoken1(pgetc(), PSSYNTAX, nullstr, 0); popfile(); @@ -11675,7 +11824,7 @@ setstackmark(&smark); skip = 0; - while ((n = parsecmd(0)) != NEOF) { + while ((n = parsecmd(0)) != NODE_EOF) { evaltree(n, 0); popstackmark(&smark); skip = evalskip; @@ -11692,7 +11841,7 @@ /* * The eval command. */ -static int +static int FAST_FUNC evalcmd(int argc UNUSED_PARAM, char **argv) { char *p; @@ -11720,8 +11869,9 @@ } /* - * Read and execute commands. "Top" is nonzero for the top level command - * loop; it turns on prompting if the shell is interactive. + * Read and execute commands. + * "Top" is nonzero for the top level command loop; + * it turns on prompting if the shell is interactive. */ static int cmdloop(int top) @@ -11748,8 +11898,11 @@ #endif } n = parsecmd(inter); - /* showtree(n); DEBUG */ - if (n == NEOF) { +#if DEBUG + if (DEBUG > 2 && debug && (n != NODE_EOF)) + showtree(n); +#endif + if (n == NODE_EOF) { if (!top || numeof >= 50) break; if (!stoppedjobs()) { @@ -11790,7 +11943,16 @@ if (strchr(name, '/')) return name; - while ((fullname = padvance(&path, name)) != NULL) { + /* IIRC standards do not say whether . is to be searched. + * And it is even smaller this way, making it unconditional for now: + */ + if (1) { /* ENABLE_ASH_BASH_COMPAT */ + fullname = name; + goto try_cur_dir; + } + + while ((fullname = path_advance(&path, name)) != NULL) { + try_cur_dir: if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { /* * Don't bother freeing here, since it will @@ -11798,7 +11960,8 @@ */ return fullname; } - stunalloc(fullname); + if (fullname != name) + stunalloc(fullname); } /* not found in the PATH */ @@ -11806,7 +11969,7 @@ /* NOTREACHED */ } -static int +static int FAST_FUNC dotcmd(int argc, char **argv) { struct strlist *sp; @@ -11841,7 +12004,7 @@ return status; } -static int +static int FAST_FUNC exitcmd(int argc UNUSED_PARAM, char **argv) { if (stoppedjobs()) @@ -11972,7 +12135,7 @@ e = ENOENT; idx = -1; loop: - while ((fullname = padvance(&path, name)) != NULL) { + while ((fullname = path_advance(&path, name)) != NULL) { stunalloc(fullname); /* NB: code below will still use fullname * despite it being "unallocated" */ @@ -12065,7 +12228,7 @@ /* * The trap builtin. */ -static int +static int FAST_FUNC trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *action; @@ -12076,14 +12239,30 @@ ap = argptr; if (!*ap) { for (signo = 0; signo < NSIG; signo++) { - if (trap[signo] != NULL) { + char *tr = trap_ptr[signo]; + if (tr) { + /* note: bash adds "SIG", but only if invoked + * as "bash". If called as "sh", or if set -o posix, + * then it prints short signal names. + * We are printing short names: */ out1fmt("trap -- %s %s\n", - single_quote(trap[signo]), + single_quote(tr), get_signame(signo)); + /* trap_ptr != trap only if we are in special-cased `trap` code. + * In this case, we will exit very soon, no need to free(). */ + /* if (trap_ptr != trap && tp[0]) */ + /* free(tr); */ } } + /* + if (trap_ptr != trap) { + free(trap_ptr); + trap_ptr = trap; + } + */ return 0; } + action = NULL; if (ap[1]) action = *ap++; @@ -12115,13 +12294,15 @@ /* * Lists available builtins */ -static int +static int FAST_FUNC helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { unsigned col; unsigned i; - out1fmt("\nBuilt-in commands:\n-------------------\n"); + out1fmt( + "Built-in commands:\n" + "------------------\n"); for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), builtintab[i].name + 1); @@ -12151,7 +12332,7 @@ /* * The export and readonly commands. */ -static int +static int FAST_FUNC exportcmd(int argc UNUSED_PARAM, char **argv) { struct var *vp; @@ -12202,7 +12383,7 @@ * variable to allow a function to be unset when there is a readonly variable * with the same name. */ -static int +static int FAST_FUNC unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char **ap; @@ -12240,7 +12421,7 @@ 0 }; -static int +static int FAST_FUNC timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { long clk_tck, s, t; @@ -12263,36 +12444,14 @@ return 0; } -#if ENABLE_ASH_MATH_SUPPORT -static arith_t -dash_arith(const char *s) -{ - arith_t result; - int errcode = 0; - - INT_OFF; - result = arith(s, &errcode); - if (errcode < 0) { - if (errcode == -3) - ash_msg_and_raise_error("exponent less than 0"); - if (errcode == -2) - ash_msg_and_raise_error("divide by zero"); - if (errcode == -5) - ash_msg_and_raise_error("expression recursion loop detected"); - raise_error_syntax(s); - } - INT_ON; - - return result; -} - +#if ENABLE_SH_MATH_SUPPORT /* - * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. - * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. + * The let builtin. partial stolen from GNU Bash, the Bourne Again SHell. + * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc. * - * Copyright (C) 2003 Vladimir Oleynik + * Copyright (C) 2003 Vladimir Oleynik */ -static int +static int FAST_FUNC letcmd(int argc UNUSED_PARAM, char **argv) { arith_t i; @@ -12301,12 +12460,12 @@ if (!*argv) ash_msg_and_raise_error("expression expected"); do { - i = dash_arith(*argv); + i = ash_arith(*argv); } while (*++argv); return !i; } -#endif /* ASH_MATH_SUPPORT */ +#endif /* SH_MATH_SUPPORT */ /* ============ miscbltin.c @@ -12334,215 +12493,59 @@ * -d DELIM End on DELIM char, not newline * -e Use line editing (tty only) */ -static int +static int FAST_FUNC readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - static const char *const arg_REPLY[] = { "REPLY", NULL }; - - char **ap; - int backslash; - char c; - int rflag; - char *prompt; - const char *ifs; - char *p; - int startword; - int status; + char *opt_n = NULL; + char *opt_p = NULL; + char *opt_t = NULL; + char *opt_u = NULL; + int read_flags = 0; + const char *r; int i; - int fd = 0; -#if ENABLE_ASH_READ_NCHARS - int nchars = 0; /* if != 0, -n is in effect */ - int silent = 0; - struct termios tty, old_tty; -#endif -#if ENABLE_ASH_READ_TIMEOUT - unsigned end_ms = 0; - unsigned timeout = 0; -#endif - - rflag = 0; - prompt = NULL; - while ((i = nextopt("p:u:r" - USE_ASH_READ_TIMEOUT("t:") - USE_ASH_READ_NCHARS("n:s") - )) != '\0') { + + while ((i = nextopt("p:u:rt:n:s")) != '\0') { switch (i) { case 'p': - prompt = optionarg; + opt_p = optionarg; break; -#if ENABLE_ASH_READ_NCHARS case 'n': - nchars = bb_strtou(optionarg, NULL, 10); - if (nchars < 0 || errno) - ash_msg_and_raise_error("invalid count"); - /* nchars == 0: off (bash 3.2 does this too) */ + opt_n = optionarg; break; case 's': - silent = 1; + read_flags |= BUILTIN_READ_SILENT; break; -#endif -#if ENABLE_ASH_READ_TIMEOUT case 't': - timeout = bb_strtou(optionarg, NULL, 10); - if (errno || timeout > UINT_MAX / 2048) - ash_msg_and_raise_error("invalid timeout"); - timeout *= 1000; -#if 0 /* even bash have no -t N.NNN support */ - ts.tv_sec = bb_strtou(optionarg, &p, 10); - ts.tv_usec = 0; - /* EINVAL means number is ok, but not terminated by NUL */ - if (*p == '.' && errno == EINVAL) { - char *p2; - if (*++p) { - int scale; - ts.tv_usec = bb_strtou(p, &p2, 10); - if (errno) - ash_msg_and_raise_error("invalid timeout"); - scale = p2 - p; - /* normalize to usec */ - if (scale > 6) - ash_msg_and_raise_error("invalid timeout"); - while (scale++ < 6) - ts.tv_usec *= 10; - } - } else if (ts.tv_sec < 0 || errno) { - ash_msg_and_raise_error("invalid timeout"); - } - if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ - ash_msg_and_raise_error("invalid timeout"); - } -#endif /* if 0 */ + opt_t = optionarg; break; -#endif case 'r': - rflag = 1; + read_flags |= BUILTIN_READ_RAW; break; case 'u': - fd = bb_strtou(optionarg, NULL, 10); - if (fd < 0 || errno) - ash_msg_and_raise_error("invalid file descriptor"); + opt_u = optionarg; break; default: break; } } - if (prompt && isatty(fd)) { - out2str(prompt); - } - ap = argptr; - if (*ap == NULL) - ap = (char**)arg_REPLY; - ifs = bltinlookup("IFS"); - if (ifs == NULL) - ifs = defifs; -#if ENABLE_ASH_READ_NCHARS - tcgetattr(fd, &tty); - old_tty = tty; - if (nchars || silent) { - if (nchars) { - tty.c_lflag &= ~ICANON; - tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; - } - if (silent) { - tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); - } - /* if tcgetattr failed, tcsetattr will fail too. - * Ignoring, it's harmless. */ - tcsetattr(fd, TCSANOW, &tty); - } -#endif - status = 0; - startword = 2; - backslash = 0; -#if ENABLE_ASH_READ_TIMEOUT - if (timeout) /* NB: ensuring end_ms is nonzero */ - end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1; -#endif - STARTSTACKSTR(p); - do { - const char *is_ifs; - -#if ENABLE_ASH_READ_TIMEOUT - if (end_ms) { - struct pollfd pfd[1]; - pfd[0].fd = fd; - pfd[0].events = POLLIN; - timeout = end_ms - (unsigned)(monotonic_us() / 1000); - if ((int)timeout <= 0 /* already late? */ - || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ - ) { /* timed out! */ -#if ENABLE_ASH_READ_NCHARS - tcsetattr(fd, TCSANOW, &old_tty); -#endif - return 1; - } - } -#endif - if (nonblock_safe_read(fd, &c, 1) != 1) { - status = 1; - break; - } - if (c == '\0') - continue; - if (backslash) { - backslash = 0; - if (c != '\n') - goto put; - continue; - } - if (!rflag && c == '\\') { - backslash = 1; - continue; - } - if (c == '\n') - break; - is_ifs = strchr(ifs, c); - if (startword && is_ifs) { - if (isspace(c)) - continue; - /* non-space ifs char */ - startword--; - if (startword == 1) /* first one? */ - continue; - } - startword = 0; - if (ap[1] != NULL && is_ifs) { - const char *beg; - STACKSTRNUL(p); - beg = stackblock(); - setvar(*ap, beg, 0); - ap++; - /* can we skip one non-space ifs? (2: yes) */ - startword = isspace(c) ? 2 : 1; - STARTSTACKSTR(p); - continue; - } - put: - STPUTC(c, p); - } -/* end of do {} while: */ -#if ENABLE_ASH_READ_NCHARS - while (--nchars); -#else - while (1); -#endif + r = shell_builtin_read(setvar2, + argptr, + bltinlookup("IFS"), /* can be NULL */ + read_flags, + opt_n, + opt_p, + opt_t, + opt_u + ); -#if ENABLE_ASH_READ_NCHARS - tcsetattr(fd, TCSANOW, &old_tty); -#endif + if ((uintptr_t)r > 1) + ash_msg_and_raise_error(r); - STACKSTRNUL(p); - /* Remove trailing space ifs chars */ - while ((char *)stackblock() <= --p && isspace(*p) && strchr(ifs, *p) != NULL) - *p = '\0'; - setvar(*ap, stackblock(), 0); - while (*++ap != NULL) - setvar(*ap, nullstr, 0); - return status; + return (uintptr_t)r; } -static int +static int FAST_FUNC umaskcmd(int argc UNUSED_PARAM, char **argv) { static const char permuser[3] ALIGN1 = "ugo"; @@ -12553,6 +12556,8 @@ S_IROTH, S_IWOTH, S_IXOTH }; + /* TODO: use bb_parse_mode() instead */ + char *ap; mode_t mask; int i; @@ -12595,7 +12600,7 @@ mask = 0; do { if (*ap >= '8' || *ap < '0') - ash_msg_and_raise_error(illnum, argv[1]); + ash_msg_and_raise_error(msg_illnum, argv[1]); mask = (mask << 3) + (*ap - '0'); } while (*++ap != '\0'); umask(mask); @@ -12619,7 +12624,6 @@ * * Public domain. */ - struct limits { uint8_t cmd; /* RLIMIT_xxx fit into it */ uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ @@ -12717,11 +12721,10 @@ } } -static int +static int FAST_FUNC ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { - int c; - rlim_t val = 0; + rlim_t val; enum limtype how = SOFT | HARD; const struct limits *l; int set, all = 0; @@ -12782,6 +12785,7 @@ continue; set = *argptr ? 1 : 0; + val = 0; if (set) { char *p = *argptr; @@ -12790,15 +12794,13 @@ if (strncmp(p, "unlimited\n", 9) == 0) val = RLIM_INFINITY; else { - val = (rlim_t) 0; - - while ((c = *p++) >= '0' && c <= '9') { - val = (val * 10) + (long)(c - '0'); - // val is actually 'unsigned long int' and can't get < 0 - if (val < (rlim_t) 0) - break; - } - if (c) + if (sizeof(val) == sizeof(int)) + val = bb_strtou(p, NULL, 10); + else if (sizeof(val) == sizeof(long)) + val = bb_strtoul(p, NULL, 10); + else + val = bb_strtoull(p, NULL, 10); + if (errno) ash_msg_and_raise_error("bad number"); val <<= l->factor_shift; } @@ -12828,654 +12830,6 @@ return 0; } - -/* ============ Math support */ - -#if ENABLE_ASH_MATH_SUPPORT - -/* Copyright (c) 2001 Aaron Lehmann - - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -/* This is my infix parser/evaluator. It is optimized for size, intended - * as a replacement for yacc-based parsers. However, it may well be faster - * than a comparable parser written in yacc. The supported operators are - * listed in #defines below. Parens, order of operations, and error handling - * are supported. This code is thread safe. The exact expression format should - * be that which POSIX specifies for shells. */ - -/* The code uses a simple two-stack algorithm. See - * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html - * for a detailed explanation of the infix-to-postfix algorithm on which - * this is based (this code differs in that it applies operators immediately - * to the stack instead of adding them to a queue to end up with an - * expression). */ - -/* To use the routine, call it with an expression string and error return - * pointer */ - -/* - * Aug 24, 2001 Manuel Novoa III - * - * Reduced the generated code size by about 30% (i386) and fixed several bugs. - * - * 1) In arith_apply(): - * a) Cached values of *numptr and &(numptr[-1]). - * b) Removed redundant test for zero denominator. - * - * 2) In arith(): - * a) Eliminated redundant code for processing operator tokens by moving - * to a table-based implementation. Also folded handling of parens - * into the table. - * b) Combined all 3 loops which called arith_apply to reduce generated - * code size at the cost of speed. - * - * 3) The following expressions were treated as valid by the original code: - * 1() , 0! , 1 ( *3 ) . - * These bugs have been fixed by internally enclosing the expression in - * parens and then checking that all binary ops and right parens are - * preceded by a valid expression (NUM_TOKEN). - * - * Note: It may be desirable to replace Aaron's test for whitespace with - * ctype's isspace() if it is used by another busybox applet or if additional - * whitespace chars should be considered. Look below the "#include"s for a - * precompiler test. - */ - -/* - * Aug 26, 2001 Manuel Novoa III - * - * Return 0 for null expressions. Pointed out by Vladimir Oleynik. - * - * Merge in Aaron's comments previously posted to the busybox list, - * modified slightly to take account of my changes to the code. - * - */ - -/* - * (C) 2003 Vladimir Oleynik - * - * - allow access to variable, - * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) - * - realize assign syntax (VAR=expr, +=, *= etc) - * - realize exponentiation (** operator) - * - realize comma separated - expr, expr - * - realise ++expr --expr expr++ expr-- - * - realise expr ? expr : expr (but, second expr calculate always) - * - allow hexadecimal and octal numbers - * - was restored loses XOR operator - * - remove one goto label, added three ;-) - * - protect $((num num)) as true zero expr (Manuel`s error) - * - always use special isspace(), see comment from bash ;-) - */ - -#define arith_isspace(arithval) \ - (arithval == ' ' || arithval == '\n' || arithval == '\t') - -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and /. */ - -#define tok_decl(prec,id) (((id)<<5)|(prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ -#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0) - -/* conditional is right associativity too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* exponent is right associativity */ -#define TOK_EXPONENT tok_decl(15,1) - -/* For now unary operators. */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -#define NUMPTR (*numstackptr) - -static int -tok_have_assign(operator op) -{ - operator prec = PREC(op); - - convert_prec_is_assing(prec); - return (prec == PREC(TOK_ASSIGN) || - prec == PREC_PRE || prec == PREC_POST); -} - -static int -is_right_associativity(operator prec) -{ - return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL)); -} - -typedef struct { - arith_t val; - arith_t contidional_second_val; - char contidional_second_val_initialized; - char *var; /* if NULL then is regular number, - else is variable name */ -} v_n_t; - -typedef struct chk_var_recursive_looped_t { - const char *var; - struct chk_var_recursive_looped_t *next; -} chk_var_recursive_looped_t; - -static chk_var_recursive_looped_t *prev_chk_var_recursive; - -static int -arith_lookup_val(v_n_t *t) -{ - if (t->var) { - const char * p = lookupvar(t->var); - - if (p) { - int errcode; - - /* recursive try as expression */ - chk_var_recursive_looped_t *cur; - chk_var_recursive_looped_t cur_save; - - for (cur = prev_chk_var_recursive; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* expression recursion loop detected */ - return -5; - } - } - /* save current lookuped var name */ - cur = prev_chk_var_recursive; - cur_save.var = t->var; - cur_save.next = cur; - prev_chk_var_recursive = &cur_save; - - t->val = arith (p, &errcode); - /* restore previous ptr after recursiving */ - prev_chk_var_recursive = cur; - return errcode; - } - /* allow undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "applying" a token means performing it on the top elements on the integer - * stack. For a unary operator it will only change the top element, but a - * binary operator will pop two arguments and push a result */ -static int -arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr) -{ - v_n_t *numptr_m1; - arith_t numptr_val, rez; - int ret_arith_lookup_val; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) goto err; - numptr_m1 = NUMPTR - 1; - - /* check operand is var with noninteger value */ - ret_arith_lookup_val = arith_lookup_val(numptr_m1); - if (ret_arith_lookup_val) - return ret_arith_lookup_val; - - rez = numptr_m1->val; - if (op == TOK_UMINUS) - rez *= -1; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - - /* check and binary operators need two arguments */ - if (numptr_m1 == numstack) goto err; - - /* ... and they pop one */ - --NUMPTR; - numptr_val = rez; - if (op == TOK_CONDITIONAL) { - if (!numptr_m1->contidional_second_val_initialized) { - /* protect $((expr1 ? expr2)) without ": expr" */ - goto err; - } - rez = numptr_m1->contidional_second_val; - } else if (numptr_m1->contidional_second_val_initialized) { - /* protect $((expr1 : expr2)) without "expr ? " */ - goto err; - } - numptr_m1 = NUMPTR - 1; - if (op != TOK_ASSIGN) { - /* check operand is var with noninteger value for not '=' */ - ret_arith_lookup_val = arith_lookup_val(numptr_m1); - if (ret_arith_lookup_val) - return ret_arith_lookup_val; - } - if (op == TOK_CONDITIONAL) { - numptr_m1->contidional_second_val = rez; - } - rez = numptr_m1->val; - if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= numptr_val; - else if (op == TOK_OR) - rez = numptr_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= numptr_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= numptr_val; - else if (op == TOK_AND) - rez = rez && numptr_val; - else if (op == TOK_EQ) - rez = (rez == numptr_val); - else if (op == TOK_NE) - rez = (rez != numptr_val); - else if (op == TOK_GE) - rez = (rez >= numptr_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= numptr_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= numptr_val; - else if (op == TOK_GT) - rez = (rez > numptr_val); - else if (op == TOK_LT) - rez = (rez < numptr_val); - else if (op == TOK_LE) - rez = (rez <= numptr_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= numptr_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += numptr_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= numptr_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = numptr_val; - else if (op == TOK_CONDITIONAL_SEP) { - if (numptr_m1 == numstack) { - /* protect $((expr : expr)) without "expr ? " */ - goto err; - } - numptr_m1->contidional_second_val_initialized = op; - numptr_m1->contidional_second_val = numptr_val; - } else if (op == TOK_CONDITIONAL) { - rez = rez ? - numptr_val : numptr_m1->contidional_second_val; - } else if (op == TOK_EXPONENT) { - if (numptr_val < 0) - return -3; /* exponent less than 0 */ - else { - arith_t c = 1; - - if (numptr_val) - while (numptr_val--) - c *= rez; - rez = c; - } - } else if (numptr_val==0) /* zero divisor check */ - return -2; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= numptr_val; - else if (op == TOK_REM || op == TOK_REM_ASSIGN) - rez %= numptr_val; - } - if (tok_have_assign(op)) { - char buf[sizeof(arith_t_type)*3 + 2]; - - if (numptr_m1->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* save to shell variable */ -#if ENABLE_ASH_MATH_SUPPORT_64 - snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez); -#else - snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez); -#endif - setvar(numptr_m1->var, buf, 0); - /* after saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - else if (op == TOK_POST_DEC) - rez++; - } - numptr_m1->val = rez; - /* protect geting var value, is number now */ - numptr_m1->var = NULL; - return 0; - err: - return -1; -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -/* ptr to ")" */ -#define endexpression (&op_tokens[sizeof(op_tokens)-7]) - -static arith_t -arith(const char *expr, int *perrcode) -{ - char arithval; /* Current character under analysis */ - operator lasttok, op; - operator prec; - operator *stack, *stackptr; - const char *p = endexpression; - int errcode; - v_n_t *numstack, *numstackptr; - unsigned datasizes = strlen(expr) + 2; - - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 integers - * in any given correct or incorrect expression is left as an exercise to - * the reader. */ - numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); - /* Stack of operator tokens */ - stackptr = stack = alloca(datasizes * sizeof(stack[0])); - - *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ - *perrcode = errcode = 0; - - while (1) { - arithval = *expr; - if (arithval == 0) { - if (p == endexpression) { - /* Null expression. */ - return 0; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != endexpression + 1) { - /* If we haven't done so already, */ - /* append a closing right paren */ - expr = endexpression; - /* and let the loop process it. */ - continue; - } - /* At this point, we're done with the expression. */ - if (numstackptr != numstack+1) { - /* ... but if there isn't, it's bad */ - err: - *perrcode = -1; - return *perrcode; - } - if (numstack->var) { - /* expression is $((var)) only, lookup now */ - errcode = arith_lookup_val(numstack); - } - ret: - *perrcode = errcode; - return numstack->val; - } - - /* Continue processing the expression. */ - if (arith_isspace(arithval)) { - /* Skip whitespace */ - goto prologue; - } - p = endofname(expr); - if (p != expr) { - size_t var_name_size = (p-expr) + 1; /* trailing zero */ - - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); - expr = p; - num: - numstackptr->contidional_second_val_initialized = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - if (isdigit(arithval)) { - numstackptr->var = NULL; -#if ENABLE_ASH_MATH_SUPPORT_64 - numstackptr->val = strtoll(expr, (char **) &expr, 0); -#else - numstackptr->val = strtol(expr, (char **) &expr, 0); -#endif - goto num; - } - for (p = op_tokens; ; p++) { - const char *o; - - if (*p == 0) { - /* strange operator not found */ - goto err; - } - for (o = expr; *p && *o == *p; p++) - o++; - if (!*p) { - /* found */ - expr = o - 1; - break; - } - /* skip tail uncompared token */ - while (*p) - p++; - /* skip zero delim */ - p++; - } - op = p[1]; - - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; - - /* Plus and minus are binary (not unary) _only_ if the last - * token was as number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want a unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. */ - /* But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. */ - /* Left paren is given the lowest priority so it will never be - * "applied" in this way. - * if associativity is right and priority eq, applied also skip - */ - prec = PREC(op); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - while (stackptr != stack) { - if (op == TOK_RPAREN) { - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - if (stackptr[-1] == TOK_LPAREN) { - --stackptr; - /* Any operator directly after a */ - lasttok = TOK_NUM; - /* close paren should consider itself binary */ - goto prologue; - } - } else { - operator prev_prec = PREC(stackptr[-1]); - - convert_prec_is_assing(prec); - convert_prec_is_assing(prev_prec); - if (prev_prec < prec) - break; - /* check right assoc */ - if (prev_prec == prec && is_right_associativity(prec)) - break; - } - errcode = arith_apply(*--stackptr, numstack, &numstackptr); - if (errcode) goto ret; - } - if (op == TOK_RPAREN) { - goto err; - } - } - - /* Push this operator to the stack and remember it. */ - *stackptr++ = lasttok = op; - prologue: - ++expr; - } /* while */ -} -#endif /* ASH_MATH_SUPPORT */ - - /* ============ main() and helpers */ /* @@ -13492,7 +12846,7 @@ status = exitstatus; TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); if (setjmp(loc.loc)) { - if (exception == EXEXIT) + if (exception_type == 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 @@ -13505,6 +12859,7 @@ if (p) { trap[0] = NULL; evalstring(p, 0); + free(p); } flush_stdout_stderr(); out: @@ -13517,15 +12872,18 @@ init(void) { /* from input.c: */ - basepf.nextc = basepf.buf = basebuf; + basepf.next_to_pgetc = basepf.buf = basebuf; /* from trap.c: */ signal(SIGCHLD, SIG_DFL); + /* bash re-enables SIGHUP which is SIG_IGNed on entry. + * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" + */ + signal(SIGHUP, SIG_DFL); /* from var.c: */ { char **envp; - char ppid[sizeof(int)*3 + 1]; const char *p; struct stat st1, st2; @@ -13536,8 +12894,7 @@ } } - snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid()); - setvar("PPID", ppid, 0); + setvar("PPID", utoa(getppid()), 0); p = lookupvar("PWD"); if (p) @@ -13638,7 +12995,8 @@ evalskip = 0; loopnest = 0; /* from input.c: */ - parselleft = parsenleft = 0; /* clear input buffer */ + g_parsefile->left_in_buffer = 0; + g_parsefile->left_in_line = 0; /* clear input buffer */ popallfiles(); /* from parser.c: */ tokpushback = 0; @@ -13662,8 +13020,8 @@ int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int ash_main(int argc UNUSED_PARAM, char **argv) { - char *shinit; - volatile int state; + const char *shinit; + volatile smallint state; struct jmploc jmploc; struct stackmark smark; @@ -13685,21 +13043,20 @@ #endif state = 0; if (setjmp(jmploc.loc)) { - int e; - int s; + smallint e; + smallint s; reset(); - e = exception; + e = exception_type; if (e == EXERROR) exitstatus = 2; s = state; if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) exitshell(); - - if (e == EXINT) { + if (e == EXINT) outcslow('\n', stderr); - } + popstackmark(&smark); FORCE_INT_ON; /* enable interrupts */ if (s == 1) @@ -13718,11 +13075,6 @@ #endif rootpid = getpid(); -#if ENABLE_ASH_RANDOM_SUPPORT - /* Can use monotonic_ns() for better randomness but for now it is - * not used anywhere else in busybox... so avoid bloat */ - random_galois_LFSR = random_LCG = rootpid + monotonic_us(); -#endif init(); setstackmark(&smark); procargs(argv); @@ -13741,7 +13093,7 @@ } } #endif - if (argv[0] && argv[0][0] == '-') + if (/* argv[0] && */ argv[0][0] == '-') isloginsh = 1; if (isloginsh) { state = 1; @@ -13768,18 +13120,18 @@ if (minusc) { /* evalstring pushes parsefile stack. * Ensure we don't falsely claim that 0 (stdin) - * is one of stacked source fds */ + * is one of stacked source fds. + * Testcase: ash -c 'exec 1>&0' must not complain. */ if (!sflag) g_parsefile->fd = -1; evalstring(minusc, 0); } if (sflag || minusc == NULL) { -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +#if defined MAX_HISTORY && MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE"); - - if (hp != NULL) + if (hp) line_input_state->hist_file = hp; } #endif