X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fash.c;h=481b84138dbed7da9286ca525934f34097d3fa08;hb=4b8b37f9815892a1c221c0ca9f8eec623ab71866;hp=b27b2777ea5975e0c2ddd4ba26c9e21f0e4e5ca5;hpb=5e34ff29bcc870936ab18172f438a34d042d4e03;p=oweals%2Fbusybox.git diff --git a/shell/ash.c b/shell/ash.c index b27b2777e..481b84138 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -51,16 +51,21 @@ #include #include #include "math.h" +#if ENABLE_ASH_RANDOM_SUPPORT +# include "random.h" +#else +# define CLEAR_RANDOM_T(rnd) ((void)0) +#endif #if defined SINGLE_APPLET_MAIN /* 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__ +# 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 @@ -112,7 +117,7 @@ enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; 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 @@ -142,17 +147,10 @@ struct globals_misc { 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 */ -// TODO: rename -// pendingsig -> pending_sig -// intpending -> pending_int - 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; + volatile /*sig_atomic_t*/ smallint pending_sig; smallint exception_type; /* kind of exception (0..5) */ /* exceptions */ #define EXINT 0 /* SIGINT received */ @@ -200,12 +198,11 @@ struct globals_misc { /* indicates specified signal received */ 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). */ @@ -220,18 +217,17 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; #define arg0 (G_misc.arg0 ) #define exception_handler (G_misc.exception_handler) #define exception_type (G_misc.exception_type ) -#define suppressint (G_misc.suppressint ) -#define intpending (G_misc.intpending ) -//#define exsig (G_misc.exsig ) -#define pendingsig (G_misc.pendingsig ) +#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 +235,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; barrier(); \ curdir = nullstr; \ physdir = nullstr; \ + trap_ptr = trap; \ } while (0) @@ -251,7 +248,7 @@ static void trace_vprintf(const char *fmt, va_list va); # 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,7 +260,7 @@ static void trace_vprintf(const char *fmt, va_list va); /* ============ Utility functions */ #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) -/* C99 say: "char" declaration may be signed or unsigned by default */ +/* C99 says: "char" declaration may be signed or unsigned by default */ #define signed_char2int(sc) ((int)(signed char)(sc)) static int isdigit_str9(const char *str) @@ -283,7 +280,7 @@ static int isdigit_str9(const char *str) * more fun than worrying about efficiency and portability. :-)) */ #define INT_OFF do { \ - suppressint++; \ + suppress_int++; \ xbarrier(); \ } while (0) @@ -324,11 +321,11 @@ raise_interrupt(void) { 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() */ ex_type = EXSIG; if (gotsig[SIGINT - 1] && !trap[SIGINT]) { @@ -353,7 +350,7 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void int_on(void) { xbarrier(); - if (--suppressint == 0 && intpending) { + if (--suppress_int == 0 && pending_int) { raise_interrupt(); } } @@ -362,18 +359,18 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void force_int_on(void) { xbarrier(); - suppressint = 0; - if (intpending) + suppress_int = 0; + if (pending_int) raise_interrupt(); } #define FORCE_INT_ON force_int_on() -#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) @@ -461,15 +458,15 @@ out2str(const char *p) /* ============ 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 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') /* variable substitution byte (follows CTLVAR) */ #define VSTYPE 0x0f /* type of variable substitution */ @@ -640,6 +637,12 @@ union node { 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; @@ -679,7 +682,7 @@ trace_printf(const char *fmt, ...) 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); @@ -695,7 +698,7 @@ trace_vprintf(const char *fmt, va_list va) 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); } @@ -954,6 +957,12 @@ shtree(union node *n, int ind, char *pfx, FILE *fp) return; indent(ind, pfx, fp); + + if (n == NODE_EOF) { + fputs("", fp); + return; + } + switch (n->type) { case NSEMI: s = "; "; @@ -976,7 +985,7 @@ shtree(union node *n, int ind, char *pfx, FILE *fp) 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); } @@ -997,7 +1006,7 @@ static void showtree(union node *n) { trace_puts("showtree called\n"); - shtree(n, 1, NULL, stdout); + shtree(n, 1, NULL, stderr); } #endif /* DEBUG */ @@ -1148,6 +1157,49 @@ errmsg(int e, const char *em) /* ============ 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. @@ -1157,7 +1209,7 @@ enum { /* 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), }; @@ -1203,43 +1255,10 @@ extern struct globals_memstack *const ash_ptr_to_globals_memstack; 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 @@ -1544,7 +1563,7 @@ static int 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); } @@ -1686,14 +1705,14 @@ freeparam(volatile struct shparam *param) } #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 */ }; @@ -1730,13 +1749,13 @@ static const char defifs[] ALIGN1 = " \t\n"; /* 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') @@ -1745,17 +1764,17 @@ change_lc_ctype(const char *value) #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 }, @@ -1861,7 +1880,7 @@ extern struct globals_var *const ash_ptr_to_globals_var; #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); @@ -2224,17 +2243,17 @@ listvars(int on, int off, char ***end) /* ============ 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; @@ -2339,8 +2358,6 @@ setprompt(int whichprompt) #define CD_PHYSICAL 1 #define CD_PRINT 2 -static int docd(const char *, int); - static int cdopt(void) { @@ -2348,7 +2365,7 @@ cdopt(void) int i, j; j = 'L'; - while ((i = nextopt("LP"))) { + while ((i = nextopt("LP")) != '\0') { if (i != j) { flags ^= CD_PHYSICAL; j = i; @@ -2492,7 +2509,7 @@ docd(const char *dest, int flags) return err; } -static int +static int FAST_FUNC cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { const char *dest; @@ -2538,7 +2555,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } 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; @@ -2556,7 +2573,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) return 0; } -static int +static int FAST_FUNC pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int flags; @@ -2698,8 +2715,8 @@ SIT(int c, int syntax) } else #endif { - if ((unsigned char)c >= (unsigned char)(CTLESC) - && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) + if ((unsigned char)c >= CTLESC + && (unsigned char)c <= CTLQUOTEMARK ) { return CCTL; } @@ -3161,7 +3178,7 @@ printalias(const struct alias *ap) /* * TODO - sort output */ -static int +static int FAST_FUNC aliascmd(int argc UNUSED_PARAM, char **argv) { char *n, *v; @@ -3196,7 +3213,7 @@ aliascmd(int argc UNUSED_PARAM, char **argv) return ret; } -static int +static int FAST_FUNC unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int i; @@ -3228,9 +3245,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) #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 @@ -3238,7 +3255,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * 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() */ @@ -3268,9 +3284,6 @@ struct job { }; 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 *); @@ -3304,14 +3317,14 @@ onsig(int signo) { gotsig[signo - 1] = 1; - if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { - if (!suppressint) { - pendingsig = 0; + if (signo == SIGINT && !trap[SIGINT]) { + if (!suppress_int) { + pending_sig = 0; raise_interrupt(); /* does not return */ } - intpending = 1; + pending_int = 1; } else { - pendingsig = signo; + pending_sig = signo; } } @@ -3498,7 +3511,7 @@ getjob(const char *name, int getctl) { 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; @@ -3534,7 +3547,6 @@ getjob(const char *name, int getctl) } if (is_number(p)) { -// TODO: number() instead? It does error checking... num = atoi(p); if (num < njobs) { jp = jobtab + num - 1; @@ -3550,10 +3562,8 @@ getjob(const char *name, int getctl) p++; } - found = 0; - while (1) { - if (!jp) - goto err; + found = NULL; + while (jp) { if (match(jp->ps[0].cmd, p)) { if (found) goto err; @@ -3562,6 +3572,9 @@ getjob(const char *name, int getctl) } jp = jp->prev_job; } + if (!found) + goto err; + jp = found; gotit: #if JOBS @@ -3680,7 +3693,7 @@ setjobctl(int on) doing_jobctl = on; } -static int +static int FAST_FUNC killcmd(int argc, char **argv) { int i = 1; @@ -3745,7 +3758,7 @@ restartjob(struct job *jp, int mode) return status; } -static int +static int FAST_FUNC fg_bgcmd(int argc UNUSED_PARAM, char **argv) { struct job *jp; @@ -3905,7 +3918,7 @@ static int blocking_wait_with_raise_on_sig(struct job *job) { pid_t pid = dowait(DOWAIT_BLOCK, job); - if (pid <= 0 && pendingsig) + if (pid <= 0 && pending_sig) raise_exception(EXSIG); return pid; } @@ -3922,7 +3935,7 @@ showjob(FILE *out, struct job *jp, int mode) 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); return; @@ -3932,11 +3945,11 @@ showjob(FILE *out, struct job *jp, int mode) 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) + if (mode & SHOW_PIDS) col += fmtstr(s + col, 16, "%d ", ps->pid); psend = ps + jp->nprocs; @@ -3950,25 +3963,32 @@ showjob(FILE *out, struct job *jp, int mode) 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 { + while (1) { /* 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->pid) - 1; start: - fprintf(out, "%s%*c%s", - s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd - ); - if (!(mode & SHOW_PID)) { - showpipe(jp, out); + fprintf(out, "%s%*c", s, 33 - col >= 0 ? 33 - col : 0, ' '); + if (ps != jp->ps) + fprintf(out, "| "); + fprintf(out, "%s", ps->cmd); + if (++ps == psend) break; - } - if (++ps == psend) { - outcslow('\n', out); - break; - } - } while (1); + } + outcslow('\n', out); jp->changed = 0; @@ -3987,7 +4007,7 @@ showjobs(FILE *out, int mode) { struct job *jp; - TRACE(("showjobs(%x) called\n", mode)); + TRACE(("showjobs(0x%x) called\n", mode)); /* Handle all finished jobs */ while (dowait(DOWAIT_NONBLOCK, NULL) > 0) @@ -4000,23 +4020,23 @@ showjobs(FILE *out, int mode) } } -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 showjobs(stdout, mode); @@ -4048,21 +4068,19 @@ getstatus(struct job *job) } 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); @@ -4298,7 +4316,7 @@ cmdputs(const char *s) if (!str) continue; dostr: - while ((c = *str++)) { + while ((c = *str++) != '\0') { USTPUTC(c, nextc); } } @@ -4375,11 +4393,12 @@ cmdtxt(union node *n) 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; @@ -4516,9 +4535,11 @@ clear_traps(void) 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; } @@ -4529,8 +4550,8 @@ clear_traps(void) 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; @@ -4543,6 +4564,53 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) * 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? */ + && 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 */ @@ -4586,6 +4654,19 @@ forkchild(struct job *jp, /*union node *n,*/ int mode) * Take care of the second rule: */ setsignal(SIGQUIT); } +#if JOBS + if (n && n->type == NCMD + && 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; @@ -4646,10 +4727,12 @@ forkshell(struct job *jp, union node *n, int mode) 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; } @@ -4974,7 +5057,7 @@ struct redirtab { 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) @@ -5285,7 +5368,7 @@ ash_arith(const char *s) #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 */ @@ -5339,7 +5422,7 @@ esclen(const char *start, const char *p) { size_t esc = 0; - while (p > start && *--p == CTLESC) { + while (p > start && (unsigned char)*--p == CTLESC) { esc++; } return esc; @@ -5349,19 +5432,19 @@ esclen(const char *start, const char *p) * 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) { @@ -5380,28 +5463,33 @@ _rmescapes(char *str, int flag) 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) { +// 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) { p++; - if (notescaped && inquotes && *p != '/') { + if (protect_against_glob && inquotes && *p != '/') { *q++ = '\\'; } } - notescaped = globbing; + protect_against_glob = globbing; copy: *q++ = *p++; } @@ -5412,8 +5500,6 @@ _rmescapes(char *str, int flag) } return r; } -#define rmescapes(p) _rmescapes((p), 0) - #define pmatch(a, b) !fnmatch((a), (b), 0) /* @@ -5428,7 +5514,7 @@ preglob(const char *pattern, int quoted, int flag) if (quoted) { flag |= RMESCAPE_QUOTED; } - return _rmescapes((char *)pattern, flag); + return rmescapes((char *)pattern, flag); } /* @@ -5439,14 +5525,17 @@ memtodest(const char *p, size_t len, int syntax, int quotes) { char *q = expdest; - q = makestrspace(len * 2, q); + q = makestrspace(quotes ? len * 2 : len, q); while (len--) { int c = signed_char2int(*p++); if (!c) 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); } @@ -5523,13 +5612,13 @@ removerecordregions(int endoff) } static char * -exptilde(char *startp, char *p, int flag) +exptilde(char *startp, char *p, int flags) { 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; @@ -5541,7 +5630,7 @@ exptilde(char *startp, char *p, int flag) case CTLQUOTEMARK: return startp; case ':': - if (flag & EXP_VARTILDE) + if (flags & EXP_VARTILDE) goto done; break; case '/': @@ -5589,7 +5678,7 @@ static uint8_t back_exitstatus; /* exit status of backquoted command */ #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; @@ -5746,7 +5835,7 @@ expari(int quotes) expdest = p; if (quotes) - rmescapes(p + 2); + rmescapes(p + 2, 0); len = cvtnum(ash_arith(p + 2)); @@ -5756,7 +5845,7 @@ expari(int quotes) #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 @@ -5768,7 +5857,7 @@ static char *evalvar(char *p, int flag, struct strlist *var_str_list); * 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 = { '=', @@ -5786,42 +5875,44 @@ argstr(char *p, int flag, struct strlist *var_str_list) }; 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 (;;) { length += strcspn(p + length, reject); - c = p[length]; - if (c && (!(c & 0x80) + c = (unsigned char) p[length]; + if (c) { + if (!(c & 0x80) #if ENABLE_SH_MATH_SUPPORT - || c == CTLENDARI + || c == CTLENDARI #endif - )) { - /* c == '=' || c == ':' || c == CTLENDARI */ - length++; + ) { + /* c == '=' || c == ':' || c == CTLENDARI */ + length++; + } } if (length > 0) { int newloc; @@ -5839,11 +5930,11 @@ argstr(char *p, int flag, struct strlist *var_str_list) 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 ':': @@ -5862,15 +5953,13 @@ argstr(char *p, int flag, struct strlist *var_str_list) 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; @@ -5886,10 +5975,10 @@ argstr(char *p, int flag, struct strlist *var_str_list) 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; @@ -6095,15 +6184,15 @@ subevalvar(char *p, char *str, int strloc, int subtype, #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) { len--; @@ -6114,15 +6203,22 @@ subevalvar(char *p, char *str, int strloc, int subtype, 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; @@ -6166,7 +6262,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, 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; @@ -6283,8 +6379,18 @@ subevalvar(char *p, char *str, int strloc, int subtype, /* * Add the value of a specialized variable to the stack string. - */ -static ssize_t + * 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 NOINLINE ssize_t varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) { int num; @@ -6297,7 +6403,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) int syntax; int quoted = varflags & VSQUOTE; int subtype = varflags & VSTYPE; - int quotes = flags & (EXP_FULL | EXP_CASE); + int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); if (quoted && (flags & EXP_FULL)) sep = 1 << CHAR_BIT; @@ -6319,7 +6425,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) return -1; numvar: len = cvtnum(num); - break; + goto check_1char_name; case '-': expdest = makestrspace(NOPTS, expdest); for (i = NOPTS - 1; i >= 0; i--) { @@ -6328,6 +6434,12 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) len++; } } + check_1char_name: +#if 0 + /* handles cases similar to ${#$1} */ + if (name[2] != '\0') + raise_error_syntax("bad substitution"); +#endif break; case '@': if (sep) @@ -6341,7 +6453,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) ap = shellparam.p; if (!ap) return -1; - while ((p = *ap++)) { + while ((p = *ap++) != NULL) { size_t partlen; partlen = strlen(p); @@ -6375,8 +6487,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) 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; @@ -6428,7 +6539,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) * 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; @@ -6439,7 +6550,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) int startloc; ssize_t varlen; - varflags = *p++; + varflags = (unsigned char) *p++; subtype = varflags & VSTYPE; quoted = varflags & VSQUOTE; var = p; @@ -6448,7 +6559,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) 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--; @@ -6461,8 +6572,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) 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; @@ -6534,7 +6645,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) 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 - ( @@ -6788,7 +6900,7 @@ expmeta(char *enddir, char *name) 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)) { @@ -6909,7 +7021,7 @@ expandmeta(struct strlist *str /*, int flag*/) */ nometa: *exparg.lastp = str; - rmescapes(str->text); + rmescapes(str->text, 0); exparg.lastp = &str->next; } else { *exparg.lastp = NULL; @@ -6957,7 +7069,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) 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; @@ -7018,7 +7130,7 @@ casematch(union node *pattern, char *val) 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) @@ -7159,7 +7271,7 @@ shellexec(char **argv, const char *path, int idx) 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(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); if (errno != ENOENT && errno != ENOTDIR) @@ -7182,8 +7294,8 @@ shellexec(char **argv, const char *path, int idx) 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 */ } @@ -7198,7 +7310,7 @@ printentry(struct tblentry *cmdp) 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)); @@ -7312,7 +7424,7 @@ addcmdentry(char *name, struct cmdentry *entry) cmdp->rehash = 0; } -static int +static int FAST_FUNC hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { struct tblentry **pp; @@ -7382,7 +7494,7 @@ hashcd(void) * 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; @@ -7570,7 +7682,7 @@ describe_command(char *command, int describe_command_verbose) p = command; } else { do { - p = padvance(&path, command); + p = path_advance(&path, command); stunalloc(p); } while (--j >= 0); } @@ -7614,7 +7726,7 @@ describe_command(char *command, int describe_command_verbose) return 0; } -static int +static int FAST_FUNC typecmd(int argc UNUSED_PARAM, char **argv) { int i = 1; @@ -7633,7 +7745,7 @@ typecmd(int argc UNUSED_PARAM, char **argv) } #if ENABLE_ASH_CMDCMD -static int +static int FAST_FUNC commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int c; @@ -7673,7 +7785,7 @@ static char *funcstring; /* block to allocate strings from */ #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)), @@ -7985,7 +8097,7 @@ dotrap(void) uint8_t savestatus; savestatus = exitstatus; - pendingsig = 0; + pending_sig = 0; xbarrier(); TRACE(("dotrap entered\n")); @@ -8165,7 +8277,7 @@ evaltree(union node *n, int flags) out1: if (checkexit & exitstatus) evalskip |= SKIPEVAL; - else if (pendingsig && dotrap()) + else if (pending_sig && dotrap()) goto exexit; if (flags & EV_EXIT) { @@ -8451,12 +8563,13 @@ setinteractive(int on) 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; } } @@ -8496,7 +8609,7 @@ poplocalvars(void) 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); @@ -8644,7 +8757,7 @@ mklocal(char *name) /* * The "local" command. */ -static int +static int FAST_FUNC localcmd(int argc UNUSED_PARAM, char **argv) { char *name; @@ -8656,19 +8769,19 @@ localcmd(int argc UNUSED_PARAM, char **argv) 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]) { @@ -8683,7 +8796,7 @@ execcmd(int argc UNUSED_PARAM, char **argv) /* * The return command. */ -static int +static int FAST_FUNC returncmd(int argc UNUSED_PARAM, char **argv) { /* @@ -8695,28 +8808,28 @@ returncmd(int argc UNUSED_PARAM, char **argv) } /* 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_SH_MATH_SUPPORT -static int letcmd(int, char **); +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" @@ -8727,21 +8840,16 @@ static int ulimitcmd(int, char **); #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[] = { @@ -8864,7 +8972,7 @@ isassignment(const char *p) 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 @@ -9090,7 +9198,7 @@ evalcommand(union node *cmd, int flags) 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: @@ -9144,7 +9252,6 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) exitstatus |= ferror(stdout); clearerr(stdout); commandname = savecmdname; -// exsig = 0; exception_handler = savehandler; return i; @@ -9189,13 +9296,13 @@ prehash(union node *n) * 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) { @@ -9353,7 +9460,7 @@ preadfd(void) * 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 the is more stuff in this buffer, use it else call read to fill it. + * 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__) @@ -9706,7 +9813,7 @@ chkmail(void) 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') @@ -9734,7 +9841,7 @@ chkmail(void) popstackmark(&smark); } -static void +static void FAST_FUNC changemail(const char *val UNUSED_PARAM) { mail_var_path_changed = 1; @@ -9890,7 +9997,7 @@ options(int cmdline) /* * The shift builtin command. */ -static int +static int FAST_FUNC shiftcmd(int argc UNUSED_PARAM, char **argv) { int n; @@ -9952,7 +10059,7 @@ showvars(const char *sep_prefix, int on, int off) /* * The set command builtin. */ -static int +static int FAST_FUNC setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { int retval; @@ -9973,37 +10080,21 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) } #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 @@ -10103,7 +10194,7 @@ getopts(char *optstr, char *optvar, char **optfirst, int *param_optind, int *opt * 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; @@ -10148,12 +10239,6 @@ static char *wordtext; /* text of last word returned by readtoke 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) /* * Called when an unexpected token is read during the parse. The argument @@ -10393,7 +10478,7 @@ parsefname(void) 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) @@ -11198,6 +11283,8 @@ parsesub: { badsub: raise_error_syntax("bad substitution"); } + if (c != '}' && subtype == VSLENGTH) + goto badsub; STPUTC('=', out); flags = 0; @@ -11685,8 +11772,8 @@ peektoken(void) } /* - * 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) @@ -11700,7 +11787,7 @@ parsecmd(int interact) needprompt = 0; t = readtoken(); if (t == TEOF) - return NEOF; + return NODE_EOF; if (t == TNL) return NULL; tokpushback = 1; @@ -11775,7 +11862,7 @@ evalstring(char *s, int mask) setstackmark(&smark); skip = 0; - while ((n = parsecmd(0)) != NEOF) { + while ((n = parsecmd(0)) != NODE_EOF) { evaltree(n, 0); popstackmark(&smark); skip = evalskip; @@ -11792,7 +11879,7 @@ evalstring(char *s, int mask) /* * The eval command. */ -static int +static int FAST_FUNC evalcmd(int argc UNUSED_PARAM, char **argv) { char *p; @@ -11849,9 +11936,10 @@ cmdloop(int top) } n = parsecmd(inter); #if DEBUG - showtree(n); + if (DEBUG > 2 && debug && (n != NODE_EOF)) + showtree(n); #endif - if (n == NEOF) { + if (n == NODE_EOF) { if (!top || numeof >= 50) break; if (!stoppedjobs()) { @@ -11900,7 +11988,7 @@ find_dot_file(char *name) goto try_cur_dir; } - while ((fullname = padvance(&path, name)) != NULL) { + while ((fullname = path_advance(&path, name)) != NULL) { try_cur_dir: if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { /* @@ -11909,7 +11997,8 @@ find_dot_file(char *name) */ return fullname; } - stunalloc(fullname); + if (fullname != name) + stunalloc(fullname); } /* not found in the PATH */ @@ -11917,7 +12006,7 @@ find_dot_file(char *name) /* NOTREACHED */ } -static int +static int FAST_FUNC dotcmd(int argc, char **argv) { struct strlist *sp; @@ -11952,7 +12041,7 @@ dotcmd(int argc, char **argv) return status; } -static int +static int FAST_FUNC exitcmd(int argc UNUSED_PARAM, char **argv) { if (stoppedjobs()) @@ -12083,7 +12172,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) 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" */ @@ -12176,7 +12265,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) /* * The trap builtin. */ -static int +static int FAST_FUNC trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { char *action; @@ -12187,14 +12276,30 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 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++; @@ -12226,13 +12331,13 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) /* * Lists available builtins */ -static int +static int FAST_FUNC helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { unsigned col; unsigned i; - out1fmt("\n" + out1fmt( "Built-in commands:\n" "------------------\n"); for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) { @@ -12264,7 +12369,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) /* * The export and readonly commands. */ -static int +static int FAST_FUNC exportcmd(int argc UNUSED_PARAM, char **argv) { struct var *vp; @@ -12315,7 +12420,7 @@ unsetfunc(const char *name) * 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; @@ -12353,7 +12458,7 @@ static const unsigned char timescmd_str[] ALIGN1 = { 0 }; -static int +static int FAST_FUNC timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { long clk_tck, s, t; @@ -12383,7 +12488,7 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) * * Copyright (C) 2003 Vladimir Oleynik */ -static int +static int FAST_FUNC letcmd(int argc UNUSED_PARAM, char **argv) { arith_t i; @@ -12425,7 +12530,7 @@ typedef enum __rlimit_resource rlim_t; * -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 }; @@ -12635,7 +12740,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) return status; } -static int +static int FAST_FUNC umaskcmd(int argc UNUSED_PARAM, char **argv) { static const char permuser[3] ALIGN1 = "ugo"; @@ -12690,7 +12795,7 @@ umaskcmd(int argc UNUSED_PARAM, char **argv) 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); @@ -12811,11 +12916,10 @@ printlim(enum limtype how, const struct rlimit *limit, } } -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; @@ -12876,6 +12980,7 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) continue; set = *argptr ? 1 : 0; + val = 0; if (set) { char *p = *argptr; @@ -12884,15 +12989,13 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 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; } @@ -12951,6 +13054,7 @@ exitshell(void) if (p) { trap[0] = NULL; evalstring(p, 0); + free(p); } flush_stdout_stderr(); out: @@ -12971,7 +13075,6 @@ init(void) /* from var.c: */ { char **envp; - char ppid[sizeof(int)*3 + 1]; const char *p; struct stat st1, st2; @@ -12982,8 +13085,7 @@ init(void) } } - snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid()); - setvar("PPID", ppid, 0); + setvar("PPID", utoa(getppid()), 0); p = lookupvar("PWD"); if (p) @@ -13164,11 +13266,6 @@ int ash_main(int argc UNUSED_PARAM, char **argv) #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); @@ -13222,7 +13319,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) } if (sflag || minusc == NULL) { -#if ENABLE_FEATURE_EDITING_SAVEHISTORY +#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY if (iflag) { const char *hp = lookupvar("HISTFILE"); if (hp)