X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=shell%2Fash.c;h=481b84138dbed7da9286ca525934f34097d3fa08;hb=4b8b37f9815892a1c221c0ca9f8eec623ab71866;hp=1690c469b867d8b1e58c9588fd3445b5fb2e9cf9;hpb=c8334a40c9345d78e3a4e83cd94bff403d4ade74;p=oweals%2Fbusybox.git diff --git a/shell/ash.c b/shell/ash.c index 1690c469b..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) @@ -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) @@ -685,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); @@ -701,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); } @@ -1160,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. @@ -1169,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), }; @@ -1215,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 @@ -1556,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); } @@ -2351,8 +2358,6 @@ setprompt(int whichprompt) #define CD_PHYSICAL 1 #define CD_PRINT 2 -static int docd(const char *, int); - static int cdopt(void) { @@ -2360,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; @@ -2710,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; } @@ -3279,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 *); @@ -3315,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; } } @@ -3545,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; @@ -3917,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; } @@ -4025,7 +4026,7 @@ 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_PIDS; else @@ -4079,9 +4080,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) int retval; struct job *jp; -// exsig++; -// xbarrier(); - if (pendingsig) + if (pending_sig) raise_exception(EXSIG); nextopt(nullstr); @@ -4317,7 +4316,7 @@ cmdputs(const char *s) if (!str) continue; dostr: - while ((c = *str++)) { + while ((c = *str++) != '\0') { USTPUTC(c, nextc); } } @@ -4536,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; } @@ -4549,10 +4550,7 @@ clear_traps(void) static void closescript(void); /* Called after fork(), in child */ -#if !JOBS -# define forkchild(jp, n, mode) forkchild(jp, mode) -#endif -static void +static NOINLINE void forkchild(struct job *jp, union node *n, int mode) { int oldlvl; @@ -4566,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 */ @@ -4610,8 +4655,14 @@ forkchild(struct job *jp, union node *n, int mode) setsignal(SIGQUIT); } #if JOBS - if (n && n->type == NCMD && strcmp(n->ncmd.args->narg.text, "jobs") == 0) { + 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; } @@ -4676,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) + if (pid == 0) { + CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ forkchild(jp, n, mode); - else + } else { forkparent(jp, n, mode, pid); + } return pid; } @@ -5004,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) @@ -5315,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 */ @@ -5369,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; @@ -5385,13 +5438,13 @@ rmescapes(char *str, int flag) 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) { @@ -5410,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++; } @@ -5554,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; @@ -5572,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 '/': @@ -5787,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 @@ -5799,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 = { '=', @@ -5817,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 | EXP_REDIR); /* 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; @@ -5870,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 ':': @@ -5893,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; @@ -5917,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; @@ -6126,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--; @@ -6145,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; @@ -6314,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; @@ -6328,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; @@ -6350,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--) { @@ -6359,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) @@ -6372,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); @@ -6406,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; @@ -6459,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; @@ -6470,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; @@ -6479,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--; @@ -6492,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; @@ -6565,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 - ( @@ -6819,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)) { @@ -7213,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 */ } @@ -7704,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)), @@ -8016,7 +8097,7 @@ dotrap(void) uint8_t savestatus; savestatus = exitstatus; - pendingsig = 0; + pending_sig = 0; xbarrier(); TRACE(("dotrap entered\n")); @@ -8196,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) { @@ -8482,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; } } @@ -9116,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: @@ -9170,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; @@ -9221,7 +9302,7 @@ 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) { @@ -10002,34 +10083,18 @@ setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 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 @@ -11218,6 +11283,8 @@ parsesub: { badsub: raise_error_syntax("bad substitution"); } + if (c != '}' && subtype == VSLENGTH) + goto badsub; STPUTC('=', out); flags = 0; @@ -12209,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++; @@ -12712,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); @@ -12836,8 +12919,7 @@ printlim(enum limtype how, const struct rlimit *limit, 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; @@ -12898,6 +12980,7 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) continue; set = *argptr ? 1 : 0; + val = 0; if (set) { char *p = *argptr; @@ -12906,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; } @@ -12973,6 +13054,7 @@ exitshell(void) if (p) { trap[0] = NULL; evalstring(p, 0); + free(p); } flush_stdout_stderr(); out: @@ -12993,7 +13075,6 @@ init(void) /* from var.c: */ { char **envp; - char ppid[sizeof(int)*3 + 1]; const char *p; struct stat st1, st2; @@ -13004,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) @@ -13186,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); @@ -13244,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)