diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2009-10-08 03:06:04 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2009-10-08 03:06:04 +0200 |
commit | 4ac9819263114edb9b5b638ffa6d2e41a4bb46e7 (patch) | |
tree | f0c5bc9c7a2bf3a384b85350bfe4c9ca5ec4858f /shell | |
parent | 5b807cd5acd1f27b3e7aa36aac2728be27c5907c (diff) | |
download | busybox-w32-4ac9819263114edb9b5b638ffa6d2e41a4bb46e7.tar.gz busybox-w32-4ac9819263114edb9b5b638ffa6d2e41a4bb46e7.tar.bz2 busybox-w32-4ac9819263114edb9b5b638ffa6d2e41a4bb46e7.zip |
apply post-1.15.1 fixes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 418 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir7.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir7.tests | 12 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir8.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-redir/redir8.tests | 15 | ||||
-rw-r--r-- | shell/ash_test/ash-signals/savetrap.right | 8 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/savetrap.tests | 9 | ||||
-rw-r--r-- | shell/hush.c | 188 | ||||
-rw-r--r-- | shell/hush_test/hush-trap/savetrap.right | 8 | ||||
-rwxr-xr-x | shell/hush_test/hush-trap/savetrap.tests | 9 | ||||
-rw-r--r-- | shell/hush_test/hush-trap/subshell.right | 6 | ||||
-rwxr-xr-x | shell/hush_test/hush-trap/subshell.tests | 20 |
12 files changed, 467 insertions, 232 deletions
diff --git a/shell/ash.c b/shell/ash.c index 4a163ffbc..952961373 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -112,7 +112,7 @@ enum { NOPTS = ARRAY_SIZE(optletters_optnames) }; | |||
112 | 112 | ||
113 | static const char homestr[] ALIGN1 = "HOME"; | 113 | static const char homestr[] ALIGN1 = "HOME"; |
114 | static const char snlfmt[] ALIGN1 = "%s\n"; | 114 | static const char snlfmt[] ALIGN1 = "%s\n"; |
115 | static const char illnum[] ALIGN1 = "Illegal number: %s"; | 115 | static const char msg_illnum[] ALIGN1 = "Illegal number: %s"; |
116 | 116 | ||
117 | /* | 117 | /* |
118 | * We enclose jmp_buf in a structure so that we can declare pointers to | 118 | * We enclose jmp_buf in a structure so that we can declare pointers to |
@@ -142,17 +142,10 @@ struct globals_misc { | |||
142 | 142 | ||
143 | struct jmploc *exception_handler; | 143 | struct jmploc *exception_handler; |
144 | 144 | ||
145 | // disabled by vda: cannot understand how it was supposed to work - | 145 | volatile int suppress_int; /* counter */ |
146 | // cannot fix bugs. That's why you have to explain your non-trivial designs! | 146 | volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ |
147 | // /* do we generate EXSIG events */ | ||
148 | // int exsig; /* counter */ | ||
149 | volatile int suppressint; /* counter */ | ||
150 | // TODO: rename | ||
151 | // pendingsig -> pending_sig | ||
152 | // intpending -> pending_int | ||
153 | volatile /*sig_atomic_t*/ smallint intpending; /* 1 = got SIGINT */ | ||
154 | /* last pending signal */ | 147 | /* last pending signal */ |
155 | volatile /*sig_atomic_t*/ smallint pendingsig; | 148 | volatile /*sig_atomic_t*/ smallint pending_sig; |
156 | smallint exception_type; /* kind of exception (0..5) */ | 149 | smallint exception_type; /* kind of exception (0..5) */ |
157 | /* exceptions */ | 150 | /* exceptions */ |
158 | #define EXINT 0 /* SIGINT received */ | 151 | #define EXINT 0 /* SIGINT received */ |
@@ -200,6 +193,7 @@ struct globals_misc { | |||
200 | /* indicates specified signal received */ | 193 | /* indicates specified signal received */ |
201 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 194 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
202 | char *trap[NSIG]; | 195 | char *trap[NSIG]; |
196 | char **trap_ptr; /* used only by "trap hack" */ | ||
203 | 197 | ||
204 | /* Rarely referenced stuff */ | 198 | /* Rarely referenced stuff */ |
205 | #if ENABLE_ASH_RANDOM_SUPPORT | 199 | #if ENABLE_ASH_RANDOM_SUPPORT |
@@ -220,16 +214,16 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
220 | #define arg0 (G_misc.arg0 ) | 214 | #define arg0 (G_misc.arg0 ) |
221 | #define exception_handler (G_misc.exception_handler) | 215 | #define exception_handler (G_misc.exception_handler) |
222 | #define exception_type (G_misc.exception_type ) | 216 | #define exception_type (G_misc.exception_type ) |
223 | #define suppressint (G_misc.suppressint ) | 217 | #define suppress_int (G_misc.suppress_int ) |
224 | #define intpending (G_misc.intpending ) | 218 | #define pending_int (G_misc.pending_int ) |
225 | //#define exsig (G_misc.exsig ) | 219 | #define pending_sig (G_misc.pending_sig ) |
226 | #define pendingsig (G_misc.pendingsig ) | ||
227 | #define isloginsh (G_misc.isloginsh ) | 220 | #define isloginsh (G_misc.isloginsh ) |
228 | #define nullstr (G_misc.nullstr ) | 221 | #define nullstr (G_misc.nullstr ) |
229 | #define optlist (G_misc.optlist ) | 222 | #define optlist (G_misc.optlist ) |
230 | #define sigmode (G_misc.sigmode ) | 223 | #define sigmode (G_misc.sigmode ) |
231 | #define gotsig (G_misc.gotsig ) | 224 | #define gotsig (G_misc.gotsig ) |
232 | #define trap (G_misc.trap ) | 225 | #define trap (G_misc.trap ) |
226 | #define trap_ptr (G_misc.trap_ptr ) | ||
233 | #define random_galois_LFSR (G_misc.random_galois_LFSR) | 227 | #define random_galois_LFSR (G_misc.random_galois_LFSR) |
234 | #define random_LCG (G_misc.random_LCG ) | 228 | #define random_LCG (G_misc.random_LCG ) |
235 | #define backgndpid (G_misc.backgndpid ) | 229 | #define backgndpid (G_misc.backgndpid ) |
@@ -239,6 +233,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
239 | barrier(); \ | 233 | barrier(); \ |
240 | curdir = nullstr; \ | 234 | curdir = nullstr; \ |
241 | physdir = nullstr; \ | 235 | physdir = nullstr; \ |
236 | trap_ptr = trap; \ | ||
242 | } while (0) | 237 | } while (0) |
243 | 238 | ||
244 | 239 | ||
@@ -283,7 +278,7 @@ static int isdigit_str9(const char *str) | |||
283 | * more fun than worrying about efficiency and portability. :-)) | 278 | * more fun than worrying about efficiency and portability. :-)) |
284 | */ | 279 | */ |
285 | #define INT_OFF do { \ | 280 | #define INT_OFF do { \ |
286 | suppressint++; \ | 281 | suppress_int++; \ |
287 | xbarrier(); \ | 282 | xbarrier(); \ |
288 | } while (0) | 283 | } while (0) |
289 | 284 | ||
@@ -324,11 +319,11 @@ raise_interrupt(void) | |||
324 | { | 319 | { |
325 | int ex_type; | 320 | int ex_type; |
326 | 321 | ||
327 | intpending = 0; | 322 | pending_int = 0; |
328 | /* Signal is not automatically unmasked after it is raised, | 323 | /* Signal is not automatically unmasked after it is raised, |
329 | * do it ourself - unmask all signals */ | 324 | * do it ourself - unmask all signals */ |
330 | sigprocmask_allsigs(SIG_UNBLOCK); | 325 | sigprocmask_allsigs(SIG_UNBLOCK); |
331 | /* pendingsig = 0; - now done in onsig() */ | 326 | /* pending_sig = 0; - now done in onsig() */ |
332 | 327 | ||
333 | ex_type = EXSIG; | 328 | ex_type = EXSIG; |
334 | if (gotsig[SIGINT - 1] && !trap[SIGINT]) { | 329 | if (gotsig[SIGINT - 1] && !trap[SIGINT]) { |
@@ -353,7 +348,7 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | |||
353 | int_on(void) | 348 | int_on(void) |
354 | { | 349 | { |
355 | xbarrier(); | 350 | xbarrier(); |
356 | if (--suppressint == 0 && intpending) { | 351 | if (--suppress_int == 0 && pending_int) { |
357 | raise_interrupt(); | 352 | raise_interrupt(); |
358 | } | 353 | } |
359 | } | 354 | } |
@@ -362,18 +357,18 @@ static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | |||
362 | force_int_on(void) | 357 | force_int_on(void) |
363 | { | 358 | { |
364 | xbarrier(); | 359 | xbarrier(); |
365 | suppressint = 0; | 360 | suppress_int = 0; |
366 | if (intpending) | 361 | if (pending_int) |
367 | raise_interrupt(); | 362 | raise_interrupt(); |
368 | } | 363 | } |
369 | #define FORCE_INT_ON force_int_on() | 364 | #define FORCE_INT_ON force_int_on() |
370 | 365 | ||
371 | #define SAVE_INT(v) ((v) = suppressint) | 366 | #define SAVE_INT(v) ((v) = suppress_int) |
372 | 367 | ||
373 | #define RESTORE_INT(v) do { \ | 368 | #define RESTORE_INT(v) do { \ |
374 | xbarrier(); \ | 369 | xbarrier(); \ |
375 | suppressint = (v); \ | 370 | suppress_int = (v); \ |
376 | if (suppressint == 0 && intpending) \ | 371 | if (suppress_int == 0 && pending_int) \ |
377 | raise_interrupt(); \ | 372 | raise_interrupt(); \ |
378 | } while (0) | 373 | } while (0) |
379 | 374 | ||
@@ -461,15 +456,15 @@ out2str(const char *p) | |||
461 | /* ============ Parser structures */ | 456 | /* ============ Parser structures */ |
462 | 457 | ||
463 | /* control characters in argument strings */ | 458 | /* control characters in argument strings */ |
464 | #define CTLESC '\201' /* escape next character */ | 459 | #define CTLESC ((unsigned char)'\201') /* escape next character */ |
465 | #define CTLVAR '\202' /* variable defn */ | 460 | #define CTLVAR ((unsigned char)'\202') /* variable defn */ |
466 | #define CTLENDVAR '\203' | 461 | #define CTLENDVAR ((unsigned char)'\203') |
467 | #define CTLBACKQ '\204' | 462 | #define CTLBACKQ ((unsigned char)'\204') |
468 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ | 463 | #define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ |
469 | /* CTLBACKQ | CTLQUOTE == '\205' */ | 464 | /* CTLBACKQ | CTLQUOTE == '\205' */ |
470 | #define CTLARI '\206' /* arithmetic expression */ | 465 | #define CTLARI ((unsigned char)'\206') /* arithmetic expression */ |
471 | #define CTLENDARI '\207' | 466 | #define CTLENDARI ((unsigned char)'\207') |
472 | #define CTLQUOTEMARK '\210' | 467 | #define CTLQUOTEMARK ((unsigned char)'\210') |
473 | 468 | ||
474 | /* variable substitution byte (follows CTLVAR) */ | 469 | /* variable substitution byte (follows CTLVAR) */ |
475 | #define VSTYPE 0x0f /* type of variable substitution */ | 470 | #define VSTYPE 0x0f /* type of variable substitution */ |
@@ -685,7 +680,7 @@ trace_printf(const char *fmt, ...) | |||
685 | if (DEBUG_PID) | 680 | if (DEBUG_PID) |
686 | fprintf(tracefile, "[%u] ", (int) getpid()); | 681 | fprintf(tracefile, "[%u] ", (int) getpid()); |
687 | if (DEBUG_SIG) | 682 | if (DEBUG_SIG) |
688 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); | 683 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); |
689 | va_start(va, fmt); | 684 | va_start(va, fmt); |
690 | vfprintf(tracefile, fmt, va); | 685 | vfprintf(tracefile, fmt, va); |
691 | va_end(va); | 686 | va_end(va); |
@@ -701,7 +696,7 @@ trace_vprintf(const char *fmt, va_list va) | |||
701 | if (DEBUG_PID) | 696 | if (DEBUG_PID) |
702 | fprintf(tracefile, "[%u] ", (int) getpid()); | 697 | fprintf(tracefile, "[%u] ", (int) getpid()); |
703 | if (DEBUG_SIG) | 698 | if (DEBUG_SIG) |
704 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pendingsig, intpending, suppressint); | 699 | fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int); |
705 | vfprintf(tracefile, fmt, va); | 700 | vfprintf(tracefile, fmt, va); |
706 | } | 701 | } |
707 | 702 | ||
@@ -1556,7 +1551,7 @@ static int | |||
1556 | number(const char *s) | 1551 | number(const char *s) |
1557 | { | 1552 | { |
1558 | if (!is_number(s)) | 1553 | if (!is_number(s)) |
1559 | ash_msg_and_raise_error(illnum, s); | 1554 | ash_msg_and_raise_error(msg_illnum, s); |
1560 | return atoi(s); | 1555 | return atoi(s); |
1561 | } | 1556 | } |
1562 | 1557 | ||
@@ -2351,8 +2346,6 @@ setprompt(int whichprompt) | |||
2351 | #define CD_PHYSICAL 1 | 2346 | #define CD_PHYSICAL 1 |
2352 | #define CD_PRINT 2 | 2347 | #define CD_PRINT 2 |
2353 | 2348 | ||
2354 | static int docd(const char *, int); | ||
2355 | |||
2356 | static int | 2349 | static int |
2357 | cdopt(void) | 2350 | cdopt(void) |
2358 | { | 2351 | { |
@@ -2360,7 +2353,7 @@ cdopt(void) | |||
2360 | int i, j; | 2353 | int i, j; |
2361 | 2354 | ||
2362 | j = 'L'; | 2355 | j = 'L'; |
2363 | while ((i = nextopt("LP"))) { | 2356 | while ((i = nextopt("LP")) != '\0') { |
2364 | if (i != j) { | 2357 | if (i != j) { |
2365 | flags ^= CD_PHYSICAL; | 2358 | flags ^= CD_PHYSICAL; |
2366 | j = i; | 2359 | j = i; |
@@ -2710,8 +2703,8 @@ SIT(int c, int syntax) | |||
2710 | } else | 2703 | } else |
2711 | #endif | 2704 | #endif |
2712 | { | 2705 | { |
2713 | if ((unsigned char)c >= (unsigned char)(CTLESC) | 2706 | if ((unsigned char)c >= CTLESC |
2714 | && (unsigned char)c <= (unsigned char)(CTLQUOTEMARK) | 2707 | && (unsigned char)c <= CTLQUOTEMARK |
2715 | ) { | 2708 | ) { |
2716 | return CCTL; | 2709 | return CCTL; |
2717 | } | 2710 | } |
@@ -3240,9 +3233,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
3240 | #define FORK_NOJOB 2 | 3233 | #define FORK_NOJOB 2 |
3241 | 3234 | ||
3242 | /* mode flags for showjob(s) */ | 3235 | /* mode flags for showjob(s) */ |
3243 | #define SHOW_PGID 0x01 /* only show pgid - for jobs -p */ | 3236 | #define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */ |
3244 | #define SHOW_PID 0x04 /* include process pid */ | 3237 | #define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */ |
3245 | #define SHOW_CHANGED 0x08 /* only jobs whose state has changed */ | 3238 | #define SHOW_CHANGED 0x04 /* only jobs whose state has changed */ |
3246 | 3239 | ||
3247 | /* | 3240 | /* |
3248 | * A job structure contains information about a job. A job is either a | 3241 | * A job structure contains information about a job. A job is either a |
@@ -3250,7 +3243,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
3250 | * latter case, pidlist will be non-NULL, and will point to a -1 terminated | 3243 | * latter case, pidlist will be non-NULL, and will point to a -1 terminated |
3251 | * array of pids. | 3244 | * array of pids. |
3252 | */ | 3245 | */ |
3253 | |||
3254 | struct procstat { | 3246 | struct procstat { |
3255 | pid_t pid; /* process id */ | 3247 | pid_t pid; /* process id */ |
3256 | int status; /* last process status from wait() */ | 3248 | int status; /* last process status from wait() */ |
@@ -3316,14 +3308,14 @@ onsig(int signo) | |||
3316 | { | 3308 | { |
3317 | gotsig[signo - 1] = 1; | 3309 | gotsig[signo - 1] = 1; |
3318 | 3310 | ||
3319 | if (/* exsig || */ (signo == SIGINT && !trap[SIGINT])) { | 3311 | if (signo == SIGINT && !trap[SIGINT]) { |
3320 | if (!suppressint) { | 3312 | if (!suppress_int) { |
3321 | pendingsig = 0; | 3313 | pending_sig = 0; |
3322 | raise_interrupt(); /* does not return */ | 3314 | raise_interrupt(); /* does not return */ |
3323 | } | 3315 | } |
3324 | intpending = 1; | 3316 | pending_int = 1; |
3325 | } else { | 3317 | } else { |
3326 | pendingsig = signo; | 3318 | pending_sig = signo; |
3327 | } | 3319 | } |
3328 | } | 3320 | } |
3329 | 3321 | ||
@@ -3546,7 +3538,6 @@ getjob(const char *name, int getctl) | |||
3546 | } | 3538 | } |
3547 | 3539 | ||
3548 | if (is_number(p)) { | 3540 | if (is_number(p)) { |
3549 | // TODO: number() instead? It does error checking... | ||
3550 | num = atoi(p); | 3541 | num = atoi(p); |
3551 | if (num < njobs) { | 3542 | if (num < njobs) { |
3552 | jp = jobtab + num - 1; | 3543 | jp = jobtab + num - 1; |
@@ -3918,7 +3909,7 @@ static int | |||
3918 | blocking_wait_with_raise_on_sig(struct job *job) | 3909 | blocking_wait_with_raise_on_sig(struct job *job) |
3919 | { | 3910 | { |
3920 | pid_t pid = dowait(DOWAIT_BLOCK, job); | 3911 | pid_t pid = dowait(DOWAIT_BLOCK, job); |
3921 | if (pid <= 0 && pendingsig) | 3912 | if (pid <= 0 && pending_sig) |
3922 | raise_exception(EXSIG); | 3913 | raise_exception(EXSIG); |
3923 | return pid; | 3914 | return pid; |
3924 | } | 3915 | } |
@@ -3935,7 +3926,7 @@ showjob(FILE *out, struct job *jp, int mode) | |||
3935 | 3926 | ||
3936 | ps = jp->ps; | 3927 | ps = jp->ps; |
3937 | 3928 | ||
3938 | if (mode & SHOW_PGID) { | 3929 | if (mode & SHOW_ONLY_PGID) { /* jobs -p */ |
3939 | /* just output process (group) id of pipeline */ | 3930 | /* just output process (group) id of pipeline */ |
3940 | fprintf(out, "%d\n", ps->pid); | 3931 | fprintf(out, "%d\n", ps->pid); |
3941 | return; | 3932 | return; |
@@ -3945,11 +3936,11 @@ showjob(FILE *out, struct job *jp, int mode) | |||
3945 | indent_col = col; | 3936 | indent_col = col; |
3946 | 3937 | ||
3947 | if (jp == curjob) | 3938 | if (jp == curjob) |
3948 | s[col - 2] = '+'; | 3939 | s[col - 3] = '+'; |
3949 | else if (curjob && jp == curjob->prev_job) | 3940 | else if (curjob && jp == curjob->prev_job) |
3950 | s[col - 2] = '-'; | 3941 | s[col - 3] = '-'; |
3951 | 3942 | ||
3952 | if (mode & SHOW_PID) | 3943 | if (mode & SHOW_PIDS) |
3953 | col += fmtstr(s + col, 16, "%d ", ps->pid); | 3944 | col += fmtstr(s + col, 16, "%d ", ps->pid); |
3954 | 3945 | ||
3955 | psend = ps + jp->nprocs; | 3946 | psend = ps + jp->nprocs; |
@@ -3963,25 +3954,32 @@ showjob(FILE *out, struct job *jp, int mode) | |||
3963 | status = jp->stopstatus; | 3954 | status = jp->stopstatus; |
3964 | col += sprint_status(s + col, status, 0); | 3955 | col += sprint_status(s + col, status, 0); |
3965 | } | 3956 | } |
3957 | /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ | ||
3966 | 3958 | ||
3959 | /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line | ||
3960 | * or prints several "PID | <cmdN>" lines, | ||
3961 | * depending on SHOW_PIDS bit. | ||
3962 | * We do not print status of individual processes | ||
3963 | * between PID and <cmdN>. bash does it, but not very well: | ||
3964 | * first line shows overall job status, not process status, | ||
3965 | * making it impossible to know 1st process status. | ||
3966 | */ | ||
3967 | goto start; | 3967 | goto start; |
3968 | 3968 | while (1) { | |
3969 | do { | ||
3970 | /* for each process */ | 3969 | /* for each process */ |
3971 | col = fmtstr(s, 48, " |\n%*c%d ", indent_col, ' ', ps->pid) - 3; | 3970 | s[0] = '\0'; |
3971 | col = 33; | ||
3972 | if (mode & SHOW_PIDS) | ||
3973 | col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->pid) - 1; | ||
3972 | start: | 3974 | start: |
3973 | fprintf(out, "%s%*c%s", | 3975 | fprintf(out, "%s%*c", s, 33 - col >= 0 ? 33 - col : 0, ' '); |
3974 | s, 33 - col >= 0 ? 33 - col : 0, ' ', ps->cmd | 3976 | if (ps != jp->ps) |
3975 | ); | 3977 | fprintf(out, "| "); |
3976 | if (!(mode & SHOW_PID)) { | 3978 | fprintf(out, "%s", ps->cmd); |
3977 | showpipe(jp, out); | 3979 | if (++ps == psend) |
3978 | break; | ||
3979 | } | ||
3980 | if (++ps == psend) { | ||
3981 | outcslow('\n', out); | ||
3982 | break; | 3980 | break; |
3983 | } | 3981 | } |
3984 | } while (1); | 3982 | outcslow('\n', out); |
3985 | 3983 | ||
3986 | jp->changed = 0; | 3984 | jp->changed = 0; |
3987 | 3985 | ||
@@ -4019,17 +4017,17 @@ jobscmd(int argc UNUSED_PARAM, char **argv) | |||
4019 | int mode, m; | 4017 | int mode, m; |
4020 | 4018 | ||
4021 | mode = 0; | 4019 | mode = 0; |
4022 | while ((m = nextopt("lp"))) { | 4020 | while ((m = nextopt("lp")) != '\0') { |
4023 | if (m == 'l') | 4021 | if (m == 'l') |
4024 | mode = SHOW_PID; | 4022 | mode |= SHOW_PIDS; |
4025 | else | 4023 | else |
4026 | mode = SHOW_PGID; | 4024 | mode |= SHOW_ONLY_PGID; |
4027 | } | 4025 | } |
4028 | 4026 | ||
4029 | argv = argptr; | 4027 | argv = argptr; |
4030 | if (*argv) { | 4028 | if (*argv) { |
4031 | do | 4029 | do |
4032 | showjob(stdout, getjob(*argv,0), mode); | 4030 | showjob(stdout, getjob(*argv, 0), mode); |
4033 | while (*++argv); | 4031 | while (*++argv); |
4034 | } else | 4032 | } else |
4035 | showjobs(stdout, mode); | 4033 | showjobs(stdout, mode); |
@@ -4073,9 +4071,7 @@ waitcmd(int argc UNUSED_PARAM, char **argv) | |||
4073 | int retval; | 4071 | int retval; |
4074 | struct job *jp; | 4072 | struct job *jp; |
4075 | 4073 | ||
4076 | // exsig++; | 4074 | if (pending_sig) |
4077 | // xbarrier(); | ||
4078 | if (pendingsig) | ||
4079 | raise_exception(EXSIG); | 4075 | raise_exception(EXSIG); |
4080 | 4076 | ||
4081 | nextopt(nullstr); | 4077 | nextopt(nullstr); |
@@ -4311,7 +4307,7 @@ cmdputs(const char *s) | |||
4311 | if (!str) | 4307 | if (!str) |
4312 | continue; | 4308 | continue; |
4313 | dostr: | 4309 | dostr: |
4314 | while ((c = *str++)) { | 4310 | while ((c = *str++) != '\0') { |
4315 | USTPUTC(c, nextc); | 4311 | USTPUTC(c, nextc); |
4316 | } | 4312 | } |
4317 | } | 4313 | } |
@@ -4530,9 +4526,11 @@ clear_traps(void) | |||
4530 | for (tp = trap; tp < &trap[NSIG]; tp++) { | 4526 | for (tp = trap; tp < &trap[NSIG]; tp++) { |
4531 | if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ | 4527 | if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */ |
4532 | INT_OFF; | 4528 | INT_OFF; |
4533 | free(*tp); | 4529 | if (trap_ptr == trap) |
4530 | free(*tp); | ||
4531 | /* else: it "belongs" to trap_ptr vector, don't free */ | ||
4534 | *tp = NULL; | 4532 | *tp = NULL; |
4535 | if (tp != &trap[0]) | 4533 | if ((tp - trap) != 0) |
4536 | setsignal(tp - trap); | 4534 | setsignal(tp - trap); |
4537 | INT_ON; | 4535 | INT_ON; |
4538 | } | 4536 | } |
@@ -4546,7 +4544,7 @@ static void closescript(void); | |||
4546 | #if !JOBS | 4544 | #if !JOBS |
4547 | # define forkchild(jp, n, mode) forkchild(jp, mode) | 4545 | # define forkchild(jp, n, mode) forkchild(jp, mode) |
4548 | #endif | 4546 | #endif |
4549 | static void | 4547 | static NOINLINE void |
4550 | forkchild(struct job *jp, union node *n, int mode) | 4548 | forkchild(struct job *jp, union node *n, int mode) |
4551 | { | 4549 | { |
4552 | int oldlvl; | 4550 | int oldlvl; |
@@ -4560,6 +4558,53 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4560 | * Do we do it correctly? */ | 4558 | * Do we do it correctly? */ |
4561 | 4559 | ||
4562 | closescript(); | 4560 | closescript(); |
4561 | |||
4562 | if (mode == FORK_NOJOB /* is it `xxx` ? */ | ||
4563 | && n && n->type == NCMD /* is it single cmd? */ | ||
4564 | /* && n->ncmd.args->type == NARG - always true? */ | ||
4565 | && strcmp(n->ncmd.args->narg.text, "trap") == 0 | ||
4566 | && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */ | ||
4567 | /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */ | ||
4568 | ) { | ||
4569 | TRACE(("Trap hack\n")); | ||
4570 | /* Awful hack for `trap` or $(trap). | ||
4571 | * | ||
4572 | * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html | ||
4573 | * contains an example where "trap" is executed in a subshell: | ||
4574 | * | ||
4575 | * save_traps=$(trap) | ||
4576 | * ... | ||
4577 | * eval "$save_traps" | ||
4578 | * | ||
4579 | * Standard does not say that "trap" in subshell shall print | ||
4580 | * parent shell's traps. It only says that its output | ||
4581 | * must have suitable form, but then, in the above example | ||
4582 | * (which is not supposed to be normative), it implies that. | ||
4583 | * | ||
4584 | * bash (and probably other shell) does implement it | ||
4585 | * (traps are reset to defaults, but "trap" still shows them), | ||
4586 | * but as a result, "trap" logic is hopelessly messed up: | ||
4587 | * | ||
4588 | * # trap | ||
4589 | * trap -- 'echo Ho' SIGWINCH <--- we have a handler | ||
4590 | * # (trap) <--- trap is in subshell - no output (correct, traps are reset) | ||
4591 | * # true | trap <--- trap is in subshell - no output (ditto) | ||
4592 | * # echo `true | trap` <--- in subshell - output (but traps are reset!) | ||
4593 | * trap -- 'echo Ho' SIGWINCH | ||
4594 | * # echo `(trap)` <--- in subshell in subshell - output | ||
4595 | * trap -- 'echo Ho' SIGWINCH | ||
4596 | * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! | ||
4597 | * trap -- 'echo Ho' SIGWINCH | ||
4598 | * | ||
4599 | * The rules when to forget and when to not forget traps | ||
4600 | * get really complex and nonsensical. | ||
4601 | * | ||
4602 | * Our solution: ONLY bare $(trap) or `trap` is special. | ||
4603 | */ | ||
4604 | /* Save trap handler strings for trap builtin to print */ | ||
4605 | trap_ptr = memcpy(xmalloc(sizeof(trap)), trap, sizeof(trap)); | ||
4606 | /* Fall through into clearing traps */ | ||
4607 | } | ||
4563 | clear_traps(); | 4608 | clear_traps(); |
4564 | #if JOBS | 4609 | #if JOBS |
4565 | /* do job control only in root shell */ | 4610 | /* do job control only in root shell */ |
@@ -4604,8 +4649,14 @@ forkchild(struct job *jp, union node *n, int mode) | |||
4604 | setsignal(SIGQUIT); | 4649 | setsignal(SIGQUIT); |
4605 | } | 4650 | } |
4606 | #if JOBS | 4651 | #if JOBS |
4607 | if (n && n->type == NCMD && strcmp(n->ncmd.args->narg.text, "jobs") == 0) { | 4652 | if (n && n->type == NCMD |
4653 | && strcmp(n->ncmd.args->narg.text, "jobs") == 0 | ||
4654 | ) { | ||
4608 | TRACE(("Job hack\n")); | 4655 | TRACE(("Job hack\n")); |
4656 | /* "jobs": we do not want to clear job list for it, | ||
4657 | * instead we remove only _its_ own_ job from job list. | ||
4658 | * This makes "jobs .... | cat" more useful. | ||
4659 | */ | ||
4609 | freejob(curjob); | 4660 | freejob(curjob); |
4610 | return; | 4661 | return; |
4611 | } | 4662 | } |
@@ -4998,7 +5049,7 @@ struct redirtab { | |||
4998 | struct redirtab *next; | 5049 | struct redirtab *next; |
4999 | int nullredirs; | 5050 | int nullredirs; |
5000 | int pair_count; | 5051 | int pair_count; |
5001 | struct two_fd_t two_fd[0]; | 5052 | struct two_fd_t two_fd[]; |
5002 | }; | 5053 | }; |
5003 | #define redirlist (G_var.redirlist) | 5054 | #define redirlist (G_var.redirlist) |
5004 | 5055 | ||
@@ -5309,7 +5360,7 @@ ash_arith(const char *s) | |||
5309 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ | 5360 | #define EXP_WORD 0x80 /* expand word in parameter expansion */ |
5310 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ | 5361 | #define EXP_QWORD 0x100 /* expand word in quoted parameter expansion */ |
5311 | /* | 5362 | /* |
5312 | * _rmescape() flags | 5363 | * rmescape() flags |
5313 | */ | 5364 | */ |
5314 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ | 5365 | #define RMESCAPE_ALLOC 0x1 /* Allocate a new string */ |
5315 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ | 5366 | #define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */ |
@@ -5363,7 +5414,7 @@ esclen(const char *start, const char *p) | |||
5363 | { | 5414 | { |
5364 | size_t esc = 0; | 5415 | size_t esc = 0; |
5365 | 5416 | ||
5366 | while (p > start && *--p == CTLESC) { | 5417 | while (p > start && (unsigned char)*--p == CTLESC) { |
5367 | esc++; | 5418 | esc++; |
5368 | } | 5419 | } |
5369 | return esc; | 5420 | return esc; |
@@ -5373,19 +5424,19 @@ esclen(const char *start, const char *p) | |||
5373 | * Remove any CTLESC characters from a string. | 5424 | * Remove any CTLESC characters from a string. |
5374 | */ | 5425 | */ |
5375 | static char * | 5426 | static char * |
5376 | _rmescapes(char *str, int flag) | 5427 | rmescapes(char *str, int flag) |
5377 | { | 5428 | { |
5378 | static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; | 5429 | static const char qchars[] ALIGN1 = { CTLESC, CTLQUOTEMARK, '\0' }; |
5379 | 5430 | ||
5380 | char *p, *q, *r; | 5431 | char *p, *q, *r; |
5381 | unsigned inquotes; | 5432 | unsigned inquotes; |
5382 | int notescaped; | 5433 | unsigned protect_against_glob; |
5383 | int globbing; | 5434 | unsigned globbing; |
5384 | 5435 | ||
5385 | p = strpbrk(str, qchars); | 5436 | p = strpbrk(str, qchars); |
5386 | if (!p) { | 5437 | if (!p) |
5387 | return str; | 5438 | return str; |
5388 | } | 5439 | |
5389 | q = p; | 5440 | q = p; |
5390 | r = str; | 5441 | r = str; |
5391 | if (flag & RMESCAPE_ALLOC) { | 5442 | if (flag & RMESCAPE_ALLOC) { |
@@ -5404,28 +5455,33 @@ _rmescapes(char *str, int flag) | |||
5404 | q = (char *)memcpy(q, str, len) + len; | 5455 | q = (char *)memcpy(q, str, len) + len; |
5405 | } | 5456 | } |
5406 | } | 5457 | } |
5458 | |||
5407 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; | 5459 | inquotes = (flag & RMESCAPE_QUOTED) ^ RMESCAPE_QUOTED; |
5408 | globbing = flag & RMESCAPE_GLOB; | 5460 | globbing = flag & RMESCAPE_GLOB; |
5409 | notescaped = globbing; | 5461 | protect_against_glob = globbing; |
5410 | while (*p) { | 5462 | while (*p) { |
5411 | if (*p == CTLQUOTEMARK) { | 5463 | if (*p == CTLQUOTEMARK) { |
5464 | // TODO: if no RMESCAPE_QUOTED in flags, inquotes never becomes 0 | ||
5465 | // (alternates between RMESCAPE_QUOTED and ~RMESCAPE_QUOTED). Is it ok? | ||
5466 | // Note: both inquotes and protect_against_glob only affect whether | ||
5467 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | ||
5412 | inquotes = ~inquotes; | 5468 | inquotes = ~inquotes; |
5413 | p++; | 5469 | p++; |
5414 | notescaped = globbing; | 5470 | protect_against_glob = globbing; |
5415 | continue; | 5471 | continue; |
5416 | } | 5472 | } |
5417 | if (*p == '\\') { | 5473 | if (*p == '\\') { |
5418 | /* naked back slash */ | 5474 | /* naked back slash */ |
5419 | notescaped = 0; | 5475 | protect_against_glob = 0; |
5420 | goto copy; | 5476 | goto copy; |
5421 | } | 5477 | } |
5422 | if (*p == CTLESC) { | 5478 | if (*p == CTLESC) { |
5423 | p++; | 5479 | p++; |
5424 | if (notescaped && inquotes && *p != '/') { | 5480 | if (protect_against_glob && inquotes && *p != '/') { |
5425 | *q++ = '\\'; | 5481 | *q++ = '\\'; |
5426 | } | 5482 | } |
5427 | } | 5483 | } |
5428 | notescaped = globbing; | 5484 | protect_against_glob = globbing; |
5429 | copy: | 5485 | copy: |
5430 | *q++ = *p++; | 5486 | *q++ = *p++; |
5431 | } | 5487 | } |
@@ -5436,8 +5492,6 @@ _rmescapes(char *str, int flag) | |||
5436 | } | 5492 | } |
5437 | return r; | 5493 | return r; |
5438 | } | 5494 | } |
5439 | #define rmescapes(p) _rmescapes((p), 0) | ||
5440 | |||
5441 | #define pmatch(a, b) !fnmatch((a), (b), 0) | 5495 | #define pmatch(a, b) !fnmatch((a), (b), 0) |
5442 | 5496 | ||
5443 | /* | 5497 | /* |
@@ -5452,7 +5506,7 @@ preglob(const char *pattern, int quoted, int flag) | |||
5452 | if (quoted) { | 5506 | if (quoted) { |
5453 | flag |= RMESCAPE_QUOTED; | 5507 | flag |= RMESCAPE_QUOTED; |
5454 | } | 5508 | } |
5455 | return _rmescapes((char *)pattern, flag); | 5509 | return rmescapes((char *)pattern, flag); |
5456 | } | 5510 | } |
5457 | 5511 | ||
5458 | /* | 5512 | /* |
@@ -5463,14 +5517,17 @@ memtodest(const char *p, size_t len, int syntax, int quotes) | |||
5463 | { | 5517 | { |
5464 | char *q = expdest; | 5518 | char *q = expdest; |
5465 | 5519 | ||
5466 | q = makestrspace(len * 2, q); | 5520 | q = makestrspace(quotes ? len * 2 : len, q); |
5467 | 5521 | ||
5468 | while (len--) { | 5522 | while (len--) { |
5469 | int c = signed_char2int(*p++); | 5523 | int c = signed_char2int(*p++); |
5470 | if (!c) | 5524 | if (!c) |
5471 | continue; | 5525 | continue; |
5472 | if (quotes && (SIT(c, syntax) == CCTL || SIT(c, syntax) == CBACK)) | 5526 | if (quotes) { |
5473 | USTPUTC(CTLESC, q); | 5527 | int n = SIT(c, syntax); |
5528 | if (n == CCTL || n == CBACK) | ||
5529 | USTPUTC(CTLESC, q); | ||
5530 | } | ||
5474 | USTPUTC(c, q); | 5531 | USTPUTC(c, q); |
5475 | } | 5532 | } |
5476 | 5533 | ||
@@ -5547,13 +5604,13 @@ removerecordregions(int endoff) | |||
5547 | } | 5604 | } |
5548 | 5605 | ||
5549 | static char * | 5606 | static char * |
5550 | exptilde(char *startp, char *p, int flag) | 5607 | exptilde(char *startp, char *p, int flags) |
5551 | { | 5608 | { |
5552 | char c; | 5609 | char c; |
5553 | char *name; | 5610 | char *name; |
5554 | struct passwd *pw; | 5611 | struct passwd *pw; |
5555 | const char *home; | 5612 | const char *home; |
5556 | int quotes = flag & (EXP_FULL | EXP_CASE); | 5613 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); |
5557 | int startloc; | 5614 | int startloc; |
5558 | 5615 | ||
5559 | name = p + 1; | 5616 | name = p + 1; |
@@ -5565,7 +5622,7 @@ exptilde(char *startp, char *p, int flag) | |||
5565 | case CTLQUOTEMARK: | 5622 | case CTLQUOTEMARK: |
5566 | return startp; | 5623 | return startp; |
5567 | case ':': | 5624 | case ':': |
5568 | if (flag & EXP_VARTILDE) | 5625 | if (flags & EXP_VARTILDE) |
5569 | goto done; | 5626 | goto done; |
5570 | break; | 5627 | break; |
5571 | case '/': | 5628 | case '/': |
@@ -5770,7 +5827,7 @@ expari(int quotes) | |||
5770 | expdest = p; | 5827 | expdest = p; |
5771 | 5828 | ||
5772 | if (quotes) | 5829 | if (quotes) |
5773 | rmescapes(p + 2); | 5830 | rmescapes(p + 2, 0); |
5774 | 5831 | ||
5775 | len = cvtnum(ash_arith(p + 2)); | 5832 | len = cvtnum(ash_arith(p + 2)); |
5776 | 5833 | ||
@@ -5780,7 +5837,7 @@ expari(int quotes) | |||
5780 | #endif | 5837 | #endif |
5781 | 5838 | ||
5782 | /* argstr needs it */ | 5839 | /* argstr needs it */ |
5783 | static char *evalvar(char *p, int flag, struct strlist *var_str_list); | 5840 | static char *evalvar(char *p, int flags, struct strlist *var_str_list); |
5784 | 5841 | ||
5785 | /* | 5842 | /* |
5786 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC | 5843 | * Perform variable and command substitution. If EXP_FULL is set, output CTLESC |
@@ -5792,7 +5849,7 @@ static char *evalvar(char *p, int flag, struct strlist *var_str_list); | |||
5792 | * for correct expansion of "B=$A" word. | 5849 | * for correct expansion of "B=$A" word. |
5793 | */ | 5850 | */ |
5794 | static void | 5851 | static void |
5795 | argstr(char *p, int flag, struct strlist *var_str_list) | 5852 | argstr(char *p, int flags, struct strlist *var_str_list) |
5796 | { | 5853 | { |
5797 | static const char spclchars[] ALIGN1 = { | 5854 | static const char spclchars[] ALIGN1 = { |
5798 | '=', | 5855 | '=', |
@@ -5810,42 +5867,44 @@ argstr(char *p, int flag, struct strlist *var_str_list) | |||
5810 | }; | 5867 | }; |
5811 | const char *reject = spclchars; | 5868 | const char *reject = spclchars; |
5812 | int c; | 5869 | int c; |
5813 | int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ | 5870 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ |
5814 | int breakall = flag & EXP_WORD; | 5871 | int breakall = flags & EXP_WORD; |
5815 | int inquotes; | 5872 | int inquotes; |
5816 | size_t length; | 5873 | size_t length; |
5817 | int startloc; | 5874 | int startloc; |
5818 | 5875 | ||
5819 | if (!(flag & EXP_VARTILDE)) { | 5876 | if (!(flags & EXP_VARTILDE)) { |
5820 | reject += 2; | 5877 | reject += 2; |
5821 | } else if (flag & EXP_VARTILDE2) { | 5878 | } else if (flags & EXP_VARTILDE2) { |
5822 | reject++; | 5879 | reject++; |
5823 | } | 5880 | } |
5824 | inquotes = 0; | 5881 | inquotes = 0; |
5825 | length = 0; | 5882 | length = 0; |
5826 | if (flag & EXP_TILDE) { | 5883 | if (flags & EXP_TILDE) { |
5827 | char *q; | 5884 | char *q; |
5828 | 5885 | ||
5829 | flag &= ~EXP_TILDE; | 5886 | flags &= ~EXP_TILDE; |
5830 | tilde: | 5887 | tilde: |
5831 | q = p; | 5888 | q = p; |
5832 | if (*q == CTLESC && (flag & EXP_QWORD)) | 5889 | if (*q == CTLESC && (flags & EXP_QWORD)) |
5833 | q++; | 5890 | q++; |
5834 | if (*q == '~') | 5891 | if (*q == '~') |
5835 | p = exptilde(p, q, flag); | 5892 | p = exptilde(p, q, flags); |
5836 | } | 5893 | } |
5837 | start: | 5894 | start: |
5838 | startloc = expdest - (char *)stackblock(); | 5895 | startloc = expdest - (char *)stackblock(); |
5839 | for (;;) { | 5896 | for (;;) { |
5840 | length += strcspn(p + length, reject); | 5897 | length += strcspn(p + length, reject); |
5841 | c = p[length]; | 5898 | c = (unsigned char) p[length]; |
5842 | if (c && (!(c & 0x80) | 5899 | if (c) { |
5900 | if (!(c & 0x80) | ||
5843 | #if ENABLE_SH_MATH_SUPPORT | 5901 | #if ENABLE_SH_MATH_SUPPORT |
5844 | || c == CTLENDARI | 5902 | || c == CTLENDARI |
5845 | #endif | 5903 | #endif |
5846 | )) { | 5904 | ) { |
5847 | /* c == '=' || c == ':' || c == CTLENDARI */ | 5905 | /* c == '=' || c == ':' || c == CTLENDARI */ |
5848 | length++; | 5906 | length++; |
5907 | } | ||
5849 | } | 5908 | } |
5850 | if (length > 0) { | 5909 | if (length > 0) { |
5851 | int newloc; | 5910 | int newloc; |
@@ -5863,11 +5922,11 @@ argstr(char *p, int flag, struct strlist *var_str_list) | |||
5863 | case '\0': | 5922 | case '\0': |
5864 | goto breakloop; | 5923 | goto breakloop; |
5865 | case '=': | 5924 | case '=': |
5866 | if (flag & EXP_VARTILDE2) { | 5925 | if (flags & EXP_VARTILDE2) { |
5867 | p--; | 5926 | p--; |
5868 | continue; | 5927 | continue; |
5869 | } | 5928 | } |
5870 | flag |= EXP_VARTILDE2; | 5929 | flags |= EXP_VARTILDE2; |
5871 | reject++; | 5930 | reject++; |
5872 | /* fall through */ | 5931 | /* fall through */ |
5873 | case ':': | 5932 | case ':': |
@@ -5886,15 +5945,13 @@ argstr(char *p, int flag, struct strlist *var_str_list) | |||
5886 | goto breakloop; | 5945 | goto breakloop; |
5887 | case CTLQUOTEMARK: | 5946 | case CTLQUOTEMARK: |
5888 | /* "$@" syntax adherence hack */ | 5947 | /* "$@" syntax adherence hack */ |
5889 | if ( | 5948 | if (!inquotes |
5890 | !inquotes && | 5949 | && memcmp(p, dolatstr, 4) == 0 |
5891 | !memcmp(p, dolatstr, 4) && | 5950 | && ( p[4] == CTLQUOTEMARK |
5892 | (p[4] == CTLQUOTEMARK || ( | 5951 | || (p[4] == CTLENDVAR && p[5] == CTLQUOTEMARK) |
5893 | p[4] == CTLENDVAR && | 5952 | ) |
5894 | p[5] == CTLQUOTEMARK | ||
5895 | )) | ||
5896 | ) { | 5953 | ) { |
5897 | p = evalvar(p + 1, flag, /* var_str_list: */ NULL) + 1; | 5954 | p = evalvar(p + 1, flags, /* var_str_list: */ NULL) + 1; |
5898 | goto start; | 5955 | goto start; |
5899 | } | 5956 | } |
5900 | inquotes = !inquotes; | 5957 | inquotes = !inquotes; |
@@ -5910,10 +5967,10 @@ argstr(char *p, int flag, struct strlist *var_str_list) | |||
5910 | length++; | 5967 | length++; |
5911 | goto addquote; | 5968 | goto addquote; |
5912 | case CTLVAR: | 5969 | case CTLVAR: |
5913 | p = evalvar(p, flag, var_str_list); | 5970 | p = evalvar(p, flags, var_str_list); |
5914 | goto start; | 5971 | goto start; |
5915 | case CTLBACKQ: | 5972 | case CTLBACKQ: |
5916 | c = 0; | 5973 | c = '\0'; |
5917 | case CTLBACKQ|CTLQUOTE: | 5974 | case CTLBACKQ|CTLQUOTE: |
5918 | expbackq(argbackq->n, c, quotes); | 5975 | expbackq(argbackq->n, c, quotes); |
5919 | argbackq = argbackq->next; | 5976 | argbackq = argbackq->next; |
@@ -6119,15 +6176,15 @@ subevalvar(char *p, char *str, int strloc, int subtype, | |||
6119 | #if ENABLE_ASH_BASH_COMPAT | 6176 | #if ENABLE_ASH_BASH_COMPAT |
6120 | case VSSUBSTR: | 6177 | case VSSUBSTR: |
6121 | loc = str = stackblock() + strloc; | 6178 | loc = str = stackblock() + strloc; |
6122 | // TODO: number() instead? It does error checking... | 6179 | /* Read POS in ${var:POS:LEN} */ |
6123 | pos = atoi(loc); | 6180 | pos = atoi(loc); /* number(loc) errors out on "1:4" */ |
6124 | len = str - startp - 1; | 6181 | len = str - startp - 1; |
6125 | 6182 | ||
6126 | /* *loc != '\0', guaranteed by parser */ | 6183 | /* *loc != '\0', guaranteed by parser */ |
6127 | if (quotes) { | 6184 | if (quotes) { |
6128 | char *ptr; | 6185 | char *ptr; |
6129 | 6186 | ||
6130 | /* We must adjust the length by the number of escapes we find. */ | 6187 | /* Adjust the length by the number of escapes */ |
6131 | for (ptr = startp; ptr < (str - 1); ptr++) { | 6188 | for (ptr = startp; ptr < (str - 1); ptr++) { |
6132 | if (*ptr == CTLESC) { | 6189 | if (*ptr == CTLESC) { |
6133 | len--; | 6190 | len--; |
@@ -6138,15 +6195,22 @@ subevalvar(char *p, char *str, int strloc, int subtype, | |||
6138 | orig_len = len; | 6195 | orig_len = len; |
6139 | 6196 | ||
6140 | if (*loc++ == ':') { | 6197 | if (*loc++ == ':') { |
6141 | // TODO: number() instead? It does error checking... | 6198 | /* ${var::LEN} */ |
6142 | len = atoi(loc); | 6199 | len = number(loc); |
6143 | } else { | 6200 | } else { |
6201 | /* Skip POS in ${var:POS:LEN} */ | ||
6144 | len = orig_len; | 6202 | len = orig_len; |
6145 | while (*loc && *loc != ':') | 6203 | while (*loc && *loc != ':') { |
6204 | /* TODO? | ||
6205 | * bash complains on: var=qwe; echo ${var:1a:123} | ||
6206 | if (!isdigit(*loc)) | ||
6207 | ash_msg_and_raise_error(msg_illnum, str); | ||
6208 | */ | ||
6146 | loc++; | 6209 | loc++; |
6147 | if (*loc++ == ':') | 6210 | } |
6148 | // TODO: number() instead? It does error checking... | 6211 | if (*loc++ == ':') { |
6149 | len = atoi(loc); | 6212 | len = number(loc); |
6213 | } | ||
6150 | } | 6214 | } |
6151 | if (pos >= orig_len) { | 6215 | if (pos >= orig_len) { |
6152 | pos = 0; | 6216 | pos = 0; |
@@ -6190,7 +6254,7 @@ subevalvar(char *p, char *str, int strloc, int subtype, | |||
6190 | rmesc = startp; | 6254 | rmesc = startp; |
6191 | rmescend = (char *)stackblock() + strloc; | 6255 | rmescend = (char *)stackblock() + strloc; |
6192 | if (quotes) { | 6256 | if (quotes) { |
6193 | rmesc = _rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); | 6257 | rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW); |
6194 | if (rmesc != startp) { | 6258 | if (rmesc != startp) { |
6195 | rmescend = expdest; | 6259 | rmescend = expdest; |
6196 | startp = (char *)stackblock() + startloc; | 6260 | startp = (char *)stackblock() + startloc; |
@@ -6321,7 +6385,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6321 | int syntax; | 6385 | int syntax; |
6322 | int quoted = varflags & VSQUOTE; | 6386 | int quoted = varflags & VSQUOTE; |
6323 | int subtype = varflags & VSTYPE; | 6387 | int subtype = varflags & VSTYPE; |
6324 | int quotes = flags & (EXP_FULL | EXP_CASE); | 6388 | int quotes = flags & (EXP_FULL | EXP_CASE | EXP_REDIR); |
6325 | 6389 | ||
6326 | if (quoted && (flags & EXP_FULL)) | 6390 | if (quoted && (flags & EXP_FULL)) |
6327 | sep = 1 << CHAR_BIT; | 6391 | sep = 1 << CHAR_BIT; |
@@ -6365,7 +6429,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6365 | ap = shellparam.p; | 6429 | ap = shellparam.p; |
6366 | if (!ap) | 6430 | if (!ap) |
6367 | return -1; | 6431 | return -1; |
6368 | while ((p = *ap++)) { | 6432 | while ((p = *ap++) != NULL) { |
6369 | size_t partlen; | 6433 | size_t partlen; |
6370 | 6434 | ||
6371 | partlen = strlen(p); | 6435 | partlen = strlen(p); |
@@ -6399,8 +6463,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6399 | case '7': | 6463 | case '7': |
6400 | case '8': | 6464 | case '8': |
6401 | case '9': | 6465 | case '9': |
6402 | // TODO: number() instead? It does error checking... | 6466 | num = atoi(name); /* number(name) fails on ${N#str} etc */ |
6403 | num = atoi(name); | ||
6404 | if (num < 0 || num > shellparam.nparam) | 6467 | if (num < 0 || num > shellparam.nparam) |
6405 | return -1; | 6468 | return -1; |
6406 | p = num ? shellparam.p[num - 1] : arg0; | 6469 | p = num ? shellparam.p[num - 1] : arg0; |
@@ -6452,7 +6515,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6452 | * input string. | 6515 | * input string. |
6453 | */ | 6516 | */ |
6454 | static char * | 6517 | static char * |
6455 | evalvar(char *p, int flag, struct strlist *var_str_list) | 6518 | evalvar(char *p, int flags, struct strlist *var_str_list) |
6456 | { | 6519 | { |
6457 | char varflags; | 6520 | char varflags; |
6458 | char subtype; | 6521 | char subtype; |
@@ -6463,7 +6526,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
6463 | int startloc; | 6526 | int startloc; |
6464 | ssize_t varlen; | 6527 | ssize_t varlen; |
6465 | 6528 | ||
6466 | varflags = *p++; | 6529 | varflags = (unsigned char) *p++; |
6467 | subtype = varflags & VSTYPE; | 6530 | subtype = varflags & VSTYPE; |
6468 | quoted = varflags & VSQUOTE; | 6531 | quoted = varflags & VSQUOTE; |
6469 | var = p; | 6532 | var = p; |
@@ -6472,7 +6535,7 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
6472 | p = strchr(p, '=') + 1; | 6535 | p = strchr(p, '=') + 1; |
6473 | 6536 | ||
6474 | again: | 6537 | again: |
6475 | varlen = varvalue(var, varflags, flag, var_str_list); | 6538 | varlen = varvalue(var, varflags, flags, var_str_list); |
6476 | if (varflags & VSNUL) | 6539 | if (varflags & VSNUL) |
6477 | varlen--; | 6540 | varlen--; |
6478 | 6541 | ||
@@ -6485,8 +6548,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
6485 | vsplus: | 6548 | vsplus: |
6486 | if (varlen < 0) { | 6549 | if (varlen < 0) { |
6487 | argstr( | 6550 | argstr( |
6488 | p, flag | EXP_TILDE | | 6551 | p, flags | EXP_TILDE | |
6489 | (quoted ? EXP_QWORD : EXP_WORD), | 6552 | (quoted ? EXP_QWORD : EXP_WORD), |
6490 | var_str_list | 6553 | var_str_list |
6491 | ); | 6554 | ); |
6492 | goto end; | 6555 | goto end; |
@@ -6558,7 +6621,8 @@ evalvar(char *p, int flag, struct strlist *var_str_list) | |||
6558 | patloc = expdest - (char *)stackblock(); | 6621 | patloc = expdest - (char *)stackblock(); |
6559 | if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, | 6622 | if (0 == subevalvar(p, /* str: */ NULL, patloc, subtype, |
6560 | startloc, varflags, | 6623 | startloc, varflags, |
6561 | /* quotes: */ flag & (EXP_FULL | EXP_CASE), | 6624 | //TODO: | EXP_REDIR too? All other such places do it too |
6625 | /* quotes: */ flags & (EXP_FULL | EXP_CASE), | ||
6562 | var_str_list) | 6626 | var_str_list) |
6563 | ) { | 6627 | ) { |
6564 | int amount = expdest - ( | 6628 | int amount = expdest - ( |
@@ -6812,7 +6876,7 @@ expmeta(char *enddir, char *name) | |||
6812 | p++; | 6876 | p++; |
6813 | if (*p == '.') | 6877 | if (*p == '.') |
6814 | matchdot++; | 6878 | matchdot++; |
6815 | while (!intpending && (dp = readdir(dirp)) != NULL) { | 6879 | while (!pending_int && (dp = readdir(dirp)) != NULL) { |
6816 | if (dp->d_name[0] == '.' && !matchdot) | 6880 | if (dp->d_name[0] == '.' && !matchdot) |
6817 | continue; | 6881 | continue; |
6818 | if (pmatch(start, dp->d_name)) { | 6882 | if (pmatch(start, dp->d_name)) { |
@@ -6933,7 +6997,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
6933 | */ | 6997 | */ |
6934 | nometa: | 6998 | nometa: |
6935 | *exparg.lastp = str; | 6999 | *exparg.lastp = str; |
6936 | rmescapes(str->text); | 7000 | rmescapes(str->text, 0); |
6937 | exparg.lastp = &str->next; | 7001 | exparg.lastp = &str->next; |
6938 | } else { | 7002 | } else { |
6939 | *exparg.lastp = NULL; | 7003 | *exparg.lastp = NULL; |
@@ -6981,7 +7045,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) | |||
6981 | expandmeta(exparg.list /*, flag*/); | 7045 | expandmeta(exparg.list /*, flag*/); |
6982 | } else { | 7046 | } else { |
6983 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ | 7047 | if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ |
6984 | rmescapes(p); | 7048 | rmescapes(p, 0); |
6985 | sp = stzalloc(sizeof(*sp)); | 7049 | sp = stzalloc(sizeof(*sp)); |
6986 | sp->text = p; | 7050 | sp->text = p; |
6987 | *exparg.lastp = sp; | 7051 | *exparg.lastp = sp; |
@@ -7206,8 +7270,8 @@ shellexec(char **argv, const char *path, int idx) | |||
7206 | break; | 7270 | break; |
7207 | } | 7271 | } |
7208 | exitstatus = exerrno; | 7272 | exitstatus = exerrno; |
7209 | TRACE(("shellexec failed for %s, errno %d, suppressint %d\n", | 7273 | TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n", |
7210 | argv[0], e, suppressint)); | 7274 | argv[0], e, suppress_int)); |
7211 | ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); | 7275 | ash_msg_and_raise(EXEXEC, "%s: %s", argv[0], errmsg(e, "not found")); |
7212 | /* NOTREACHED */ | 7276 | /* NOTREACHED */ |
7213 | } | 7277 | } |
@@ -8009,7 +8073,7 @@ dotrap(void) | |||
8009 | uint8_t savestatus; | 8073 | uint8_t savestatus; |
8010 | 8074 | ||
8011 | savestatus = exitstatus; | 8075 | savestatus = exitstatus; |
8012 | pendingsig = 0; | 8076 | pending_sig = 0; |
8013 | xbarrier(); | 8077 | xbarrier(); |
8014 | 8078 | ||
8015 | TRACE(("dotrap entered\n")); | 8079 | TRACE(("dotrap entered\n")); |
@@ -8189,7 +8253,7 @@ evaltree(union node *n, int flags) | |||
8189 | out1: | 8253 | out1: |
8190 | if (checkexit & exitstatus) | 8254 | if (checkexit & exitstatus) |
8191 | evalskip |= SKIPEVAL; | 8255 | evalskip |= SKIPEVAL; |
8192 | else if (pendingsig && dotrap()) | 8256 | else if (pending_sig && dotrap()) |
8193 | goto exexit; | 8257 | goto exexit; |
8194 | 8258 | ||
8195 | if (flags & EV_EXIT) { | 8259 | if (flags & EV_EXIT) { |
@@ -9109,7 +9173,7 @@ evalcommand(union node *cmd, int flags) | |||
9109 | if (i == EXINT) | 9173 | if (i == EXINT) |
9110 | exit_status = 128 + SIGINT; | 9174 | exit_status = 128 + SIGINT; |
9111 | if (i == EXSIG) | 9175 | if (i == EXSIG) |
9112 | exit_status = 128 + pendingsig; | 9176 | exit_status = 128 + pending_sig; |
9113 | exitstatus = exit_status; | 9177 | exitstatus = exit_status; |
9114 | if (i == EXINT || spclbltin > 0) { | 9178 | if (i == EXINT || spclbltin > 0) { |
9115 | raise: | 9179 | raise: |
@@ -9163,7 +9227,6 @@ evalbltin(const struct builtincmd *cmd, int argc, char **argv) | |||
9163 | exitstatus |= ferror(stdout); | 9227 | exitstatus |= ferror(stdout); |
9164 | clearerr(stdout); | 9228 | clearerr(stdout); |
9165 | commandname = savecmdname; | 9229 | commandname = savecmdname; |
9166 | // exsig = 0; | ||
9167 | exception_handler = savehandler; | 9230 | exception_handler = savehandler; |
9168 | 9231 | ||
9169 | return i; | 9232 | return i; |
@@ -9214,7 +9277,7 @@ breakcmd(int argc UNUSED_PARAM, char **argv) | |||
9214 | int n = argv[1] ? number(argv[1]) : 1; | 9277 | int n = argv[1] ? number(argv[1]) : 1; |
9215 | 9278 | ||
9216 | if (n <= 0) | 9279 | if (n <= 0) |
9217 | ash_msg_and_raise_error(illnum, argv[1]); | 9280 | ash_msg_and_raise_error(msg_illnum, argv[1]); |
9218 | if (n > loopnest) | 9281 | if (n > loopnest) |
9219 | n = loopnest; | 9282 | n = loopnest; |
9220 | if (n > 0) { | 9283 | if (n > 0) { |
@@ -10022,7 +10085,7 @@ change_random(const char *value) | |||
10022 | vrandom.flags &= ~VNOFUNC; | 10085 | vrandom.flags &= ~VNOFUNC; |
10023 | } else { | 10086 | } else { |
10024 | /* set/reset */ | 10087 | /* set/reset */ |
10025 | random_galois_LFSR = random_LCG = strtoul(value, (char **)NULL, 10); | 10088 | random_galois_LFSR = random_LCG = strtoul(value, NULL, 10); |
10026 | } | 10089 | } |
10027 | } | 10090 | } |
10028 | #endif | 10091 | #endif |
@@ -10406,7 +10469,7 @@ parsefname(void) | |||
10406 | TRACE(("Here document %d\n", n->type)); | 10469 | TRACE(("Here document %d\n", n->type)); |
10407 | if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) | 10470 | if (!noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) |
10408 | raise_error_syntax("illegal eof marker for << redirection"); | 10471 | raise_error_syntax("illegal eof marker for << redirection"); |
10409 | rmescapes(wordtext); | 10472 | rmescapes(wordtext, 0); |
10410 | here->eofmark = wordtext; | 10473 | here->eofmark = wordtext; |
10411 | here->next = NULL; | 10474 | here->next = NULL; |
10412 | if (heredoclist == NULL) | 10475 | if (heredoclist == NULL) |
@@ -12202,14 +12265,30 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
12202 | ap = argptr; | 12265 | ap = argptr; |
12203 | if (!*ap) { | 12266 | if (!*ap) { |
12204 | for (signo = 0; signo < NSIG; signo++) { | 12267 | for (signo = 0; signo < NSIG; signo++) { |
12205 | if (trap[signo] != NULL) { | 12268 | char *tr = trap_ptr[signo]; |
12269 | if (tr) { | ||
12270 | /* note: bash adds "SIG", but only if invoked | ||
12271 | * as "bash". If called as "sh", or if set -o posix, | ||
12272 | * then it prints short signal names. | ||
12273 | * We are printing short names: */ | ||
12206 | out1fmt("trap -- %s %s\n", | 12274 | out1fmt("trap -- %s %s\n", |
12207 | single_quote(trap[signo]), | 12275 | single_quote(tr), |
12208 | get_signame(signo)); | 12276 | get_signame(signo)); |
12277 | /* trap_ptr != trap only if we are in special-cased `trap` code. | ||
12278 | * In this case, we will exit very soon, no need to free(). */ | ||
12279 | /* if (trap_ptr != trap && tp[0]) */ | ||
12280 | /* free(tr); */ | ||
12209 | } | 12281 | } |
12210 | } | 12282 | } |
12283 | /* | ||
12284 | if (trap_ptr != trap) { | ||
12285 | free(trap_ptr); | ||
12286 | trap_ptr = trap; | ||
12287 | } | ||
12288 | */ | ||
12211 | return 0; | 12289 | return 0; |
12212 | } | 12290 | } |
12291 | |||
12213 | action = NULL; | 12292 | action = NULL; |
12214 | if (ap[1]) | 12293 | if (ap[1]) |
12215 | action = *ap++; | 12294 | action = *ap++; |
@@ -12705,7 +12784,7 @@ umaskcmd(int argc UNUSED_PARAM, char **argv) | |||
12705 | mask = 0; | 12784 | mask = 0; |
12706 | do { | 12785 | do { |
12707 | if (*ap >= '8' || *ap < '0') | 12786 | if (*ap >= '8' || *ap < '0') |
12708 | ash_msg_and_raise_error(illnum, argv[1]); | 12787 | ash_msg_and_raise_error(msg_illnum, argv[1]); |
12709 | mask = (mask << 3) + (*ap - '0'); | 12788 | mask = (mask << 3) + (*ap - '0'); |
12710 | } while (*++ap != '\0'); | 12789 | } while (*++ap != '\0'); |
12711 | umask(mask); | 12790 | umask(mask); |
@@ -12966,6 +13045,7 @@ exitshell(void) | |||
12966 | if (p) { | 13045 | if (p) { |
12967 | trap[0] = NULL; | 13046 | trap[0] = NULL; |
12968 | evalstring(p, 0); | 13047 | evalstring(p, 0); |
13048 | free(p); | ||
12969 | } | 13049 | } |
12970 | flush_stdout_stderr(); | 13050 | flush_stdout_stderr(); |
12971 | out: | 13051 | out: |
@@ -12986,7 +13066,7 @@ init(void) | |||
12986 | /* from var.c: */ | 13066 | /* from var.c: */ |
12987 | { | 13067 | { |
12988 | char **envp; | 13068 | char **envp; |
12989 | char ppid[sizeof(int)*3 + 1]; | 13069 | char ppid[sizeof(int)*3 + 2]; |
12990 | const char *p; | 13070 | const char *p; |
12991 | struct stat st1, st2; | 13071 | struct stat st1, st2; |
12992 | 13072 | ||
@@ -12997,7 +13077,7 @@ init(void) | |||
12997 | } | 13077 | } |
12998 | } | 13078 | } |
12999 | 13079 | ||
13000 | snprintf(ppid, sizeof(ppid), "%u", (unsigned) getppid()); | 13080 | sprintf(ppid, "%u", (unsigned) getppid()); |
13001 | setvar("PPID", ppid, 0); | 13081 | setvar("PPID", ppid, 0); |
13002 | 13082 | ||
13003 | p = lookupvar("PWD"); | 13083 | p = lookupvar("PWD"); |
@@ -13237,7 +13317,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13237 | } | 13317 | } |
13238 | 13318 | ||
13239 | if (sflag || minusc == NULL) { | 13319 | if (sflag || minusc == NULL) { |
13240 | #if ENABLE_FEATURE_EDITING_SAVEHISTORY | 13320 | #if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY |
13241 | if (iflag) { | 13321 | if (iflag) { |
13242 | const char *hp = lookupvar("HISTFILE"); | 13322 | const char *hp = lookupvar("HISTFILE"); |
13243 | if (hp) | 13323 | if (hp) |
diff --git a/shell/ash_test/ash-redir/redir7.right b/shell/ash_test/ash-redir/redir7.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/ash_test/ash-redir/redir7.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Done | ||
diff --git a/shell/ash_test/ash-redir/redir7.tests b/shell/ash_test/ash-redir/redir7.tests new file mode 100755 index 000000000..17d1040e0 --- /dev/null +++ b/shell/ash_test/ash-redir/redir7.tests | |||
@@ -0,0 +1,12 @@ | |||
1 | # Chars above 0x7f are used as special codes. | ||
2 | # 0x81 is CTLESC (see ash.c). | ||
3 | # The bug was that quoting and unquoting of them | ||
4 | # was out of sync for redirect filenames. | ||
5 | |||
6 | >unicode.sh | ||
7 | echo -e 'echo Ok >uni\x81code' >>unicode.sh | ||
8 | echo -e 'cat uni\x81code' >>unicode.sh | ||
9 | echo -e 'cat uni?code' >>unicode.sh | ||
10 | . unicode.sh | ||
11 | rm uni*code* | ||
12 | echo Done | ||
diff --git a/shell/ash_test/ash-redir/redir8.right b/shell/ash_test/ash-redir/redir8.right new file mode 100644 index 000000000..6430b0211 --- /dev/null +++ b/shell/ash_test/ash-redir/redir8.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Done | ||
diff --git a/shell/ash_test/ash-redir/redir8.tests b/shell/ash_test/ash-redir/redir8.tests new file mode 100755 index 000000000..32ab607b8 --- /dev/null +++ b/shell/ash_test/ash-redir/redir8.tests | |||
@@ -0,0 +1,15 @@ | |||
1 | # Chars above 0x7f are used as special codes. | ||
2 | # 0x81 is CTLESC (see ash.c). | ||
3 | # The bug was that quoting and unquoting of them | ||
4 | # was out of sync for redirect filenames. | ||
5 | |||
6 | # Subcase when redirect filename is specified in a variable. | ||
7 | |||
8 | >unicode.sh | ||
9 | echo -e 'v=uni\x81code' >>unicode.sh | ||
10 | echo -e 'echo Ok >"$v"' >>unicode.sh | ||
11 | echo -e 'cat uni\x81code' >>unicode.sh | ||
12 | echo -e 'cat uni?code' >>unicode.sh | ||
13 | . unicode.sh | ||
14 | rm uni*code* | ||
15 | echo Done | ||
diff --git a/shell/ash_test/ash-signals/savetrap.right b/shell/ash_test/ash-signals/savetrap.right new file mode 100644 index 000000000..a59225be3 --- /dev/null +++ b/shell/ash_test/ash-signals/savetrap.right | |||
@@ -0,0 +1,8 @@ | |||
1 | trap -- 'echo Exiting' EXIT | ||
2 | trap -- 'echo WINCH!' WINCH | ||
3 | trap -- 'echo Exiting' EXIT | ||
4 | trap -- 'echo WINCH!' WINCH | ||
5 | trap -- 'echo Exiting' EXIT | ||
6 | trap -- 'echo WINCH!' WINCH | ||
7 | Done | ||
8 | Exiting | ||
diff --git a/shell/ash_test/ash-signals/savetrap.tests b/shell/ash_test/ash-signals/savetrap.tests new file mode 100755 index 000000000..c2b312fb8 --- /dev/null +++ b/shell/ash_test/ash-signals/savetrap.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | trap 'echo Exiting' EXIT | ||
2 | trap 'echo WINCH!' SIGWINCH | ||
3 | v=` trap ` | ||
4 | echo "$v" | ||
5 | v=$( trap ) | ||
6 | echo "$v" | ||
7 | v=`trap` | ||
8 | echo "$v" | ||
9 | echo Done | ||
diff --git a/shell/hush.c b/shell/hush.c index 5794b1ddf..b515eabd2 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -78,6 +78,7 @@ | |||
78 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. | 78 | * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
79 | */ | 79 | */ |
80 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 80 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
81 | #include <malloc.h> /* for malloc_trim */ | ||
81 | #include <glob.h> | 82 | #include <glob.h> |
82 | /* #include <dmalloc.h> */ | 83 | /* #include <dmalloc.h> */ |
83 | #if ENABLE_HUSH_CASE | 84 | #if ENABLE_HUSH_CASE |
@@ -786,7 +787,7 @@ static void xxfree(void *ptr) | |||
786 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. | 787 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. |
787 | */ | 788 | */ |
788 | #if HUSH_DEBUG < 2 | 789 | #if HUSH_DEBUG < 2 |
789 | # define die_if_script(lineno, fmt...) die_if_script(fmt) | 790 | # define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) |
790 | # define syntax_error(lineno, msg) syntax_error(msg) | 791 | # define syntax_error(lineno, msg) syntax_error(msg) |
791 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) | 792 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) |
792 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) | 793 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) |
@@ -855,7 +856,7 @@ static void syntax_error_unexpected_ch(unsigned lineno, int ch) | |||
855 | # undef syntax_error_unterm_str | 856 | # undef syntax_error_unterm_str |
856 | # undef syntax_error_unexpected_ch | 857 | # undef syntax_error_unexpected_ch |
857 | #else | 858 | #else |
858 | # define die_if_script(fmt...) die_if_script(__LINE__, fmt) | 859 | # define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) |
859 | # define syntax_error(msg) syntax_error(__LINE__, msg) | 860 | # define syntax_error(msg) syntax_error(__LINE__, msg) |
860 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) | 861 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) |
861 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) | 862 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) |
@@ -898,7 +899,7 @@ static int is_well_formed_var_name(const char *s, char terminator) | |||
898 | /* Replace each \x with x in place, return ptr past NUL. */ | 899 | /* Replace each \x with x in place, return ptr past NUL. */ |
899 | static char *unbackslash(char *src) | 900 | static char *unbackslash(char *src) |
900 | { | 901 | { |
901 | char *dst = src; | 902 | char *dst = src = strchrnul(src, '\\'); |
902 | while (1) { | 903 | while (1) { |
903 | if (*src == '\\') | 904 | if (*src == '\\') |
904 | src++; | 905 | src++; |
@@ -1037,7 +1038,7 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1037 | * is finished or backgrounded. It is the same in interactive and | 1038 | * is finished or backgrounded. It is the same in interactive and |
1038 | * non-interactive shells, and is the same regardless of whether | 1039 | * non-interactive shells, and is the same regardless of whether |
1039 | * a user trap handler is installed or a shell special one is in effect. | 1040 | * a user trap handler is installed or a shell special one is in effect. |
1040 | * ^C or ^Z from keyboard seem to execute "at once" because it usually | 1041 | * ^C or ^Z from keyboard seems to execute "at once" because it usually |
1041 | * backgrounds (i.e. stops) or kills all members of currently running | 1042 | * backgrounds (i.e. stops) or kills all members of currently running |
1042 | * pipe. | 1043 | * pipe. |
1043 | * | 1044 | * |
@@ -1104,12 +1105,17 @@ static void restore_G_args(save_arg_t *sv, char **argv) | |||
1104 | * (child shell is not interactive), | 1105 | * (child shell is not interactive), |
1105 | * unset all traps (note: regardless of child shell's type - {}, (), etc) | 1106 | * unset all traps (note: regardless of child shell's type - {}, (), etc) |
1106 | * after [v]fork, if we plan to exec: | 1107 | * after [v]fork, if we plan to exec: |
1107 | * POSIX says pending signal mask is cleared in child - no need to clear it. | 1108 | * POSIX says fork clears pending signal mask in child - no need to clear it. |
1108 | * Restore blocked signal set to one inherited by shell just prior to exec. | 1109 | * Restore blocked signal set to one inherited by shell just prior to exec. |
1109 | * | 1110 | * |
1110 | * Note: as a result, we do not use signal handlers much. The only uses | 1111 | * Note: as a result, we do not use signal handlers much. The only uses |
1111 | * are to count SIGCHLDs | 1112 | * are to count SIGCHLDs |
1112 | * and to restore tty pgrp on signal-induced exit. | 1113 | * and to restore tty pgrp on signal-induced exit. |
1114 | * | ||
1115 | * Note 2 (compat): | ||
1116 | * Standard says "When a subshell is entered, traps that are not being ignored | ||
1117 | * are set to the default actions". bash interprets it so that traps which | ||
1118 | * are set to "" (ignore) are NOT reset to defaults. We do the same. | ||
1113 | */ | 1119 | */ |
1114 | enum { | 1120 | enum { |
1115 | SPECIAL_INTERACTIVE_SIGS = 0 | 1121 | SPECIAL_INTERACTIVE_SIGS = 0 |
@@ -2596,43 +2602,51 @@ static void reset_traps_to_defaults(void) | |||
2596 | { | 2602 | { |
2597 | /* This function is always called in a child shell | 2603 | /* This function is always called in a child shell |
2598 | * after fork (not vfork, NOMMU doesn't use this function). | 2604 | * after fork (not vfork, NOMMU doesn't use this function). |
2599 | * Child shells are not interactive. | ||
2600 | * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. | ||
2601 | * Testcase: (while :; do :; done) + ^Z should background. | ||
2602 | * Same goes for SIGTERM, SIGHUP, SIGINT. | ||
2603 | */ | 2605 | */ |
2604 | unsigned sig; | 2606 | unsigned sig; |
2605 | unsigned mask; | 2607 | unsigned mask; |
2606 | 2608 | ||
2609 | /* Child shells are not interactive. | ||
2610 | * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling. | ||
2611 | * Testcase: (while :; do :; done) + ^Z should background. | ||
2612 | * Same goes for SIGTERM, SIGHUP, SIGINT. | ||
2613 | */ | ||
2607 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) | 2614 | if (!G.traps && !(G.non_DFL_mask & SPECIAL_INTERACTIVE_SIGS)) |
2608 | return; | 2615 | return; /* already no traps and no SPECIAL_INTERACTIVE_SIGS */ |
2609 | 2616 | ||
2610 | /* Stupid. It can be done with *single* &= op, but we can't use | 2617 | /* Switching off SPECIAL_INTERACTIVE_SIGS. |
2611 | * the fact that G.blocked_set is implemented as a bitmask... */ | 2618 | * Stupid. It can be done with *single* &= op, but we can't use |
2619 | * the fact that G.blocked_set is implemented as a bitmask | ||
2620 | * in libc... */ | ||
2612 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); | 2621 | mask = (SPECIAL_INTERACTIVE_SIGS >> 1); |
2613 | sig = 1; | 2622 | sig = 1; |
2614 | while (1) { | 2623 | while (1) { |
2615 | if (mask & 1) | 2624 | if (mask & 1) { |
2616 | sigdelset(&G.blocked_set, sig); | 2625 | /* Careful. Only if no trap or trap is not "" */ |
2626 | if (!G.traps || !G.traps[sig] || G.traps[sig][0]) | ||
2627 | sigdelset(&G.blocked_set, sig); | ||
2628 | } | ||
2617 | mask >>= 1; | 2629 | mask >>= 1; |
2618 | if (!mask) | 2630 | if (!mask) |
2619 | break; | 2631 | break; |
2620 | sig++; | 2632 | sig++; |
2621 | } | 2633 | } |
2622 | 2634 | /* Our homegrown sig mask is saner to work with :) */ | |
2623 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; | 2635 | G.non_DFL_mask &= ~SPECIAL_INTERACTIVE_SIGS; |
2636 | |||
2637 | /* Resetting all traps to default except empty ones */ | ||
2624 | mask = G.non_DFL_mask; | 2638 | mask = G.non_DFL_mask; |
2625 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { | 2639 | if (G.traps) for (sig = 0; sig < NSIG; sig++, mask >>= 1) { |
2626 | if (!G.traps[sig]) | 2640 | if (!G.traps[sig] || !G.traps[sig][0]) |
2627 | continue; | 2641 | continue; |
2628 | free(G.traps[sig]); | 2642 | free(G.traps[sig]); |
2629 | G.traps[sig] = NULL; | 2643 | G.traps[sig] = NULL; |
2630 | /* There is no signal for 0 (EXIT) */ | 2644 | /* There is no signal for 0 (EXIT) */ |
2631 | if (sig == 0) | 2645 | if (sig == 0) |
2632 | continue; | 2646 | continue; |
2633 | /* There was a trap handler, we are removing it. | 2647 | /* There was a trap handler, we just removed it. |
2634 | * But if sig still has non-DFL handling, | 2648 | * But if sig still has non-DFL handling, |
2635 | * we should not unblock it. */ | 2649 | * we should not unblock the sig. */ |
2636 | if (mask & 1) | 2650 | if (mask & 1) |
2637 | continue; | 2651 | continue; |
2638 | sigdelset(&G.blocked_set, sig); | 2652 | sigdelset(&G.blocked_set, sig); |
@@ -3079,15 +3093,21 @@ static const struct built_in_command* find_builtin(const char *name) | |||
3079 | } | 3093 | } |
3080 | 3094 | ||
3081 | #if ENABLE_HUSH_FUNCTIONS | 3095 | #if ENABLE_HUSH_FUNCTIONS |
3082 | static const struct function *find_function(const char *name) | 3096 | static struct function **find_function_slot(const char *name) |
3083 | { | 3097 | { |
3084 | const struct function *funcp = G.top_func; | 3098 | struct function **funcpp = &G.top_func; |
3085 | while (funcp) { | 3099 | while (*funcpp) { |
3086 | if (strcmp(name, funcp->name) == 0) { | 3100 | if (strcmp(name, (*funcpp)->name) == 0) { |
3087 | break; | 3101 | break; |
3088 | } | 3102 | } |
3089 | funcp = funcp->next; | 3103 | funcpp = &(*funcpp)->next; |
3090 | } | 3104 | } |
3105 | return funcpp; | ||
3106 | } | ||
3107 | |||
3108 | static const struct function *find_function(const char *name) | ||
3109 | { | ||
3110 | const struct function *funcp = *find_function_slot(name); | ||
3091 | if (funcp) | 3111 | if (funcp) |
3092 | debug_printf_exec("found function '%s'\n", name); | 3112 | debug_printf_exec("found function '%s'\n", name); |
3093 | return funcp; | 3113 | return funcp; |
@@ -3096,18 +3116,11 @@ static const struct function *find_function(const char *name) | |||
3096 | /* Note: takes ownership on name ptr */ | 3116 | /* Note: takes ownership on name ptr */ |
3097 | static struct function *new_function(char *name) | 3117 | static struct function *new_function(char *name) |
3098 | { | 3118 | { |
3099 | struct function *funcp; | 3119 | struct function **funcpp = find_function_slot(name); |
3100 | struct function **funcpp = &G.top_func; | 3120 | struct function *funcp = *funcpp; |
3101 | 3121 | ||
3102 | while ((funcp = *funcpp) != NULL) { | 3122 | if (funcp != NULL) { |
3103 | struct command *cmd; | 3123 | struct command *cmd = funcp->parent_cmd; |
3104 | |||
3105 | if (strcmp(funcp->name, name) != 0) { | ||
3106 | funcpp = &funcp->next; | ||
3107 | continue; | ||
3108 | } | ||
3109 | |||
3110 | cmd = funcp->parent_cmd; | ||
3111 | debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd); | 3124 | debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd); |
3112 | if (!cmd) { | 3125 | if (!cmd) { |
3113 | debug_printf_exec("freeing & replacing function '%s'\n", funcp->name); | 3126 | debug_printf_exec("freeing & replacing function '%s'\n", funcp->name); |
@@ -3129,39 +3142,36 @@ static struct function *new_function(char *name) | |||
3129 | cmd->group_as_string = funcp->body_as_string; | 3142 | cmd->group_as_string = funcp->body_as_string; |
3130 | # endif | 3143 | # endif |
3131 | } | 3144 | } |
3132 | goto skip; | 3145 | } else { |
3146 | debug_printf_exec("remembering new function '%s'\n", name); | ||
3147 | funcp = *funcpp = xzalloc(sizeof(*funcp)); | ||
3148 | /*funcp->next = NULL;*/ | ||
3133 | } | 3149 | } |
3134 | debug_printf_exec("remembering new function '%s'\n", name); | 3150 | |
3135 | funcp = *funcpp = xzalloc(sizeof(*funcp)); | ||
3136 | /*funcp->next = NULL;*/ | ||
3137 | skip: | ||
3138 | funcp->name = name; | 3151 | funcp->name = name; |
3139 | return funcp; | 3152 | return funcp; |
3140 | } | 3153 | } |
3141 | 3154 | ||
3142 | static void unset_func(const char *name) | 3155 | static void unset_func(const char *name) |
3143 | { | 3156 | { |
3144 | struct function *funcp; | 3157 | struct function **funcpp = find_function_slot(name); |
3145 | struct function **funcpp = &G.top_func; | 3158 | struct function *funcp = *funcpp; |
3146 | 3159 | ||
3147 | while ((funcp = *funcpp) != NULL) { | 3160 | if (funcp != NULL) { |
3148 | if (strcmp(funcp->name, name) == 0) { | 3161 | debug_printf_exec("freeing function '%s'\n", funcp->name); |
3149 | *funcpp = funcp->next; | 3162 | *funcpp = funcp->next; |
3150 | /* funcp is unlinked now, deleting it. | 3163 | /* funcp is unlinked now, deleting it. |
3151 | * Note: if !funcp->body, the function was created by | 3164 | * Note: if !funcp->body, the function was created by |
3152 | * "-F name body", do not free ->body_as_string | 3165 | * "-F name body", do not free ->body_as_string |
3153 | * and ->name as they were not malloced. */ | 3166 | * and ->name as they were not malloced. */ |
3154 | if (funcp->body) { | 3167 | if (funcp->body) { |
3155 | free_pipe_list(funcp->body); | 3168 | free_pipe_list(funcp->body); |
3156 | free(funcp->name); | 3169 | free(funcp->name); |
3157 | # if !BB_MMU | 3170 | # if !BB_MMU |
3158 | free(funcp->body_as_string); | 3171 | free(funcp->body_as_string); |
3159 | # endif | 3172 | # endif |
3160 | } | ||
3161 | free(funcp); | ||
3162 | break; | ||
3163 | } | 3173 | } |
3164 | funcpp = &funcp->next; | 3174 | free(funcp); |
3165 | } | 3175 | } |
3166 | } | 3176 | } |
3167 | 3177 | ||
@@ -3628,9 +3638,9 @@ static int checkjobs(struct pipe* fg_pipe) | |||
3628 | /* Note: is WIFSIGNALED, WEXITSTATUS = sig + 128 */ | 3638 | /* Note: is WIFSIGNALED, WEXITSTATUS = sig + 128 */ |
3629 | rcode = WEXITSTATUS(status); | 3639 | rcode = WEXITSTATUS(status); |
3630 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) | 3640 | IF_HAS_KEYWORDS(if (fg_pipe->pi_inverted) rcode = !rcode;) |
3631 | /* bash prints killing signal's name for *last* | 3641 | /* bash prints killer signal's name for *last* |
3632 | * process in pipe (prints just newline for SIGINT). | 3642 | * process in pipe (prints just newline for SIGINT). |
3633 | * Mimic this. Example: "sleep 5" + ^\ | 3643 | * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT) |
3634 | */ | 3644 | */ |
3635 | if (WIFSIGNALED(status)) { | 3645 | if (WIFSIGNALED(status)) { |
3636 | int sig = WTERMSIG(status); | 3646 | int sig = WTERMSIG(status); |
@@ -5183,6 +5193,47 @@ static FILE *generate_stream_from_string(const char *s) | |||
5183 | xmove_fd(channel[1], 1); | 5193 | xmove_fd(channel[1], 1); |
5184 | /* Prevent it from trying to handle ctrl-z etc */ | 5194 | /* Prevent it from trying to handle ctrl-z etc */ |
5185 | IF_HUSH_JOB(G.run_list_level = 1;) | 5195 | IF_HUSH_JOB(G.run_list_level = 1;) |
5196 | /* Awful hack for `trap` or $(trap). | ||
5197 | * | ||
5198 | * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html | ||
5199 | * contains an example where "trap" is executed in a subshell: | ||
5200 | * | ||
5201 | * save_traps=$(trap) | ||
5202 | * ... | ||
5203 | * eval "$save_traps" | ||
5204 | * | ||
5205 | * Standard does not say that "trap" in subshell shall print | ||
5206 | * parent shell's traps. It only says that its output | ||
5207 | * must have suitable form, but then, in the above example | ||
5208 | * (which is not supposed to be normative), it implies that. | ||
5209 | * | ||
5210 | * bash (and probably other shell) does implement it | ||
5211 | * (traps are reset to defaults, but "trap" still shows them), | ||
5212 | * but as a result, "trap" logic is hopelessly messed up: | ||
5213 | * | ||
5214 | * # trap | ||
5215 | * trap -- 'echo Ho' SIGWINCH <--- we have a handler | ||
5216 | * # (trap) <--- trap is in subshell - no output (correct, traps are reset) | ||
5217 | * # true | trap <--- trap is in subshell - no output (ditto) | ||
5218 | * # echo `true | trap` <--- in subshell - output (but traps are reset!) | ||
5219 | * trap -- 'echo Ho' SIGWINCH | ||
5220 | * # echo `(trap)` <--- in subshell in subshell - output | ||
5221 | * trap -- 'echo Ho' SIGWINCH | ||
5222 | * # echo `true | (trap)` <--- in subshell in subshell in subshell - output! | ||
5223 | * trap -- 'echo Ho' SIGWINCH | ||
5224 | * | ||
5225 | * The rules when to forget and when to not forget traps | ||
5226 | * get really complex and nonsensical. | ||
5227 | * | ||
5228 | * Our solution: ONLY bare $(trap) or `trap` is special. | ||
5229 | */ | ||
5230 | s = skip_whitespace(s); | ||
5231 | if (strncmp(s, "trap", 4) == 0 && (*skip_whitespace(s + 4) == '\0')) | ||
5232 | { | ||
5233 | static const char *const argv[] = { NULL, NULL }; | ||
5234 | builtin_trap((char**)argv); | ||
5235 | exit(0); /* not _exit() - we need to fflush */ | ||
5236 | } | ||
5186 | #if BB_MMU | 5237 | #if BB_MMU |
5187 | reset_traps_to_defaults(); | 5238 | reset_traps_to_defaults(); |
5188 | parse_and_run_string(s); | 5239 | parse_and_run_string(s); |
@@ -5676,8 +5727,10 @@ static int handle_dollar(o_string *as_string, | |||
5676 | goto make_var; | 5727 | goto make_var; |
5677 | } | 5728 | } |
5678 | /* else: it's $_ */ | 5729 | /* else: it's $_ */ |
5679 | /* TODO: */ | 5730 | /* TODO: $_ and $-: */ |
5680 | /* $_ Shell or shell script name; or last cmd name */ | 5731 | /* $_ Shell or shell script name; or last argument of last command |
5732 | * (if last command wasn't a pipe; if it was, bash sets $_ to ""); | ||
5733 | * but in command's env, set to full pathname used to invoke it */ | ||
5681 | /* $- Option flags set by set builtin or shell options (-i etc) */ | 5734 | /* $- Option flags set by set builtin or shell options (-i etc) */ |
5682 | default: | 5735 | default: |
5683 | o_addQchr(dest, '$'); | 5736 | o_addQchr(dest, '$'); |
@@ -5794,7 +5847,7 @@ static struct pipe *parse_stream(char **pstring, | |||
5794 | * found. When recursing, quote state is passed in via dest->o_escape. | 5847 | * found. When recursing, quote state is passed in via dest->o_escape. |
5795 | */ | 5848 | */ |
5796 | debug_printf_parse("parse_stream entered, end_trigger='%c'\n", | 5849 | debug_printf_parse("parse_stream entered, end_trigger='%c'\n", |
5797 | end_trigger ? : 'X'); | 5850 | end_trigger ? end_trigger : 'X'); |
5798 | debug_enter(); | 5851 | debug_enter(); |
5799 | 5852 | ||
5800 | G.ifs = get_local_var_value("IFS"); | 5853 | G.ifs = get_local_var_value("IFS"); |
@@ -6860,7 +6913,8 @@ static int FAST_FUNC builtin_cd(char **argv) | |||
6860 | * bash says "bash: cd: HOME not set" and does nothing | 6913 | * bash says "bash: cd: HOME not set" and does nothing |
6861 | * (exitcode 1) | 6914 | * (exitcode 1) |
6862 | */ | 6915 | */ |
6863 | newdir = get_local_var_value("HOME") ? : "/"; | 6916 | const char *home = get_local_var_value("HOME"); |
6917 | newdir = home ? home : "/"; | ||
6864 | } | 6918 | } |
6865 | if (chdir(newdir)) { | 6919 | if (chdir(newdir)) { |
6866 | /* Mimic bash message exactly */ | 6920 | /* Mimic bash message exactly */ |
@@ -7057,6 +7111,10 @@ static int FAST_FUNC builtin_trap(char **argv) | |||
7057 | if (G.traps[i]) { | 7111 | if (G.traps[i]) { |
7058 | printf("trap -- "); | 7112 | printf("trap -- "); |
7059 | print_escaped(G.traps[i]); | 7113 | print_escaped(G.traps[i]); |
7114 | /* note: bash adds "SIG", but only if invoked | ||
7115 | * as "bash". If called as "sh", or if set -o posix, | ||
7116 | * then it prints short signal names. | ||
7117 | * We are printing short names: */ | ||
7060 | printf(" %s\n", get_signame(i)); | 7118 | printf(" %s\n", get_signame(i)); |
7061 | } | 7119 | } |
7062 | } | 7120 | } |
@@ -7268,6 +7326,10 @@ static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | |||
7268 | void *p; | 7326 | void *p; |
7269 | unsigned long l; | 7327 | unsigned long l; |
7270 | 7328 | ||
7329 | #ifdef M_TRIM_THRESHOLD | ||
7330 | /* Optional. Reduces probability of false positives */ | ||
7331 | malloc_trim(0); | ||
7332 | #endif | ||
7271 | /* Crude attempt to find where "free memory" starts, | 7333 | /* Crude attempt to find where "free memory" starts, |
7272 | * sans fragmentation. */ | 7334 | * sans fragmentation. */ |
7273 | p = malloc(240); | 7335 | p = malloc(240); |
diff --git a/shell/hush_test/hush-trap/savetrap.right b/shell/hush_test/hush-trap/savetrap.right new file mode 100644 index 000000000..a59225be3 --- /dev/null +++ b/shell/hush_test/hush-trap/savetrap.right | |||
@@ -0,0 +1,8 @@ | |||
1 | trap -- 'echo Exiting' EXIT | ||
2 | trap -- 'echo WINCH!' WINCH | ||
3 | trap -- 'echo Exiting' EXIT | ||
4 | trap -- 'echo WINCH!' WINCH | ||
5 | trap -- 'echo Exiting' EXIT | ||
6 | trap -- 'echo WINCH!' WINCH | ||
7 | Done | ||
8 | Exiting | ||
diff --git a/shell/hush_test/hush-trap/savetrap.tests b/shell/hush_test/hush-trap/savetrap.tests new file mode 100755 index 000000000..c2b312fb8 --- /dev/null +++ b/shell/hush_test/hush-trap/savetrap.tests | |||
@@ -0,0 +1,9 @@ | |||
1 | trap 'echo Exiting' EXIT | ||
2 | trap 'echo WINCH!' SIGWINCH | ||
3 | v=` trap ` | ||
4 | echo "$v" | ||
5 | v=$( trap ) | ||
6 | echo "$v" | ||
7 | v=`trap` | ||
8 | echo "$v" | ||
9 | echo Done | ||
diff --git a/shell/hush_test/hush-trap/subshell.right b/shell/hush_test/hush-trap/subshell.right new file mode 100644 index 000000000..0d20ed4e9 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.right | |||
@@ -0,0 +1,6 @@ | |||
1 | Ok | ||
2 | Ok | ||
3 | Ok | ||
4 | Ok | ||
5 | TERM | ||
6 | Done | ||
diff --git a/shell/hush_test/hush-trap/subshell.tests b/shell/hush_test/hush-trap/subshell.tests new file mode 100755 index 000000000..4564c2ee2 --- /dev/null +++ b/shell/hush_test/hush-trap/subshell.tests | |||
@@ -0,0 +1,20 @@ | |||
1 | # Non-empty traps should be reset in subshell | ||
2 | |||
3 | # HUP is special in interactive shells | ||
4 | trap '' HUP | ||
5 | # QUIT is always special | ||
6 | trap '' QUIT | ||
7 | # SYS is not special | ||
8 | trap '' SYS | ||
9 | # WINCH is harmless | ||
10 | trap 'bad: caught WINCH' WINCH | ||
11 | # With TERM we'll check whether it is reset | ||
12 | trap 'bad: caught TERM' TERM | ||
13 | |||
14 | # using bash, because we don't have $PPID (yet) | ||
15 | (bash -c 'kill -HUP $PPID'; echo Ok) | ||
16 | (bash -c 'kill -QUIT $PPID'; echo Ok) | ||
17 | (bash -c 'kill -SYS $PPID'; echo Ok) | ||
18 | (bash -c 'kill -WINCH $PPID'; echo Ok) | ||
19 | (bash -c 'kill -TERM $PPID'; echo Bad: TERM is not reset) | ||
20 | echo Done | ||