diff options
Diffstat (limited to 'shell/ash.c')
-rw-r--r-- | shell/ash.c | 1245 |
1 files changed, 742 insertions, 503 deletions
diff --git a/shell/ash.c b/shell/ash.c index 750ca7b6d..02e76c0ae 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -57,8 +57,18 @@ | |||
57 | #include <sys/utsname.h> /* for setting $HOSTNAME */ | 57 | #include <sys/utsname.h> /* for setting $HOSTNAME */ |
58 | 58 | ||
59 | #include "busybox.h" /* for applet_names */ | 59 | #include "busybox.h" /* for applet_names */ |
60 | #include "unicode.h" | ||
61 | 60 | ||
61 | #if defined(__ANDROID_API__) && __ANDROID_API__ <= 24 | ||
62 | /* Bionic at least up to version 24 has no glob() */ | ||
63 | # undef ENABLE_ASH_INTERNAL_GLOB | ||
64 | # define ENABLE_ASH_INTERNAL_GLOB 1 | ||
65 | #endif | ||
66 | |||
67 | #if !ENABLE_ASH_INTERNAL_GLOB | ||
68 | # include <glob.h> | ||
69 | #endif | ||
70 | |||
71 | #include "unicode.h" | ||
62 | #include "shell_common.h" | 72 | #include "shell_common.h" |
63 | #if ENABLE_SH_MATH_SUPPORT | 73 | #if ENABLE_SH_MATH_SUPPORT |
64 | # include "math.h" | 74 | # include "math.h" |
@@ -104,6 +114,42 @@ | |||
104 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell | 114 | //config: shell (by Herbert Xu), which was created by porting the 'ash' shell |
105 | //config: (written by Kenneth Almquist) from NetBSD. | 115 | //config: (written by Kenneth Almquist) from NetBSD. |
106 | //config: | 116 | //config: |
117 | //config:config ASH_OPTIMIZE_FOR_SIZE | ||
118 | //config: bool "Optimize for size instead of speed" | ||
119 | //config: default y | ||
120 | //config: depends on ASH | ||
121 | //config: help | ||
122 | //config: Compile ash for reduced size at the price of speed. | ||
123 | //config: | ||
124 | //config:config ASH_INTERNAL_GLOB | ||
125 | //config: bool "Use internal glob() implementation" | ||
126 | //config: default n | ||
127 | //config: depends on ASH | ||
128 | //config: help | ||
129 | //config: Do not use glob() function from libc, use internal implementation. | ||
130 | //config: Use this if you are getting "glob.h: No such file or directory" | ||
131 | //config: or similar build errors. | ||
132 | //config: | ||
133 | //config:config ASH_RANDOM_SUPPORT | ||
134 | //config: bool "Pseudorandom generator and $RANDOM variable" | ||
135 | //config: default y | ||
136 | //config: depends on ASH | ||
137 | //config: help | ||
138 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | ||
139 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | ||
140 | //config: You can reset the generator by using a specified start value. | ||
141 | //config: After "unset RANDOM" the generator will switch off and this | ||
142 | //config: variable will no longer have special treatment. | ||
143 | //config: | ||
144 | //config:config ASH_EXPAND_PRMT | ||
145 | //config: bool "Expand prompt string" | ||
146 | //config: default y | ||
147 | //config: depends on ASH | ||
148 | //config: help | ||
149 | //config: "PS#" may contain volatile content, such as backquote commands. | ||
150 | //config: This option recreates the prompt string from the environment | ||
151 | //config: variable each time it is displayed. | ||
152 | //config: | ||
107 | //config:config ASH_BASH_COMPAT | 153 | //config:config ASH_BASH_COMPAT |
108 | //config: bool "bash-compatible extensions" | 154 | //config: bool "bash-compatible extensions" |
109 | //config: default y | 155 | //config: default y |
@@ -183,33 +229,6 @@ | |||
183 | //config: help | 229 | //config: help |
184 | //config: Enable "check for new mail" function in the ash shell. | 230 | //config: Enable "check for new mail" function in the ash shell. |
185 | //config: | 231 | //config: |
186 | //config:config ASH_OPTIMIZE_FOR_SIZE | ||
187 | //config: bool "Optimize for size instead of speed" | ||
188 | //config: default y | ||
189 | //config: depends on ASH | ||
190 | //config: help | ||
191 | //config: Compile ash for reduced size at the price of speed. | ||
192 | //config: | ||
193 | //config:config ASH_RANDOM_SUPPORT | ||
194 | //config: bool "Pseudorandom generator and $RANDOM variable" | ||
195 | //config: default y | ||
196 | //config: depends on ASH | ||
197 | //config: help | ||
198 | //config: Enable pseudorandom generator and dynamic variable "$RANDOM". | ||
199 | //config: Each read of "$RANDOM" will generate a new pseudorandom value. | ||
200 | //config: You can reset the generator by using a specified start value. | ||
201 | //config: After "unset RANDOM" the generator will switch off and this | ||
202 | //config: variable will no longer have special treatment. | ||
203 | //config: | ||
204 | //config:config ASH_EXPAND_PRMT | ||
205 | //config: bool "Expand prompt string" | ||
206 | //config: default y | ||
207 | //config: depends on ASH | ||
208 | //config: help | ||
209 | //config: "PS#" may contain volatile content, such as backquote commands. | ||
210 | //config: This option recreates the prompt string from the environment | ||
211 | //config: variable each time it is displayed. | ||
212 | //config: | ||
213 | 232 | ||
214 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) | 233 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) |
215 | //applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) | 234 | //applet:IF_FEATURE_SH_IS_ASH(APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, sh)) |
@@ -324,8 +343,10 @@ struct jmploc { | |||
324 | }; | 343 | }; |
325 | 344 | ||
326 | struct globals_misc { | 345 | struct globals_misc { |
327 | /* pid of main shell */ | 346 | uint8_t exitstatus; /* exit status of last command */ |
328 | int rootpid; | 347 | uint8_t back_exitstatus;/* exit status of backquoted command */ |
348 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ | ||
349 | int rootpid; /* pid of main shell */ | ||
329 | /* shell level: 0 for the main shell, 1 for its children, and so on */ | 350 | /* shell level: 0 for the main shell, 1 for its children, and so on */ |
330 | int shlvl; | 351 | int shlvl; |
331 | #define rootshell (!shlvl) | 352 | #define rootshell (!shlvl) |
@@ -393,7 +414,7 @@ struct globals_misc { | |||
393 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ | 414 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ |
394 | #define S_CATCH 2 /* signal is caught */ | 415 | #define S_CATCH 2 /* signal is caught */ |
395 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ | 416 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ |
396 | #define S_HARD_IGN 4 /* signal is ignored permenantly */ | 417 | #define S_HARD_IGN 4 /* signal is ignored permanently */ |
397 | 418 | ||
398 | /* indicates specified signal received */ | 419 | /* indicates specified signal received */ |
399 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ | 420 | uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ |
@@ -406,10 +427,12 @@ struct globals_misc { | |||
406 | random_t random_gen; | 427 | random_t random_gen; |
407 | #endif | 428 | #endif |
408 | pid_t backgndpid; /* pid of last background process */ | 429 | pid_t backgndpid; /* pid of last background process */ |
409 | smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ | ||
410 | }; | 430 | }; |
411 | extern struct globals_misc *const ash_ptr_to_globals_misc; | 431 | extern struct globals_misc *const ash_ptr_to_globals_misc; |
412 | #define G_misc (*ash_ptr_to_globals_misc) | 432 | #define G_misc (*ash_ptr_to_globals_misc) |
433 | #define exitstatus (G_misc.exitstatus ) | ||
434 | #define back_exitstatus (G_misc.back_exitstatus ) | ||
435 | #define job_warning (G_misc.job_warning) | ||
413 | #define rootpid (G_misc.rootpid ) | 436 | #define rootpid (G_misc.rootpid ) |
414 | #define shlvl (G_misc.shlvl ) | 437 | #define shlvl (G_misc.shlvl ) |
415 | #define minusc (G_misc.minusc ) | 438 | #define minusc (G_misc.minusc ) |
@@ -431,7 +454,6 @@ extern struct globals_misc *const ash_ptr_to_globals_misc; | |||
431 | #define trap_ptr (G_misc.trap_ptr ) | 454 | #define trap_ptr (G_misc.trap_ptr ) |
432 | #define random_gen (G_misc.random_gen ) | 455 | #define random_gen (G_misc.random_gen ) |
433 | #define backgndpid (G_misc.backgndpid ) | 456 | #define backgndpid (G_misc.backgndpid ) |
434 | #define job_warning (G_misc.job_warning) | ||
435 | #define INIT_G_misc() do { \ | 457 | #define INIT_G_misc() do { \ |
436 | (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ | 458 | (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ |
437 | barrier(); \ | 459 | barrier(); \ |
@@ -460,12 +482,11 @@ static void trace_vprintf(const char *fmt, va_list va); | |||
460 | 482 | ||
461 | 483 | ||
462 | /* ============ Utility functions */ | 484 | /* ============ Utility functions */ |
463 | #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0) | ||
464 | |||
465 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | 485 | #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) |
466 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | 486 | #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) |
467 | 487 | ||
468 | static int isdigit_str9(const char *str) | 488 | static int |
489 | isdigit_str9(const char *str) | ||
469 | { | 490 | { |
470 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ | 491 | int maxlen = 9 + 1; /* max 9 digits: 999999999 */ |
471 | while (--maxlen && isdigit(*str)) | 492 | while (--maxlen && isdigit(*str)) |
@@ -473,7 +494,8 @@ static int isdigit_str9(const char *str) | |||
473 | return (*str == '\0'); | 494 | return (*str == '\0'); |
474 | } | 495 | } |
475 | 496 | ||
476 | static const char *var_end(const char *var) | 497 | static const char * |
498 | var_end(const char *var) | ||
477 | { | 499 | { |
478 | while (*var) | 500 | while (*var) |
479 | if (*var++ == '=') | 501 | if (*var++ == '=') |
@@ -494,7 +516,7 @@ static void exitshell(void) NORETURN; | |||
494 | */ | 516 | */ |
495 | #define INT_OFF do { \ | 517 | #define INT_OFF do { \ |
496 | suppress_int++; \ | 518 | suppress_int++; \ |
497 | xbarrier(); \ | 519 | barrier(); \ |
498 | } while (0) | 520 | } while (0) |
499 | 521 | ||
500 | /* | 522 | /* |
@@ -522,7 +544,7 @@ raise_exception(int e) | |||
522 | #endif | 544 | #endif |
523 | 545 | ||
524 | /* | 546 | /* |
525 | * Called from trap.c when a SIGINT is received. (If the user specifies | 547 | * Called when a SIGINT is received. (If the user specifies |
526 | * that SIGINT is to be trapped or ignored using the trap builtin, then | 548 | * that SIGINT is to be trapped or ignored using the trap builtin, then |
527 | * this routine is not called.) Suppressint is nonzero when interrupts | 549 | * this routine is not called.) Suppressint is nonzero when interrupts |
528 | * are held using the INT_OFF macro. (The test for iflag is just | 550 | * are held using the INT_OFF macro. (The test for iflag is just |
@@ -549,6 +571,8 @@ raise_interrupt(void) | |||
549 | } | 571 | } |
550 | ex_type = EXINT; | 572 | ex_type = EXINT; |
551 | } | 573 | } |
574 | /* bash: ^C even on empty command line sets $? */ | ||
575 | exitstatus = SIGINT + 128; | ||
552 | raise_exception(ex_type); | 576 | raise_exception(ex_type); |
553 | /* NOTREACHED */ | 577 | /* NOTREACHED */ |
554 | } | 578 | } |
@@ -562,7 +586,7 @@ raise_interrupt(void) | |||
562 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | 586 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
563 | int_on(void) | 587 | int_on(void) |
564 | { | 588 | { |
565 | xbarrier(); | 589 | barrier(); |
566 | if (--suppress_int == 0 && pending_int) { | 590 | if (--suppress_int == 0 && pending_int) { |
567 | raise_interrupt(); | 591 | raise_interrupt(); |
568 | } | 592 | } |
@@ -571,7 +595,7 @@ int_on(void) | |||
571 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void | 595 | static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void |
572 | force_int_on(void) | 596 | force_int_on(void) |
573 | { | 597 | { |
574 | xbarrier(); | 598 | barrier(); |
575 | suppress_int = 0; | 599 | suppress_int = 0; |
576 | if (pending_int) | 600 | if (pending_int) |
577 | raise_interrupt(); | 601 | raise_interrupt(); |
@@ -581,7 +605,7 @@ force_int_on(void) | |||
581 | #define SAVE_INT(v) ((v) = suppress_int) | 605 | #define SAVE_INT(v) ((v) = suppress_int) |
582 | 606 | ||
583 | #define RESTORE_INT(v) do { \ | 607 | #define RESTORE_INT(v) do { \ |
584 | xbarrier(); \ | 608 | barrier(); \ |
585 | suppress_int = (v); \ | 609 | suppress_int = (v); \ |
586 | if (suppress_int == 0 && pending_int) \ | 610 | if (suppress_int == 0 && pending_int) \ |
587 | raise_interrupt(); \ | 611 | raise_interrupt(); \ |
@@ -1237,6 +1261,12 @@ struct strpush { | |||
1237 | struct alias *ap; /* if push was associated with an alias */ | 1261 | struct alias *ap; /* if push was associated with an alias */ |
1238 | #endif | 1262 | #endif |
1239 | char *string; /* remember the string since it may change */ | 1263 | char *string; /* remember the string since it may change */ |
1264 | |||
1265 | /* Remember last two characters for pungetc. */ | ||
1266 | int lastc[2]; | ||
1267 | |||
1268 | /* Number of outstanding calls to pungetc. */ | ||
1269 | int unget; | ||
1240 | }; | 1270 | }; |
1241 | 1271 | ||
1242 | struct parsefile { | 1272 | struct parsefile { |
@@ -1249,6 +1279,12 @@ struct parsefile { | |||
1249 | char *buf; /* input buffer */ | 1279 | char *buf; /* input buffer */ |
1250 | struct strpush *strpush; /* for pushing strings at this level */ | 1280 | struct strpush *strpush; /* for pushing strings at this level */ |
1251 | struct strpush basestrpush; /* so pushing one is fast */ | 1281 | struct strpush basestrpush; /* so pushing one is fast */ |
1282 | |||
1283 | /* Remember last two characters for pungetc. */ | ||
1284 | int lastc[2]; | ||
1285 | |||
1286 | /* Number of outstanding calls to pungetc. */ | ||
1287 | int unget; | ||
1252 | }; | 1288 | }; |
1253 | 1289 | ||
1254 | static struct parsefile basepf; /* top level input file */ | 1290 | static struct parsefile basepf; /* top level input file */ |
@@ -1256,7 +1292,6 @@ static struct parsefile *g_parsefile = &basepf; /* current input file */ | |||
1256 | static int startlinno; /* line # where last token started */ | 1292 | static int startlinno; /* line # where last token started */ |
1257 | static char *commandname; /* currently executing command */ | 1293 | static char *commandname; /* currently executing command */ |
1258 | static struct strlist *cmdenviron; /* environment for builtin command */ | 1294 | static struct strlist *cmdenviron; /* environment for builtin command */ |
1259 | static uint8_t exitstatus; /* exit status of last command */ | ||
1260 | 1295 | ||
1261 | 1296 | ||
1262 | /* ============ Message printing */ | 1297 | /* ============ Message printing */ |
@@ -1428,13 +1463,11 @@ struct stackmark { | |||
1428 | struct stack_block *stackp; | 1463 | struct stack_block *stackp; |
1429 | char *stacknxt; | 1464 | char *stacknxt; |
1430 | size_t stacknleft; | 1465 | size_t stacknleft; |
1431 | struct stackmark *marknext; | ||
1432 | }; | 1466 | }; |
1433 | 1467 | ||
1434 | 1468 | ||
1435 | struct globals_memstack { | 1469 | struct globals_memstack { |
1436 | struct stack_block *g_stackp; // = &stackbase; | 1470 | struct stack_block *g_stackp; // = &stackbase; |
1437 | struct stackmark *markp; | ||
1438 | char *g_stacknxt; // = stackbase.space; | 1471 | char *g_stacknxt; // = stackbase.space; |
1439 | char *sstrend; // = stackbase.space + MINSIZE; | 1472 | char *sstrend; // = stackbase.space + MINSIZE; |
1440 | size_t g_stacknleft; // = MINSIZE; | 1473 | size_t g_stacknleft; // = MINSIZE; |
@@ -1444,7 +1477,6 @@ struct globals_memstack { | |||
1444 | extern struct globals_memstack *const ash_ptr_to_globals_memstack; | 1477 | extern struct globals_memstack *const ash_ptr_to_globals_memstack; |
1445 | #define G_memstack (*ash_ptr_to_globals_memstack) | 1478 | #define G_memstack (*ash_ptr_to_globals_memstack) |
1446 | #define g_stackp (G_memstack.g_stackp ) | 1479 | #define g_stackp (G_memstack.g_stackp ) |
1447 | #define markp (G_memstack.markp ) | ||
1448 | #define g_stacknxt (G_memstack.g_stacknxt ) | 1480 | #define g_stacknxt (G_memstack.g_stacknxt ) |
1449 | #define sstrend (G_memstack.sstrend ) | 1481 | #define sstrend (G_memstack.sstrend ) |
1450 | #define g_stacknleft (G_memstack.g_stacknleft) | 1482 | #define g_stacknleft (G_memstack.g_stacknleft) |
@@ -1528,20 +1560,33 @@ stunalloc(void *p) | |||
1528 | * Like strdup but works with the ash stack. | 1560 | * Like strdup but works with the ash stack. |
1529 | */ | 1561 | */ |
1530 | static char * | 1562 | static char * |
1531 | ststrdup(const char *p) | 1563 | sstrdup(const char *p) |
1532 | { | 1564 | { |
1533 | size_t len = strlen(p) + 1; | 1565 | size_t len = strlen(p) + 1; |
1534 | return memcpy(stalloc(len), p, len); | 1566 | return memcpy(stalloc(len), p, len); |
1535 | } | 1567 | } |
1536 | 1568 | ||
1537 | static void | 1569 | static void |
1538 | setstackmark(struct stackmark *mark) | 1570 | grabstackblock(size_t len) |
1571 | { | ||
1572 | len = SHELL_ALIGN(len); | ||
1573 | g_stacknxt += len; | ||
1574 | g_stacknleft -= len; | ||
1575 | } | ||
1576 | |||
1577 | static void | ||
1578 | pushstackmark(struct stackmark *mark, size_t len) | ||
1539 | { | 1579 | { |
1540 | mark->stackp = g_stackp; | 1580 | mark->stackp = g_stackp; |
1541 | mark->stacknxt = g_stacknxt; | 1581 | mark->stacknxt = g_stacknxt; |
1542 | mark->stacknleft = g_stacknleft; | 1582 | mark->stacknleft = g_stacknleft; |
1543 | mark->marknext = markp; | 1583 | grabstackblock(len); |
1544 | markp = mark; | 1584 | } |
1585 | |||
1586 | static void | ||
1587 | setstackmark(struct stackmark *mark) | ||
1588 | { | ||
1589 | pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase); | ||
1545 | } | 1590 | } |
1546 | 1591 | ||
1547 | static void | 1592 | static void |
@@ -1553,7 +1598,6 @@ popstackmark(struct stackmark *mark) | |||
1553 | return; | 1598 | return; |
1554 | 1599 | ||
1555 | INT_OFF; | 1600 | INT_OFF; |
1556 | markp = mark->marknext; | ||
1557 | while (g_stackp != mark->stackp) { | 1601 | while (g_stackp != mark->stackp) { |
1558 | sp = g_stackp; | 1602 | sp = g_stackp; |
1559 | g_stackp = sp->prev; | 1603 | g_stackp = sp->prev; |
@@ -1586,14 +1630,11 @@ growstackblock(void) | |||
1586 | newlen += 128; | 1630 | newlen += 128; |
1587 | 1631 | ||
1588 | if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { | 1632 | if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) { |
1589 | struct stack_block *oldstackp; | ||
1590 | struct stackmark *xmark; | ||
1591 | struct stack_block *sp; | 1633 | struct stack_block *sp; |
1592 | struct stack_block *prevstackp; | 1634 | struct stack_block *prevstackp; |
1593 | size_t grosslen; | 1635 | size_t grosslen; |
1594 | 1636 | ||
1595 | INT_OFF; | 1637 | INT_OFF; |
1596 | oldstackp = g_stackp; | ||
1597 | sp = g_stackp; | 1638 | sp = g_stackp; |
1598 | prevstackp = sp->prev; | 1639 | prevstackp = sp->prev; |
1599 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; | 1640 | grosslen = newlen + sizeof(struct stack_block) - MINSIZE; |
@@ -1603,18 +1644,6 @@ growstackblock(void) | |||
1603 | g_stacknxt = sp->space; | 1644 | g_stacknxt = sp->space; |
1604 | g_stacknleft = newlen; | 1645 | g_stacknleft = newlen; |
1605 | sstrend = sp->space + newlen; | 1646 | sstrend = sp->space + newlen; |
1606 | |||
1607 | /* | ||
1608 | * Stack marks pointing to the start of the old block | ||
1609 | * must be relocated to point to the new block | ||
1610 | */ | ||
1611 | xmark = markp; | ||
1612 | while (xmark != NULL && xmark->stackp == oldstackp) { | ||
1613 | xmark->stackp = g_stackp; | ||
1614 | xmark->stacknxt = g_stacknxt; | ||
1615 | xmark->stacknleft = g_stacknleft; | ||
1616 | xmark = xmark->marknext; | ||
1617 | } | ||
1618 | INT_ON; | 1647 | INT_ON; |
1619 | } else { | 1648 | } else { |
1620 | char *oldspace = g_stacknxt; | 1649 | char *oldspace = g_stacknxt; |
@@ -1627,14 +1656,6 @@ growstackblock(void) | |||
1627 | } | 1656 | } |
1628 | } | 1657 | } |
1629 | 1658 | ||
1630 | static void | ||
1631 | grabstackblock(size_t len) | ||
1632 | { | ||
1633 | len = SHELL_ALIGN(len); | ||
1634 | g_stacknxt += len; | ||
1635 | g_stacknleft -= len; | ||
1636 | } | ||
1637 | |||
1638 | /* | 1659 | /* |
1639 | * The following routines are somewhat easier to use than the above. | 1660 | * The following routines are somewhat easier to use than the above. |
1640 | * The user declares a variable of type STACKSTR, which may be declared | 1661 | * The user declares a variable of type STACKSTR, which may be declared |
@@ -1671,7 +1692,7 @@ static char * | |||
1671 | makestrspace(size_t newlen, char *p) | 1692 | makestrspace(size_t newlen, char *p) |
1672 | { | 1693 | { |
1673 | size_t len = p - g_stacknxt; | 1694 | size_t len = p - g_stacknxt; |
1674 | size_t size = stackblocksize(); | 1695 | size_t size; |
1675 | 1696 | ||
1676 | for (;;) { | 1697 | for (;;) { |
1677 | size_t nleft; | 1698 | size_t nleft; |
@@ -1990,7 +2011,7 @@ static const struct { | |||
1990 | { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, | 2011 | { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL }, |
1991 | { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, | 2012 | { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL }, |
1992 | #if ENABLE_ASH_GETOPTS | 2013 | #if ENABLE_ASH_GETOPTS |
1993 | { VSTRFIXED|VTEXTFIXED , "OPTIND=1" , getoptsreset }, | 2014 | { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset }, |
1994 | #endif | 2015 | #endif |
1995 | #if ENABLE_ASH_RANDOM_SUPPORT | 2016 | #if ENABLE_ASH_RANDOM_SUPPORT |
1996 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, | 2017 | { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random }, |
@@ -2193,7 +2214,8 @@ lookupvar(const char *name) | |||
2193 | return NULL; | 2214 | return NULL; |
2194 | } | 2215 | } |
2195 | 2216 | ||
2196 | static void reinit_unicode_for_ash(void) | 2217 | static void |
2218 | reinit_unicode_for_ash(void) | ||
2197 | { | 2219 | { |
2198 | /* Unicode support should be activated even if LANG is set | 2220 | /* Unicode support should be activated even if LANG is set |
2199 | * _during_ shell execution, not only if it was set when | 2221 | * _during_ shell execution, not only if it was set when |
@@ -2560,8 +2582,7 @@ setprompt_if(smallint do_set, int whichprompt) | |||
2560 | prompt = nullstr; | 2582 | prompt = nullstr; |
2561 | } | 2583 | } |
2562 | #if ENABLE_ASH_EXPAND_PRMT | 2584 | #if ENABLE_ASH_EXPAND_PRMT |
2563 | setstackmark(&smark); | 2585 | pushstackmark(&smark, stackblocksize()); |
2564 | stalloc(stackblocksize()); | ||
2565 | #endif | 2586 | #endif |
2566 | putprompt(expandstr(prompt)); | 2587 | putprompt(expandstr(prompt)); |
2567 | #if ENABLE_ASH_EXPAND_PRMT | 2588 | #if ENABLE_ASH_EXPAND_PRMT |
@@ -2704,7 +2725,7 @@ updatepwd(const char *dir) | |||
2704 | char *cdcomppath; | 2725 | char *cdcomppath; |
2705 | const char *lim; | 2726 | const char *lim; |
2706 | 2727 | ||
2707 | cdcomppath = ststrdup(dir); | 2728 | cdcomppath = sstrdup(dir); |
2708 | STARTSTACKSTR(new); | 2729 | STARTSTACKSTR(new); |
2709 | if (*dir != '/') { | 2730 | if (*dir != '/') { |
2710 | if (curdir == nullstr) | 2731 | if (curdir == nullstr) |
@@ -3000,18 +3021,27 @@ enum { | |||
3000 | static int | 3021 | static int |
3001 | SIT(int c, int syntax) | 3022 | SIT(int c, int syntax) |
3002 | { | 3023 | { |
3003 | static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-/:;<=>?[\\]`|}~"; | 3024 | /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */ |
3025 | static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~"; | ||
3026 | /* | ||
3027 | * This causes '/' to be prepended with CTLESC in dquoted string, | ||
3028 | * making "./file"* treated incorrectly because we feed | ||
3029 | * ".\/file*" string to glob(), confusing it (see expandmeta func). | ||
3030 | * The "homegrown" glob implementation is okay with that, | ||
3031 | * but glibc one isn't. With '/' always treated as CWORD, | ||
3032 | * both work fine. | ||
3033 | */ | ||
3004 | # if ENABLE_ASH_ALIAS | 3034 | # if ENABLE_ASH_ALIAS |
3005 | static const uint8_t syntax_index_table[] ALIGN1 = { | 3035 | static const uint8_t syntax_index_table[] ALIGN1 = { |
3006 | 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ | 3036 | 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */ |
3007 | 7, 8, 3, 3, 3, 3, 1, 1, /* "()*-/:;<" */ | 3037 | 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */ |
3008 | 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ | 3038 | 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */ |
3009 | 11, 3 /* "}~" */ | 3039 | 11, 3 /* "}~" */ |
3010 | }; | 3040 | }; |
3011 | # else | 3041 | # else |
3012 | static const uint8_t syntax_index_table[] ALIGN1 = { | 3042 | static const uint8_t syntax_index_table[] ALIGN1 = { |
3013 | 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ | 3043 | 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */ |
3014 | 6, 7, 2, 2, 2, 2, 0, 0, /* "()*-/:;<" */ | 3044 | 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */ |
3015 | 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ | 3045 | 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */ |
3016 | 10, 2 /* "}~" */ | 3046 | 10, 2 /* "}~" */ |
3017 | }; | 3047 | }; |
@@ -3093,7 +3123,8 @@ static const uint8_t syntax_index_table[] ALIGN1 = { | |||
3093 | /* 44 "," */ CWORD_CWORD_CWORD_CWORD, | 3123 | /* 44 "," */ CWORD_CWORD_CWORD_CWORD, |
3094 | /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, | 3124 | /* 45 "-" */ CWORD_CCTL_CCTL_CWORD, |
3095 | /* 46 "." */ CWORD_CWORD_CWORD_CWORD, | 3125 | /* 46 "." */ CWORD_CWORD_CWORD_CWORD, |
3096 | /* 47 "/" */ CWORD_CCTL_CCTL_CWORD, | 3126 | /* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */ |
3127 | /* 47 "/" */ CWORD_CWORD_CWORD_CWORD, | ||
3097 | /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, | 3128 | /* 48 "0" */ CWORD_CWORD_CWORD_CWORD, |
3098 | /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, | 3129 | /* 49 "1" */ CWORD_CWORD_CWORD_CWORD, |
3099 | /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, | 3130 | /* 50 "2" */ CWORD_CWORD_CWORD_CWORD, |
@@ -3335,7 +3366,8 @@ static struct alias **atab; // [ATABSIZE]; | |||
3335 | 3366 | ||
3336 | 3367 | ||
3337 | static struct alias ** | 3368 | static struct alias ** |
3338 | __lookupalias(const char *name) { | 3369 | __lookupalias(const char *name) |
3370 | { | ||
3339 | unsigned int hashval; | 3371 | unsigned int hashval; |
3340 | struct alias **app; | 3372 | struct alias **app; |
3341 | const char *p; | 3373 | const char *p; |
@@ -3517,8 +3549,6 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
3517 | #endif /* ASH_ALIAS */ | 3549 | #endif /* ASH_ALIAS */ |
3518 | 3550 | ||
3519 | 3551 | ||
3520 | /* ============ jobs.c */ | ||
3521 | |||
3522 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ | 3552 | /* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ |
3523 | #define FORK_FG 0 | 3553 | #define FORK_FG 0 |
3524 | #define FORK_BG 1 | 3554 | #define FORK_BG 1 |
@@ -3842,7 +3872,7 @@ getjob(const char *name, int getctl) | |||
3842 | 3872 | ||
3843 | if (is_number(p)) { | 3873 | if (is_number(p)) { |
3844 | num = atoi(p); | 3874 | num = atoi(p); |
3845 | if (num <= njobs) { | 3875 | if (num > 0 && num <= njobs) { |
3846 | jp = jobtab + num - 1; | 3876 | jp = jobtab + num - 1; |
3847 | if (jp->used) | 3877 | if (jp->used) |
3848 | goto gotit; | 3878 | goto gotit; |
@@ -5419,6 +5449,19 @@ openredirect(union node *redir) | |||
5419 | char *fname; | 5449 | char *fname; |
5420 | int f; | 5450 | int f; |
5421 | 5451 | ||
5452 | switch (redir->nfile.type) { | ||
5453 | /* Can't happen, our single caller does this itself */ | ||
5454 | // case NTOFD: | ||
5455 | // case NFROMFD: | ||
5456 | // return -1; | ||
5457 | case NHERE: | ||
5458 | case NXHERE: | ||
5459 | return openhere(redir); | ||
5460 | } | ||
5461 | |||
5462 | /* For N[X]HERE, reading redir->nfile.expfname would touch beyond | ||
5463 | * allocated space. Do it only when we know it is safe. | ||
5464 | */ | ||
5422 | fname = redir->nfile.expfname; | 5465 | fname = redir->nfile.expfname; |
5423 | #if ENABLE_PLATFORM_MINGW32 | 5466 | #if ENABLE_PLATFORM_MINGW32 |
5424 | /* Support for /dev/null */ | 5467 | /* Support for /dev/null */ |
@@ -5445,7 +5488,12 @@ openredirect(union node *redir) | |||
5445 | break; | 5488 | break; |
5446 | } | 5489 | } |
5447 | #endif | 5490 | #endif |
5491 | |||
5448 | switch (redir->nfile.type) { | 5492 | switch (redir->nfile.type) { |
5493 | default: | ||
5494 | #if DEBUG | ||
5495 | abort(); | ||
5496 | #endif | ||
5449 | case NFROM: | 5497 | case NFROM: |
5450 | f = open(fname, O_RDONLY); | 5498 | f = open(fname, O_RDONLY); |
5451 | if (f < 0) | 5499 | if (f < 0) |
@@ -5481,20 +5529,6 @@ openredirect(union node *redir) | |||
5481 | lseek(f, 0, SEEK_END); | 5529 | lseek(f, 0, SEEK_END); |
5482 | #endif | 5530 | #endif |
5483 | break; | 5531 | break; |
5484 | default: | ||
5485 | #if DEBUG | ||
5486 | abort(); | ||
5487 | #endif | ||
5488 | /* Fall through to eliminate warning. */ | ||
5489 | /* Our single caller does this itself */ | ||
5490 | // case NTOFD: | ||
5491 | // case NFROMFD: | ||
5492 | // f = -1; | ||
5493 | // break; | ||
5494 | case NHERE: | ||
5495 | case NXHERE: | ||
5496 | f = openhere(redir); | ||
5497 | break; | ||
5498 | } | 5532 | } |
5499 | 5533 | ||
5500 | return f; | 5534 | return f; |
@@ -5548,7 +5582,8 @@ struct redirtab { | |||
5548 | }; | 5582 | }; |
5549 | #define redirlist (G_var.redirlist) | 5583 | #define redirlist (G_var.redirlist) |
5550 | 5584 | ||
5551 | static int need_to_remember(struct redirtab *rp, int fd) | 5585 | static int |
5586 | need_to_remember(struct redirtab *rp, int fd) | ||
5552 | { | 5587 | { |
5553 | int i; | 5588 | int i; |
5554 | 5589 | ||
@@ -5565,7 +5600,8 @@ static int need_to_remember(struct redirtab *rp, int fd) | |||
5565 | } | 5600 | } |
5566 | 5601 | ||
5567 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ | 5602 | /* "hidden" fd is a fd used to read scripts, or a copy of such */ |
5568 | static int is_hidden_fd(struct redirtab *rp, int fd) | 5603 | static int |
5604 | is_hidden_fd(struct redirtab *rp, int fd) | ||
5569 | { | 5605 | { |
5570 | int i; | 5606 | int i; |
5571 | struct parsefile *pf; | 5607 | struct parsefile *pf; |
@@ -5979,7 +6015,6 @@ rmescapes(char *str, int flag) | |||
5979 | while (*p) { | 6015 | while (*p) { |
5980 | if ((unsigned char)*p == CTLQUOTEMARK) { | 6016 | if ((unsigned char)*p == CTLQUOTEMARK) { |
5981 | // Note: both inquotes and protect_against_glob only affect whether | 6017 | // Note: both inquotes and protect_against_glob only affect whether |
5982 | // CTLESC,<ch> gets converted to <ch> or to \<ch> | ||
5983 | inquotes = ~inquotes; | 6018 | inquotes = ~inquotes; |
5984 | p++; | 6019 | p++; |
5985 | protect_against_glob = globbing; | 6020 | protect_against_glob = globbing; |
@@ -5987,6 +6022,10 @@ rmescapes(char *str, int flag) | |||
5987 | } | 6022 | } |
5988 | if ((unsigned char)*p == CTLESC) { | 6023 | if ((unsigned char)*p == CTLESC) { |
5989 | p++; | 6024 | p++; |
6025 | #if DEBUG | ||
6026 | if (*p == '\0') | ||
6027 | ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)"); | ||
6028 | #endif | ||
5990 | if (protect_against_glob) { | 6029 | if (protect_against_glob) { |
5991 | *q++ = '\\'; | 6030 | *q++ = '\\'; |
5992 | } | 6031 | } |
@@ -6043,11 +6082,14 @@ memtodest(const char *p, size_t len, int syntax, int quotes) | |||
6043 | unsigned char c = *p++; | 6082 | unsigned char c = *p++; |
6044 | if (c) { | 6083 | if (c) { |
6045 | int n = SIT(c, syntax); | 6084 | int n = SIT(c, syntax); |
6046 | if ((quotes & QUOTES_ESC) && | 6085 | if ((quotes & QUOTES_ESC) |
6047 | ((n == CCTL) || | 6086 | && ((n == CCTL) |
6048 | (((quotes & EXP_FULL) || syntax != BASESYNTAX) && | 6087 | || (((quotes & EXP_FULL) || syntax != BASESYNTAX) |
6049 | n == CBACK))) | 6088 | && n == CBACK) |
6089 | ) | ||
6090 | ) { | ||
6050 | USTPUTC(CTLESC, q); | 6091 | USTPUTC(CTLESC, q); |
6092 | } | ||
6051 | } else if (!(quotes & QUOTES_KEEPNUL)) | 6093 | } else if (!(quotes & QUOTES_KEEPNUL)) |
6052 | continue; | 6094 | continue; |
6053 | USTPUTC(c, q); | 6095 | USTPUTC(c, q); |
@@ -6188,9 +6230,8 @@ struct backcmd { /* result of evalbackcmd */ | |||
6188 | }; | 6230 | }; |
6189 | 6231 | ||
6190 | /* These forward decls are needed to use "eval" code for backticks handling: */ | 6232 | /* These forward decls are needed to use "eval" code for backticks handling: */ |
6191 | static uint8_t back_exitstatus; /* exit status of backquoted command */ | ||
6192 | #define EV_EXIT 01 /* exit after evaluating tree */ | 6233 | #define EV_EXIT 01 /* exit after evaluating tree */ |
6193 | static void evaltree(union node *, int); | 6234 | static int evaltree(union node *, int); |
6194 | 6235 | ||
6195 | static void FAST_FUNC | 6236 | static void FAST_FUNC |
6196 | evalbackcmd(union node *n, struct backcmd *result) | 6237 | evalbackcmd(union node *n, struct backcmd *result) |
@@ -6262,10 +6303,8 @@ expbackq(union node *cmd, int flag) | |||
6262 | struct stackmark smark; | 6303 | struct stackmark smark; |
6263 | 6304 | ||
6264 | INT_OFF; | 6305 | INT_OFF; |
6265 | setstackmark(&smark); | 6306 | startloc = expdest - (char *)stackblock(); |
6266 | dest = expdest; | 6307 | pushstackmark(&smark, startloc); |
6267 | startloc = dest - (char *)stackblock(); | ||
6268 | grabstackstr(dest); | ||
6269 | evalbackcmd(cmd, &in); | 6308 | evalbackcmd(cmd, &in); |
6270 | popstackmark(&smark); | 6309 | popstackmark(&smark); |
6271 | 6310 | ||
@@ -6681,6 +6720,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6681 | 6720 | ||
6682 | #if ENABLE_ASH_BASH_COMPAT | 6721 | #if ENABLE_ASH_BASH_COMPAT |
6683 | case VSSUBSTR: | 6722 | case VSSUBSTR: |
6723 | //TODO: support more general format ${v:EXPR:EXPR}, | ||
6724 | // where EXPR follows $(()) rules | ||
6684 | loc = str = stackblock() + strloc; | 6725 | loc = str = stackblock() + strloc; |
6685 | /* Read POS in ${var:POS:LEN} */ | 6726 | /* Read POS in ${var:POS:LEN} */ |
6686 | pos = atoi(loc); /* number(loc) errors out on "1:4" */ | 6727 | pos = atoi(loc); /* number(loc) errors out on "1:4" */ |
@@ -6791,7 +6832,8 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6791 | char *idx, *end; | 6832 | char *idx, *end; |
6792 | 6833 | ||
6793 | if (!repl) { | 6834 | if (!repl) { |
6794 | if ((repl=strchr(str, CTLESC))) | 6835 | repl = strchr(str, CTLESC); |
6836 | if (repl) | ||
6795 | *repl++ = '\0'; | 6837 | *repl++ = '\0'; |
6796 | else | 6838 | else |
6797 | repl = nullstr; | 6839 | repl = nullstr; |
@@ -6924,20 +6966,21 @@ subevalvar(char *p, char *varname, int strloc, int subtype, | |||
6924 | * ash -c 'echo ${#1#}' name:'1=#' | 6966 | * ash -c 'echo ${#1#}' name:'1=#' |
6925 | */ | 6967 | */ |
6926 | static NOINLINE ssize_t | 6968 | static NOINLINE ssize_t |
6927 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | 6969 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list, int *quotedp) |
6928 | { | 6970 | { |
6929 | const char *p; | 6971 | const char *p; |
6930 | int num; | 6972 | int num; |
6931 | int i; | 6973 | int i; |
6932 | ssize_t len = 0; | 6974 | ssize_t len = 0; |
6933 | int sep; | 6975 | int sep; |
6934 | int quoted = flags & EXP_QUOTED; | 6976 | int quoted = *quotedp; |
6935 | int subtype = varflags & VSTYPE; | 6977 | int subtype = varflags & VSTYPE; |
6936 | int discard = subtype == VSPLUS || subtype == VSLENGTH; | 6978 | int discard = subtype == VSPLUS || subtype == VSLENGTH; |
6937 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; | 6979 | int quotes = (discard ? 0 : (flags & QUOTES_ESC)) | QUOTES_KEEPNUL; |
6938 | int syntax = quoted ? DQSYNTAX : BASESYNTAX; | 6980 | int syntax; |
6939 | 6981 | ||
6940 | sep = quoted ? ((flags & EXP_FULL) << CHAR_BIT) : 0; | 6982 | sep = (flags & EXP_FULL) << CHAR_BIT; |
6983 | syntax = quoted ? DQSYNTAX : BASESYNTAX; | ||
6941 | 6984 | ||
6942 | switch (*name) { | 6985 | switch (*name) { |
6943 | case '$': | 6986 | case '$': |
@@ -6971,21 +7014,21 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6971 | raise_error_syntax("bad substitution"); | 7014 | raise_error_syntax("bad substitution"); |
6972 | #endif | 7015 | #endif |
6973 | break; | 7016 | break; |
6974 | case '@': { | 7017 | case '@': |
7018 | if (quoted && sep) | ||
7019 | goto param; | ||
7020 | /* fall through */ | ||
7021 | case '*': { | ||
6975 | char **ap; | 7022 | char **ap; |
6976 | char sepc; | 7023 | char sepc; |
6977 | 7024 | ||
6978 | if (quoted && (flags & EXP_FULL)) { | 7025 | if (quoted) |
6979 | /* note: this is not meant as PEOF value */ | 7026 | sep = 0; |
6980 | sep = 1 << CHAR_BIT; | 7027 | sep |= ifsset() ? ifsval()[0] : ' '; |
6981 | goto param; | ||
6982 | } | ||
6983 | /* fall through */ | ||
6984 | case '*': | ||
6985 | sep = ifsset() ? (unsigned char)(ifsval()[0]) : ' '; | ||
6986 | param: | 7028 | param: |
6987 | ap = shellparam.p; | ||
6988 | sepc = sep; | 7029 | sepc = sep; |
7030 | *quotedp = !sepc; | ||
7031 | ap = shellparam.p; | ||
6989 | if (!ap) | 7032 | if (!ap) |
6990 | return -1; | 7033 | return -1; |
6991 | while ((p = *ap++) != NULL) { | 7034 | while ((p = *ap++) != NULL) { |
@@ -6997,7 +7040,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
6997 | } | 7040 | } |
6998 | } | 7041 | } |
6999 | break; | 7042 | break; |
7000 | } /* case '@' and '*' */ | 7043 | } /* case '*' */ |
7001 | case '0': | 7044 | case '0': |
7002 | case '1': | 7045 | case '1': |
7003 | case '2': | 7046 | case '2': |
@@ -7069,7 +7112,7 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | |||
7069 | * input string. | 7112 | * input string. |
7070 | */ | 7113 | */ |
7071 | static char * | 7114 | static char * |
7072 | evalvar(char *p, int flags, struct strlist *var_str_list) | 7115 | evalvar(char *p, int flag, struct strlist *var_str_list) |
7073 | { | 7116 | { |
7074 | char varflags; | 7117 | char varflags; |
7075 | char subtype; | 7118 | char subtype; |
@@ -7082,14 +7125,14 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7082 | 7125 | ||
7083 | varflags = (unsigned char) *p++; | 7126 | varflags = (unsigned char) *p++; |
7084 | subtype = varflags & VSTYPE; | 7127 | subtype = varflags & VSTYPE; |
7085 | quoted = flags & EXP_QUOTED; | 7128 | quoted = flag & EXP_QUOTED; |
7086 | var = p; | 7129 | var = p; |
7087 | easy = (!quoted || (*var == '@' && shellparam.nparam)); | 7130 | easy = (!quoted || (*var == '@' && shellparam.nparam)); |
7088 | startloc = expdest - (char *)stackblock(); | 7131 | startloc = expdest - (char *)stackblock(); |
7089 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? | 7132 | p = strchr(p, '=') + 1; //TODO: use var_end(p)? |
7090 | 7133 | ||
7091 | again: | 7134 | again: |
7092 | varlen = varvalue(var, varflags, flags, var_str_list); | 7135 | varlen = varvalue(var, varflags, flag, var_str_list, "ed); |
7093 | if (varflags & VSNUL) | 7136 | if (varflags & VSNUL) |
7094 | varlen--; | 7137 | varlen--; |
7095 | 7138 | ||
@@ -7103,36 +7146,27 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7103 | if (varlen < 0) { | 7146 | if (varlen < 0) { |
7104 | argstr( | 7147 | argstr( |
7105 | p, | 7148 | p, |
7106 | flags | EXP_TILDE | EXP_WORD, | 7149 | flag | EXP_TILDE | EXP_WORD, |
7107 | var_str_list | 7150 | var_str_list |
7108 | ); | 7151 | ); |
7109 | goto end; | 7152 | goto end; |
7110 | } | 7153 | } |
7111 | if (easy) | 7154 | goto record; |
7112 | goto record; | ||
7113 | goto end; | ||
7114 | } | 7155 | } |
7115 | 7156 | ||
7116 | if (subtype == VSASSIGN || subtype == VSQUESTION) { | 7157 | if (subtype == VSASSIGN || subtype == VSQUESTION) { |
7117 | if (varlen < 0) { | 7158 | if (varlen >= 0) |
7118 | if (subevalvar(p, var, /* strloc: */ 0, | ||
7119 | subtype, startloc, varflags, | ||
7120 | /* quotes: */ flags & ~QUOTES_ESC, | ||
7121 | var_str_list) | ||
7122 | ) { | ||
7123 | varflags &= ~VSNUL; | ||
7124 | /* | ||
7125 | * Remove any recorded regions beyond | ||
7126 | * start of variable | ||
7127 | */ | ||
7128 | removerecordregions(startloc); | ||
7129 | goto again; | ||
7130 | } | ||
7131 | goto end; | ||
7132 | } | ||
7133 | if (easy) | ||
7134 | goto record; | 7159 | goto record; |
7135 | goto end; | 7160 | |
7161 | subevalvar(p, var, 0, subtype, startloc, varflags, | ||
7162 | flag & ~QUOTES_ESC, var_str_list); | ||
7163 | varflags &= ~VSNUL; | ||
7164 | /* | ||
7165 | * Remove any recorded regions beyond | ||
7166 | * start of variable | ||
7167 | */ | ||
7168 | removerecordregions(startloc); | ||
7169 | goto again; | ||
7136 | } | 7170 | } |
7137 | 7171 | ||
7138 | if (varlen < 0 && uflag) | 7172 | if (varlen < 0 && uflag) |
@@ -7144,8 +7178,10 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7144 | } | 7178 | } |
7145 | 7179 | ||
7146 | if (subtype == VSNORMAL) { | 7180 | if (subtype == VSNORMAL) { |
7147 | if (easy) | 7181 | record: |
7148 | goto record; | 7182 | if (!easy) |
7183 | goto end; | ||
7184 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
7149 | goto end; | 7185 | goto end; |
7150 | } | 7186 | } |
7151 | 7187 | ||
@@ -7174,7 +7210,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7174 | STPUTC('\0', expdest); | 7210 | STPUTC('\0', expdest); |
7175 | patloc = expdest - (char *)stackblock(); | 7211 | patloc = expdest - (char *)stackblock(); |
7176 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, | 7212 | if (NULL == subevalvar(p, /* varname: */ NULL, patloc, subtype, |
7177 | startloc, varflags, flags, var_str_list)) { | 7213 | startloc, varflags, flag, var_str_list)) { |
7178 | int amount = expdest - ( | 7214 | int amount = expdest - ( |
7179 | (char *)stackblock() + patloc - 1 | 7215 | (char *)stackblock() + patloc - 1 |
7180 | ); | 7216 | ); |
@@ -7182,8 +7218,7 @@ evalvar(char *p, int flags, struct strlist *var_str_list) | |||
7182 | } | 7218 | } |
7183 | /* Remove any recorded regions beyond start of variable */ | 7219 | /* Remove any recorded regions beyond start of variable */ |
7184 | removerecordregions(startloc); | 7220 | removerecordregions(startloc); |
7185 | record: | 7221 | goto record; |
7186 | recordregion(startloc, expdest - (char *)stackblock(), quoted); | ||
7187 | } | 7222 | } |
7188 | 7223 | ||
7189 | end: | 7224 | end: |
@@ -7327,11 +7362,90 @@ addfname(const char *name) | |||
7327 | struct strlist *sp; | 7362 | struct strlist *sp; |
7328 | 7363 | ||
7329 | sp = stzalloc(sizeof(*sp)); | 7364 | sp = stzalloc(sizeof(*sp)); |
7330 | sp->text = ststrdup(name); | 7365 | sp->text = sstrdup(name); |
7331 | *exparg.lastp = sp; | 7366 | *exparg.lastp = sp; |
7332 | exparg.lastp = &sp->next; | 7367 | exparg.lastp = &sp->next; |
7333 | } | 7368 | } |
7334 | 7369 | ||
7370 | /* If we want to use glob() from libc... */ | ||
7371 | #if !ENABLE_ASH_INTERNAL_GLOB | ||
7372 | |||
7373 | /* Add the result of glob() to the list */ | ||
7374 | static void | ||
7375 | addglob(const glob_t *pglob) | ||
7376 | { | ||
7377 | char **p = pglob->gl_pathv; | ||
7378 | |||
7379 | do { | ||
7380 | addfname(*p); | ||
7381 | } while (*++p); | ||
7382 | } | ||
7383 | static void | ||
7384 | expandmeta(struct strlist *str /*, int flag*/) | ||
7385 | { | ||
7386 | /* TODO - EXP_REDIR */ | ||
7387 | |||
7388 | while (str) { | ||
7389 | char *p; | ||
7390 | glob_t pglob; | ||
7391 | int i; | ||
7392 | |||
7393 | if (fflag) | ||
7394 | goto nometa; | ||
7395 | INT_OFF; | ||
7396 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | ||
7397 | // GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match | ||
7398 | // GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?) | ||
7399 | // | ||
7400 | // glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping: | ||
7401 | // if you pass it "file\?", it returns "file\?", not "file?", if no match. | ||
7402 | // Which means you need to unescape the string, right? Not so fast: | ||
7403 | // if there _is_ a file named "file\?" (with backslash), it is returned | ||
7404 | // as "file\?" too (whichever pattern you used to find it, say, "file*"). | ||
7405 | // You DONT KNOW by looking at the result whether you need to unescape it. | ||
7406 | // | ||
7407 | // Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?", | ||
7408 | // returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file. | ||
7409 | // Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match). | ||
7410 | // With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly. | ||
7411 | // i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob); | ||
7412 | // i = glob(p, GLOB_NOMAGIC, NULL, &pglob); | ||
7413 | i = glob(p, 0, NULL, &pglob); | ||
7414 | //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-"); | ||
7415 | if (p != str->text) | ||
7416 | free(p); | ||
7417 | switch (i) { | ||
7418 | case 0: | ||
7419 | #if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR | ||
7420 | /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */ | ||
7421 | if (!(pglob.gl_flags & GLOB_MAGCHAR)) | ||
7422 | goto nometa2; | ||
7423 | #endif | ||
7424 | addglob(&pglob); | ||
7425 | globfree(&pglob); | ||
7426 | INT_ON; | ||
7427 | break; | ||
7428 | case GLOB_NOMATCH: | ||
7429 | //nometa2: | ||
7430 | globfree(&pglob); | ||
7431 | INT_ON; | ||
7432 | nometa: | ||
7433 | *exparg.lastp = str; | ||
7434 | rmescapes(str->text, 0); | ||
7435 | exparg.lastp = &str->next; | ||
7436 | break; | ||
7437 | default: /* GLOB_NOSPACE */ | ||
7438 | globfree(&pglob); | ||
7439 | INT_ON; | ||
7440 | ash_msg_and_raise_error(bb_msg_memory_exhausted); | ||
7441 | } | ||
7442 | str = str->next; | ||
7443 | } | ||
7444 | } | ||
7445 | |||
7446 | #else | ||
7447 | /* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */ | ||
7448 | |||
7335 | /* | 7449 | /* |
7336 | * Do metacharacter (i.e. *, ?, [...]) expansion. | 7450 | * Do metacharacter (i.e. *, ?, [...]) expansion. |
7337 | */ | 7451 | */ |
@@ -7535,7 +7649,8 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7535 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); | 7649 | p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP); |
7536 | { | 7650 | { |
7537 | int i = strlen(str->text); | 7651 | int i = strlen(str->text); |
7538 | expdir = ckmalloc(i < 2048 ? 2048 : i); /* XXX */ | 7652 | //BUGGY estimation of how long expanded name can be |
7653 | expdir = ckmalloc(i < 2048 ? 2048 : i+1); | ||
7539 | } | 7654 | } |
7540 | expmeta(expdir, expdir, p); | 7655 | expmeta(expdir, expdir, p); |
7541 | free(expdir); | 7656 | free(expdir); |
@@ -7560,6 +7675,7 @@ expandmeta(struct strlist *str /*, int flag*/) | |||
7560 | str = str->next; | 7675 | str = str->next; |
7561 | } | 7676 | } |
7562 | } | 7677 | } |
7678 | #endif /* ENABLE_ASH_INTERNAL_GLOB */ | ||
7563 | 7679 | ||
7564 | /* | 7680 | /* |
7565 | * Perform variable substitution and command substitution on an argument, | 7681 | * Perform variable substitution and command substitution on an argument, |
@@ -8126,49 +8242,86 @@ enum { | |||
8126 | }; | 8242 | }; |
8127 | typedef smallint token_id_t; | 8243 | typedef smallint token_id_t; |
8128 | 8244 | ||
8129 | /* first char is indicating which tokens mark the end of a list */ | 8245 | /* Nth bit indicates if token marks the end of a list */ |
8246 | enum { | ||
8247 | tokendlist = 0 | ||
8248 | /* 0 */ | (1u << TEOF) | ||
8249 | /* 1 */ | (0u << TNL) | ||
8250 | /* 2 */ | (0u << TREDIR) | ||
8251 | /* 3 */ | (0u << TWORD) | ||
8252 | /* 4 */ | (0u << TSEMI) | ||
8253 | /* 5 */ | (0u << TBACKGND) | ||
8254 | /* 6 */ | (0u << TAND) | ||
8255 | /* 7 */ | (0u << TOR) | ||
8256 | /* 8 */ | (0u << TPIPE) | ||
8257 | /* 9 */ | (0u << TLP) | ||
8258 | /* 10 */ | (1u << TRP) | ||
8259 | /* 11 */ | (1u << TENDCASE) | ||
8260 | /* 12 */ | (1u << TENDBQUOTE) | ||
8261 | /* 13 */ | (0u << TNOT) | ||
8262 | /* 14 */ | (0u << TCASE) | ||
8263 | /* 15 */ | (1u << TDO) | ||
8264 | /* 16 */ | (1u << TDONE) | ||
8265 | /* 17 */ | (1u << TELIF) | ||
8266 | /* 18 */ | (1u << TELSE) | ||
8267 | /* 19 */ | (1u << TESAC) | ||
8268 | /* 20 */ | (1u << TFI) | ||
8269 | /* 21 */ | (0u << TFOR) | ||
8270 | #if ENABLE_ASH_BASH_COMPAT | ||
8271 | /* 22 */ | (0u << TFUNCTION) | ||
8272 | #endif | ||
8273 | /* 23 */ | (0u << TIF) | ||
8274 | /* 24 */ | (0u << TIN) | ||
8275 | /* 25 */ | (1u << TTHEN) | ||
8276 | /* 26 */ | (0u << TUNTIL) | ||
8277 | /* 27 */ | (0u << TWHILE) | ||
8278 | /* 28 */ | (0u << TBEGIN) | ||
8279 | /* 29 */ | (1u << TEND) | ||
8280 | , /* thus far 29 bits used */ | ||
8281 | }; | ||
8282 | |||
8130 | static const char *const tokname_array[] = { | 8283 | static const char *const tokname_array[] = { |
8131 | "\1end of file", | 8284 | "end of file", |
8132 | "\0newline", | 8285 | "newline", |
8133 | "\0redirection", | 8286 | "redirection", |
8134 | "\0word", | 8287 | "word", |
8135 | "\0;", | 8288 | ";", |
8136 | "\0&", | 8289 | "&", |
8137 | "\0&&", | 8290 | "&&", |
8138 | "\0||", | 8291 | "||", |
8139 | "\0|", | 8292 | "|", |
8140 | "\0(", | 8293 | "(", |
8141 | "\1)", | 8294 | ")", |
8142 | "\1;;", | 8295 | ";;", |
8143 | "\1`", | 8296 | "`", |
8144 | #define KWDOFFSET 13 | 8297 | #define KWDOFFSET 13 |
8145 | /* the following are keywords */ | 8298 | /* the following are keywords */ |
8146 | "\0!", | 8299 | "!", |
8147 | "\0case", | 8300 | "case", |
8148 | "\1do", | 8301 | "do", |
8149 | "\1done", | 8302 | "done", |
8150 | "\1elif", | 8303 | "elif", |
8151 | "\1else", | 8304 | "else", |
8152 | "\1esac", | 8305 | "esac", |
8153 | "\1fi", | 8306 | "fi", |
8154 | "\0for", | 8307 | "for", |
8155 | #if ENABLE_ASH_BASH_COMPAT | 8308 | #if ENABLE_ASH_BASH_COMPAT |
8156 | "\0function", | 8309 | "function", |
8157 | #endif | 8310 | #endif |
8158 | "\0if", | 8311 | "if", |
8159 | "\0in", | 8312 | "in", |
8160 | "\1then", | 8313 | "then", |
8161 | "\0until", | 8314 | "until", |
8162 | "\0while", | 8315 | "while", |
8163 | "\0{", | 8316 | "{", |
8164 | "\1}", | 8317 | "}", |
8165 | }; | 8318 | }; |
8166 | 8319 | ||
8167 | /* Wrapper around strcmp for qsort/bsearch/... */ | 8320 | /* Wrapper around strcmp for qsort/bsearch/... */ |
8168 | static int | 8321 | static int |
8169 | pstrcmp(const void *a, const void *b) | 8322 | pstrcmp(const void *a, const void *b) |
8170 | { | 8323 | { |
8171 | return strcmp((char*) a, (*(char**) b) + 1); | 8324 | return strcmp((char*)a, *(char**)b); |
8172 | } | 8325 | } |
8173 | 8326 | ||
8174 | static const char *const * | 8327 | static const char *const * |
@@ -8297,9 +8450,45 @@ typecmd(int argc UNUSED_PARAM, char **argv) | |||
8297 | } | 8450 | } |
8298 | 8451 | ||
8299 | #if ENABLE_ASH_CMDCMD | 8452 | #if ENABLE_ASH_CMDCMD |
8453 | /* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */ | ||
8454 | static char ** | ||
8455 | parse_command_args(char **argv, const char **path) | ||
8456 | { | ||
8457 | char *cp, c; | ||
8458 | |||
8459 | for (;;) { | ||
8460 | cp = *++argv; | ||
8461 | if (!cp) | ||
8462 | return NULL; | ||
8463 | if (*cp++ != '-') | ||
8464 | break; | ||
8465 | c = *cp++; | ||
8466 | if (!c) | ||
8467 | break; | ||
8468 | if (c == '-' && !*cp) { | ||
8469 | if (!*++argv) | ||
8470 | return NULL; | ||
8471 | break; | ||
8472 | } | ||
8473 | do { | ||
8474 | switch (c) { | ||
8475 | case 'p': | ||
8476 | *path = bb_default_path; | ||
8477 | break; | ||
8478 | default: | ||
8479 | /* run 'typecmd' for other options */ | ||
8480 | return NULL; | ||
8481 | } | ||
8482 | c = *cp++; | ||
8483 | } while (c); | ||
8484 | } | ||
8485 | return argv; | ||
8486 | } | ||
8487 | |||
8300 | static int FAST_FUNC | 8488 | static int FAST_FUNC |
8301 | commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | 8489 | commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) |
8302 | { | 8490 | { |
8491 | char *cmd; | ||
8303 | int c; | 8492 | int c; |
8304 | enum { | 8493 | enum { |
8305 | VERIFY_BRIEF = 1, | 8494 | VERIFY_BRIEF = 1, |
@@ -8307,29 +8496,32 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
8307 | } verify = 0; | 8496 | } verify = 0; |
8308 | const char *path = NULL; | 8497 | const char *path = NULL; |
8309 | 8498 | ||
8499 | /* "command [-p] PROG ARGS" (that is, without -V or -v) | ||
8500 | * never reaches this function. | ||
8501 | */ | ||
8502 | |||
8310 | while ((c = nextopt("pvV")) != '\0') | 8503 | while ((c = nextopt("pvV")) != '\0') |
8311 | if (c == 'V') | 8504 | if (c == 'V') |
8312 | verify |= VERIFY_VERBOSE; | 8505 | verify |= VERIFY_VERBOSE; |
8313 | else if (c == 'v') | 8506 | else if (c == 'v') |
8314 | verify |= VERIFY_BRIEF; | 8507 | /*verify |= VERIFY_BRIEF*/; |
8315 | #if DEBUG | 8508 | #if DEBUG |
8316 | else if (c != 'p') | 8509 | else if (c != 'p') |
8317 | abort(); | 8510 | abort(); |
8318 | #endif | 8511 | #endif |
8319 | else | 8512 | else |
8320 | path = bb_default_path; | 8513 | path = bb_default_path; |
8514 | |||
8321 | /* Mimic bash: just "command -v" doesn't complain, it's a nop */ | 8515 | /* Mimic bash: just "command -v" doesn't complain, it's a nop */ |
8322 | if (verify && (*argptr != NULL)) { | 8516 | cmd = *argptr; |
8323 | return describe_command(*argptr, path, verify - VERIFY_BRIEF); | 8517 | if (/*verify && */ cmd) |
8324 | } | 8518 | return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/); |
8325 | 8519 | ||
8326 | return 0; | 8520 | return 0; |
8327 | } | 8521 | } |
8328 | #endif | 8522 | #endif |
8329 | 8523 | ||
8330 | 8524 | ||
8331 | /* ============ eval.c */ | ||
8332 | |||
8333 | static int funcblocksize; /* size of structures in function */ | 8525 | static int funcblocksize; /* size of structures in function */ |
8334 | static int funcstringsize; /* size of strings in node */ | 8526 | static int funcstringsize; /* size of strings in node */ |
8335 | static void *funcblock; /* block to allocate function from */ | 8527 | static void *funcblock; /* block to allocate function from */ |
@@ -8342,7 +8534,6 @@ static char **nodeptr; | |||
8342 | /* flags in argument to evaltree */ | 8534 | /* flags in argument to evaltree */ |
8343 | #define EV_EXIT 01 /* exit after evaluating tree */ | 8535 | #define EV_EXIT 01 /* exit after evaluating tree */ |
8344 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ | 8536 | #define EV_TESTED 02 /* exit status is checked; ignore -e flag */ |
8345 | #define EV_BACKCMD 04 /* command executing within back quotes */ | ||
8346 | 8537 | ||
8347 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 8538 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { |
8348 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), | 8539 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
@@ -8674,15 +8865,13 @@ defun(char *name, union node *func) | |||
8674 | #define SKIPBREAK (1 << 0) | 8865 | #define SKIPBREAK (1 << 0) |
8675 | #define SKIPCONT (1 << 1) | 8866 | #define SKIPCONT (1 << 1) |
8676 | #define SKIPFUNC (1 << 2) | 8867 | #define SKIPFUNC (1 << 2) |
8677 | #define SKIPFILE (1 << 3) | ||
8678 | #define SKIPEVAL (1 << 4) | ||
8679 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ | 8868 | static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ |
8680 | static int skipcount; /* number of levels to skip */ | 8869 | static int skipcount; /* number of levels to skip */ |
8681 | static int funcnest; /* depth of function calls */ | 8870 | static int funcnest; /* depth of function calls */ |
8682 | static int loopnest; /* current loop nesting level */ | 8871 | static int loopnest; /* current loop nesting level */ |
8683 | 8872 | ||
8684 | /* Forward decl way out to parsing code - dotrap needs it */ | 8873 | /* Forward decl way out to parsing code - dotrap needs it */ |
8685 | static int evalstring(char *s, int mask); | 8874 | static int evalstring(char *s, int flags); |
8686 | 8875 | ||
8687 | /* Called to execute a trap. | 8876 | /* Called to execute a trap. |
8688 | * Single callsite - at the end of evaltree(). | 8877 | * Single callsite - at the end of evaltree(). |
@@ -8691,69 +8880,71 @@ static int evalstring(char *s, int mask); | |||
8691 | * Perhaps we should avoid entering new trap handlers | 8880 | * Perhaps we should avoid entering new trap handlers |
8692 | * while we are executing a trap handler. [is it a TODO?] | 8881 | * while we are executing a trap handler. [is it a TODO?] |
8693 | */ | 8882 | */ |
8694 | static int | 8883 | static void |
8695 | dotrap(void) | 8884 | dotrap(void) |
8696 | { | 8885 | { |
8697 | uint8_t *g; | 8886 | uint8_t *g; |
8698 | int sig; | 8887 | int sig; |
8699 | uint8_t savestatus; | 8888 | uint8_t last_status; |
8889 | |||
8890 | if (!pending_sig) | ||
8891 | return; | ||
8700 | 8892 | ||
8701 | savestatus = exitstatus; | 8893 | last_status = exitstatus; |
8702 | pending_sig = 0; | 8894 | pending_sig = 0; |
8703 | xbarrier(); | 8895 | barrier(); |
8704 | 8896 | ||
8705 | TRACE(("dotrap entered\n")); | 8897 | TRACE(("dotrap entered\n")); |
8706 | for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { | 8898 | for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) { |
8707 | int want_exexit; | 8899 | char *p; |
8708 | char *t; | ||
8709 | 8900 | ||
8710 | if (*g == 0) | 8901 | if (!*g) |
8711 | continue; | 8902 | continue; |
8712 | t = trap[sig]; | 8903 | |
8904 | if (evalskip) { | ||
8905 | pending_sig = sig; | ||
8906 | break; | ||
8907 | } | ||
8908 | |||
8909 | p = trap[sig]; | ||
8713 | /* non-trapped SIGINT is handled separately by raise_interrupt, | 8910 | /* non-trapped SIGINT is handled separately by raise_interrupt, |
8714 | * don't upset it by resetting gotsig[SIGINT-1] */ | 8911 | * don't upset it by resetting gotsig[SIGINT-1] */ |
8715 | if (sig == SIGINT && !t) | 8912 | if (sig == SIGINT && !p) |
8716 | continue; | 8913 | continue; |
8717 | 8914 | ||
8718 | TRACE(("sig %d is active, will run handler '%s'\n", sig, t)); | 8915 | TRACE(("sig %d is active, will run handler '%s'\n", sig, p)); |
8719 | *g = 0; | 8916 | *g = 0; |
8720 | if (!t) | 8917 | if (!p) |
8721 | continue; | 8918 | continue; |
8722 | want_exexit = evalstring(t, SKIPEVAL); | 8919 | evalstring(p, 0); |
8723 | exitstatus = savestatus; | ||
8724 | if (want_exexit) { | ||
8725 | TRACE(("dotrap returns %d\n", want_exexit)); | ||
8726 | return want_exexit; | ||
8727 | } | ||
8728 | } | 8920 | } |
8729 | 8921 | exitstatus = last_status; | |
8730 | TRACE(("dotrap returns 0\n")); | 8922 | TRACE(("dotrap returns\n")); |
8731 | return 0; | ||
8732 | } | 8923 | } |
8733 | 8924 | ||
8734 | /* forward declarations - evaluation is fairly recursive business... */ | 8925 | /* forward declarations - evaluation is fairly recursive business... */ |
8735 | static void evalloop(union node *, int); | 8926 | static int evalloop(union node *, int); |
8736 | static void evalfor(union node *, int); | 8927 | static int evalfor(union node *, int); |
8737 | static void evalcase(union node *, int); | 8928 | static int evalcase(union node *, int); |
8738 | static void evalsubshell(union node *, int); | 8929 | static int evalsubshell(union node *, int); |
8739 | static void expredir(union node *); | 8930 | static void expredir(union node *); |
8740 | static void evalpipe(union node *, int); | 8931 | static int evalpipe(union node *, int); |
8741 | static void evalcommand(union node *, int); | 8932 | static int evalcommand(union node *, int); |
8742 | static int evalbltin(const struct builtincmd *, int, char **); | 8933 | static int evalbltin(const struct builtincmd *, int, char **, int); |
8743 | static void prehash(union node *); | 8934 | static void prehash(union node *); |
8744 | 8935 | ||
8745 | /* | 8936 | /* |
8746 | * Evaluate a parse tree. The value is left in the global variable | 8937 | * Evaluate a parse tree. The value is left in the global variable |
8747 | * exitstatus. | 8938 | * exitstatus. |
8748 | */ | 8939 | */ |
8749 | static void | 8940 | static int |
8750 | evaltree(union node *n, int flags) | 8941 | evaltree(union node *n, int flags) |
8751 | { | 8942 | { |
8752 | struct jmploc *volatile savehandler = exception_handler; | 8943 | struct jmploc *volatile savehandler = exception_handler; |
8753 | struct jmploc jmploc; | 8944 | struct jmploc jmploc; |
8754 | int checkexit = 0; | 8945 | int checkexit = 0; |
8755 | void (*evalfn)(union node *, int); | 8946 | int (*evalfn)(union node *, int); |
8756 | int status; | 8947 | int status = 0; |
8757 | int int_level; | 8948 | int int_level; |
8758 | 8949 | ||
8759 | SAVE_INT(int_level); | 8950 | SAVE_INT(int_level); |
@@ -8764,6 +8955,8 @@ evaltree(union node *n, int flags) | |||
8764 | } | 8955 | } |
8765 | TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); | 8956 | TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags)); |
8766 | 8957 | ||
8958 | dotrap(); | ||
8959 | |||
8767 | exception_handler = &jmploc; | 8960 | exception_handler = &jmploc; |
8768 | { | 8961 | { |
8769 | int err = setjmp(jmploc.loc); | 8962 | int err = setjmp(jmploc.loc); |
@@ -8790,15 +8983,13 @@ evaltree(union node *n, int flags) | |||
8790 | break; | 8983 | break; |
8791 | #endif | 8984 | #endif |
8792 | case NNOT: | 8985 | case NNOT: |
8793 | evaltree(n->nnot.com, EV_TESTED); | 8986 | status = !evaltree(n->nnot.com, EV_TESTED); |
8794 | status = !exitstatus; | ||
8795 | goto setstatus; | 8987 | goto setstatus; |
8796 | case NREDIR: | 8988 | case NREDIR: |
8797 | expredir(n->nredir.redirect); | 8989 | expredir(n->nredir.redirect); |
8798 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); | 8990 | status = redirectsafe(n->nredir.redirect, REDIR_PUSH); |
8799 | if (!status) { | 8991 | if (!status) { |
8800 | evaltree(n->nredir.n, flags & EV_TESTED); | 8992 | status = evaltree(n->nredir.n, flags & EV_TESTED); |
8801 | status = exitstatus; | ||
8802 | } | 8993 | } |
8803 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); | 8994 | popredir(/*drop:*/ 0, /*restore:*/ 0 /* not sure */); |
8804 | goto setstatus; | 8995 | goto setstatus; |
@@ -8816,6 +9007,8 @@ evaltree(union node *n, int flags) | |||
8816 | evalfn = evalloop; | 9007 | evalfn = evalloop; |
8817 | goto calleval; | 9008 | goto calleval; |
8818 | case NSUBSHELL: | 9009 | case NSUBSHELL: |
9010 | evalfn = evalsubshell; | ||
9011 | goto checkexit; | ||
8819 | case NBACKGND: | 9012 | case NBACKGND: |
8820 | evalfn = evalsubshell; | 9013 | evalfn = evalsubshell; |
8821 | goto calleval; | 9014 | goto calleval; |
@@ -8836,27 +9029,24 @@ evaltree(union node *n, int flags) | |||
8836 | #error NOR + 1 != NSEMI | 9029 | #error NOR + 1 != NSEMI |
8837 | #endif | 9030 | #endif |
8838 | unsigned is_or = n->type - NAND; | 9031 | unsigned is_or = n->type - NAND; |
8839 | evaltree( | 9032 | status = evaltree( |
8840 | n->nbinary.ch1, | 9033 | n->nbinary.ch1, |
8841 | (flags | ((is_or >> 1) - 1)) & EV_TESTED | 9034 | (flags | ((is_or >> 1) - 1)) & EV_TESTED |
8842 | ); | 9035 | ); |
8843 | if ((!exitstatus) == is_or) | 9036 | if ((!status) == is_or || evalskip) |
8844 | break; | 9037 | break; |
8845 | if (!evalskip) { | 9038 | n = n->nbinary.ch2; |
8846 | n = n->nbinary.ch2; | ||
8847 | evaln: | 9039 | evaln: |
8848 | evalfn = evaltree; | 9040 | evalfn = evaltree; |
8849 | calleval: | 9041 | calleval: |
8850 | evalfn(n, flags); | 9042 | status = evalfn(n, flags); |
8851 | break; | 9043 | goto setstatus; |
8852 | } | ||
8853 | break; | ||
8854 | } | 9044 | } |
8855 | case NIF: | 9045 | case NIF: |
8856 | evaltree(n->nif.test, EV_TESTED); | 9046 | status = evaltree(n->nif.test, EV_TESTED); |
8857 | if (evalskip) | 9047 | if (evalskip) |
8858 | break; | 9048 | break; |
8859 | if (exitstatus == 0) { | 9049 | if (!status) { |
8860 | n = n->nif.ifpart; | 9050 | n = n->nif.ifpart; |
8861 | goto evaln; | 9051 | goto evaln; |
8862 | } | 9052 | } |
@@ -8864,11 +9054,14 @@ evaltree(union node *n, int flags) | |||
8864 | n = n->nif.elsepart; | 9054 | n = n->nif.elsepart; |
8865 | goto evaln; | 9055 | goto evaln; |
8866 | } | 9056 | } |
8867 | goto success; | 9057 | status = 0; |
9058 | goto setstatus; | ||
8868 | case NDEFUN: | 9059 | case NDEFUN: |
8869 | defun(n->narg.text, n->narg.next); | 9060 | defun(n->narg.text, n->narg.next); |
8870 | success: | 9061 | /* Not necessary. To test it: |
8871 | status = 0; | 9062 | * "false; f() { qwerty; }; echo $?" should print 0. |
9063 | */ | ||
9064 | /* status = 0; */ | ||
8872 | setstatus: | 9065 | setstatus: |
8873 | exitstatus = status; | 9066 | exitstatus = status; |
8874 | break; | 9067 | break; |
@@ -8881,119 +9074,127 @@ evaltree(union node *n, int flags) | |||
8881 | /* Order of checks below is important: | 9074 | /* Order of checks below is important: |
8882 | * signal handlers trigger before exit caused by "set -e". | 9075 | * signal handlers trigger before exit caused by "set -e". |
8883 | */ | 9076 | */ |
8884 | if (pending_sig && dotrap()) | 9077 | dotrap(); |
8885 | goto exexit; | ||
8886 | if (checkexit & exitstatus) | ||
8887 | evalskip |= SKIPEVAL; | ||
8888 | 9078 | ||
8889 | if (flags & EV_EXIT) { | 9079 | if (checkexit & status) |
8890 | exexit: | 9080 | raise_exception(EXEXIT); |
9081 | if (flags & EV_EXIT) | ||
8891 | raise_exception(EXEXIT); | 9082 | raise_exception(EXEXIT); |
8892 | } | ||
8893 | 9083 | ||
8894 | RESTORE_INT(int_level); | 9084 | RESTORE_INT(int_level); |
8895 | TRACE(("leaving evaltree (no interrupts)\n")); | 9085 | TRACE(("leaving evaltree (no interrupts)\n")); |
9086 | |||
9087 | return exitstatus; | ||
8896 | } | 9088 | } |
8897 | 9089 | ||
8898 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) | 9090 | #if !defined(__alpha__) || (defined(__GNUC__) && __GNUC__ >= 3) |
8899 | static | 9091 | static |
8900 | #endif | 9092 | #endif |
8901 | void evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); | 9093 | int evaltreenr(union node *, int) __attribute__ ((alias("evaltree"),__noreturn__)); |
8902 | 9094 | ||
8903 | static void | 9095 | static int |
9096 | skiploop(void) | ||
9097 | { | ||
9098 | int skip = evalskip; | ||
9099 | |||
9100 | switch (skip) { | ||
9101 | case 0: | ||
9102 | break; | ||
9103 | case SKIPBREAK: | ||
9104 | case SKIPCONT: | ||
9105 | if (--skipcount <= 0) { | ||
9106 | evalskip = 0; | ||
9107 | break; | ||
9108 | } | ||
9109 | skip = SKIPBREAK; | ||
9110 | break; | ||
9111 | } | ||
9112 | return skip; | ||
9113 | } | ||
9114 | |||
9115 | static int | ||
8904 | evalloop(union node *n, int flags) | 9116 | evalloop(union node *n, int flags) |
8905 | { | 9117 | { |
9118 | int skip; | ||
8906 | int status; | 9119 | int status; |
8907 | 9120 | ||
8908 | loopnest++; | 9121 | loopnest++; |
8909 | status = 0; | 9122 | status = 0; |
8910 | flags &= EV_TESTED; | 9123 | flags &= EV_TESTED; |
8911 | for (;;) { | 9124 | do { |
8912 | int i; | 9125 | int i; |
8913 | 9126 | ||
8914 | evaltree(n->nbinary.ch1, EV_TESTED); | 9127 | i = evaltree(n->nbinary.ch1, EV_TESTED); |
8915 | if (evalskip) { | 9128 | skip = skiploop(); |
8916 | skipping: | 9129 | if (skip == SKIPFUNC) |
8917 | if (evalskip == SKIPCONT && --skipcount <= 0) { | 9130 | status = i; |
8918 | evalskip = 0; | 9131 | if (skip) |
8919 | continue; | 9132 | continue; |
8920 | } | ||
8921 | if (evalskip == SKIPBREAK && --skipcount <= 0) | ||
8922 | evalskip = 0; | ||
8923 | break; | ||
8924 | } | ||
8925 | i = exitstatus; | ||
8926 | if (n->type != NWHILE) | 9133 | if (n->type != NWHILE) |
8927 | i = !i; | 9134 | i = !i; |
8928 | if (i != 0) | 9135 | if (i != 0) |
8929 | break; | 9136 | break; |
8930 | evaltree(n->nbinary.ch2, flags); | 9137 | status = evaltree(n->nbinary.ch2, flags); |
8931 | status = exitstatus; | 9138 | skip = skiploop(); |
8932 | if (evalskip) | 9139 | } while (!(skip & ~SKIPCONT)); |
8933 | goto skipping; | ||
8934 | } | ||
8935 | loopnest--; | 9140 | loopnest--; |
8936 | exitstatus = status; | 9141 | |
9142 | return status; | ||
8937 | } | 9143 | } |
8938 | 9144 | ||
8939 | static void | 9145 | static int |
8940 | evalfor(union node *n, int flags) | 9146 | evalfor(union node *n, int flags) |
8941 | { | 9147 | { |
8942 | struct arglist arglist; | 9148 | struct arglist arglist; |
8943 | union node *argp; | 9149 | union node *argp; |
8944 | struct strlist *sp; | 9150 | struct strlist *sp; |
8945 | struct stackmark smark; | 9151 | struct stackmark smark; |
9152 | int status = 0; | ||
8946 | 9153 | ||
8947 | setstackmark(&smark); | 9154 | setstackmark(&smark); |
8948 | arglist.list = NULL; | 9155 | arglist.list = NULL; |
8949 | arglist.lastp = &arglist.list; | 9156 | arglist.lastp = &arglist.list; |
8950 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { | 9157 | for (argp = n->nfor.args; argp; argp = argp->narg.next) { |
8951 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); | 9158 | expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); |
8952 | /* XXX */ | ||
8953 | if (evalskip) | ||
8954 | goto out; | ||
8955 | } | 9159 | } |
8956 | *arglist.lastp = NULL; | 9160 | *arglist.lastp = NULL; |
8957 | 9161 | ||
8958 | exitstatus = 0; | ||
8959 | loopnest++; | 9162 | loopnest++; |
8960 | flags &= EV_TESTED; | 9163 | flags &= EV_TESTED; |
8961 | for (sp = arglist.list; sp; sp = sp->next) { | 9164 | for (sp = arglist.list; sp; sp = sp->next) { |
8962 | setvar0(n->nfor.var, sp->text); | 9165 | setvar0(n->nfor.var, sp->text); |
8963 | evaltree(n->nfor.body, flags); | 9166 | status = evaltree(n->nfor.body, flags); |
8964 | if (evalskip) { | 9167 | if (skiploop() & ~SKIPCONT) |
8965 | if (evalskip == SKIPCONT && --skipcount <= 0) { | ||
8966 | evalskip = 0; | ||
8967 | continue; | ||
8968 | } | ||
8969 | if (evalskip == SKIPBREAK && --skipcount <= 0) | ||
8970 | evalskip = 0; | ||
8971 | break; | 9168 | break; |
8972 | } | ||
8973 | } | 9169 | } |
8974 | loopnest--; | 9170 | loopnest--; |
8975 | out: | ||
8976 | popstackmark(&smark); | 9171 | popstackmark(&smark); |
9172 | |||
9173 | return status; | ||
8977 | } | 9174 | } |
8978 | 9175 | ||
8979 | static void | 9176 | static int |
8980 | evalcase(union node *n, int flags) | 9177 | evalcase(union node *n, int flags) |
8981 | { | 9178 | { |
8982 | union node *cp; | 9179 | union node *cp; |
8983 | union node *patp; | 9180 | union node *patp; |
8984 | struct arglist arglist; | 9181 | struct arglist arglist; |
8985 | struct stackmark smark; | 9182 | struct stackmark smark; |
9183 | int status = 0; | ||
8986 | 9184 | ||
8987 | setstackmark(&smark); | 9185 | setstackmark(&smark); |
8988 | arglist.list = NULL; | 9186 | arglist.list = NULL; |
8989 | arglist.lastp = &arglist.list; | 9187 | arglist.lastp = &arglist.list; |
8990 | expandarg(n->ncase.expr, &arglist, EXP_TILDE); | 9188 | expandarg(n->ncase.expr, &arglist, EXP_TILDE); |
8991 | exitstatus = 0; | ||
8992 | for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { | 9189 | for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) { |
8993 | for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { | 9190 | for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) { |
8994 | if (casematch(patp, arglist.list->text)) { | 9191 | if (casematch(patp, arglist.list->text)) { |
8995 | if (evalskip == 0) { | 9192 | /* Ensure body is non-empty as otherwise |
8996 | evaltree(cp->nclist.body, flags); | 9193 | * EV_EXIT may prevent us from setting the |
9194 | * exit status. | ||
9195 | */ | ||
9196 | if (evalskip == 0 && cp->nclist.body) { | ||
9197 | status = evaltree(cp->nclist.body, flags); | ||
8997 | } | 9198 | } |
8998 | goto out; | 9199 | goto out; |
8999 | } | 9200 | } |
@@ -9001,12 +9202,14 @@ evalcase(union node *n, int flags) | |||
9001 | } | 9202 | } |
9002 | out: | 9203 | out: |
9003 | popstackmark(&smark); | 9204 | popstackmark(&smark); |
9205 | |||
9206 | return status; | ||
9004 | } | 9207 | } |
9005 | 9208 | ||
9006 | /* | 9209 | /* |
9007 | * Kick off a subshell to evaluate a tree. | 9210 | * Kick off a subshell to evaluate a tree. |
9008 | */ | 9211 | */ |
9009 | static void | 9212 | static int |
9010 | evalsubshell(union node *n, int flags) | 9213 | evalsubshell(union node *n, int flags) |
9011 | { | 9214 | { |
9012 | IF_PLATFORM_MINGW32(struct forkshell fs;) | 9215 | IF_PLATFORM_MINGW32(struct forkshell fs;) |
@@ -9043,8 +9246,8 @@ evalsubshell(union node *n, int flags) | |||
9043 | status = 0; | 9246 | status = 0; |
9044 | if (!backgnd) | 9247 | if (!backgnd) |
9045 | status = waitforjob(jp); | 9248 | status = waitforjob(jp); |
9046 | exitstatus = status; | ||
9047 | INT_ON; | 9249 | INT_ON; |
9250 | return status; | ||
9048 | } | 9251 | } |
9049 | 9252 | ||
9050 | /* | 9253 | /* |
@@ -9117,7 +9320,7 @@ expredir(union node *n) | |||
9117 | * of the shell, which make the last process in a pipeline the parent | 9320 | * of the shell, which make the last process in a pipeline the parent |
9118 | * of all the rest.) | 9321 | * of all the rest.) |
9119 | */ | 9322 | */ |
9120 | static void | 9323 | static int |
9121 | evalpipe(union node *n, int flags) | 9324 | evalpipe(union node *n, int flags) |
9122 | { | 9325 | { |
9123 | IF_PLATFORM_MINGW32(struct forkshell fs;) | 9326 | IF_PLATFORM_MINGW32(struct forkshell fs;) |
@@ -9126,6 +9329,7 @@ evalpipe(union node *n, int flags) | |||
9126 | int pipelen; | 9329 | int pipelen; |
9127 | int prevfd; | 9330 | int prevfd; |
9128 | int pip[2]; | 9331 | int pip[2]; |
9332 | int status = 0; | ||
9129 | 9333 | ||
9130 | TRACE(("evalpipe(0x%lx) called\n", (long)n)); | 9334 | TRACE(("evalpipe(0x%lx) called\n", (long)n)); |
9131 | pipelen = 0; | 9335 | pipelen = 0; |
@@ -9180,10 +9384,12 @@ evalpipe(union node *n, int flags) | |||
9180 | close(pip[1]); | 9384 | close(pip[1]); |
9181 | } | 9385 | } |
9182 | if (n->npipe.pipe_backgnd == 0) { | 9386 | if (n->npipe.pipe_backgnd == 0) { |
9183 | exitstatus = waitforjob(jp); | 9387 | status = waitforjob(jp); |
9184 | TRACE(("evalpipe: job done exit status %d\n", exitstatus)); | 9388 | TRACE(("evalpipe: job done exit status %d\n", status)); |
9185 | } | 9389 | } |
9186 | INT_ON; | 9390 | INT_ON; |
9391 | |||
9392 | return status; | ||
9187 | } | 9393 | } |
9188 | 9394 | ||
9189 | /* | 9395 | /* |
@@ -9282,12 +9488,12 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9282 | 9488 | ||
9283 | saveparam = shellparam; | 9489 | saveparam = shellparam; |
9284 | savelocalvars = localvars; | 9490 | savelocalvars = localvars; |
9491 | savehandler = exception_handler; | ||
9285 | e = setjmp(jmploc.loc); | 9492 | e = setjmp(jmploc.loc); |
9286 | if (e) { | 9493 | if (e) { |
9287 | goto funcdone; | 9494 | goto funcdone; |
9288 | } | 9495 | } |
9289 | INT_OFF; | 9496 | INT_OFF; |
9290 | savehandler = exception_handler; | ||
9291 | exception_handler = &jmploc; | 9497 | exception_handler = &jmploc; |
9292 | localvars = NULL; | 9498 | localvars = NULL; |
9293 | shellparam.malloced = 0; | 9499 | shellparam.malloced = 0; |
@@ -9315,42 +9521,6 @@ evalfun(struct funcnode *func, int argc, char **argv, int flags) | |||
9315 | return e; | 9521 | return e; |
9316 | } | 9522 | } |
9317 | 9523 | ||
9318 | #if ENABLE_ASH_CMDCMD | ||
9319 | static char ** | ||
9320 | parse_command_args(char **argv, const char **path) | ||
9321 | { | ||
9322 | char *cp, c; | ||
9323 | |||
9324 | for (;;) { | ||
9325 | cp = *++argv; | ||
9326 | if (!cp) | ||
9327 | return NULL; | ||
9328 | if (*cp++ != '-') | ||
9329 | break; | ||
9330 | c = *cp++; | ||
9331 | if (!c) | ||
9332 | break; | ||
9333 | if (c == '-' && !*cp) { | ||
9334 | if (!*++argv) | ||
9335 | return NULL; | ||
9336 | break; | ||
9337 | } | ||
9338 | do { | ||
9339 | switch (c) { | ||
9340 | case 'p': | ||
9341 | *path = bb_default_path; | ||
9342 | break; | ||
9343 | default: | ||
9344 | /* run 'typecmd' for other options */ | ||
9345 | return NULL; | ||
9346 | } | ||
9347 | c = *cp++; | ||
9348 | } while (c); | ||
9349 | } | ||
9350 | return argv; | ||
9351 | } | ||
9352 | #endif | ||
9353 | |||
9354 | /* | 9524 | /* |
9355 | * Make a variable a local variable. When a variable is made local, it's | 9525 | * Make a variable a local variable. When a variable is made local, it's |
9356 | * value and flags are saved in a localvar structure. The saved values | 9526 | * value and flags are saved in a localvar structure. The saved values |
@@ -9458,7 +9628,20 @@ execcmd(int argc UNUSED_PARAM, char **argv) | |||
9458 | iflag = 0; /* exit on error */ | 9628 | iflag = 0; /* exit on error */ |
9459 | mflag = 0; | 9629 | mflag = 0; |
9460 | optschanged(); | 9630 | optschanged(); |
9631 | /* We should set up signals for "exec CMD" | ||
9632 | * the same way as for "CMD" without "exec". | ||
9633 | * But optschanged->setinteractive->setsignal | ||
9634 | * still thought we are a root shell. Therefore, for example, | ||
9635 | * SIGQUIT is still set to IGN. Fix it: | ||
9636 | */ | ||
9637 | shlvl++; | ||
9638 | setsignal(SIGQUIT); | ||
9639 | /*setsignal(SIGTERM); - unnecessary because of iflag=0 */ | ||
9640 | /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */ | ||
9641 | /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */ | ||
9642 | |||
9461 | shellexec(argv + 1, pathval(), 0); | 9643 | shellexec(argv + 1, pathval(), 0); |
9644 | /* NOTREACHED */ | ||
9462 | } | 9645 | } |
9463 | return 0; | 9646 | return 0; |
9464 | } | 9647 | } |
@@ -9473,14 +9656,14 @@ returncmd(int argc UNUSED_PARAM, char **argv) | |||
9473 | * If called outside a function, do what ksh does; | 9656 | * If called outside a function, do what ksh does; |
9474 | * skip the rest of the file. | 9657 | * skip the rest of the file. |
9475 | */ | 9658 | */ |
9476 | evalskip = funcnest ? SKIPFUNC : SKIPFILE; | 9659 | evalskip = SKIPFUNC; |
9477 | return argv[1] ? number(argv[1]) : exitstatus; | 9660 | return argv[1] ? number(argv[1]) : exitstatus; |
9478 | } | 9661 | } |
9479 | 9662 | ||
9480 | /* Forward declarations for builtintab[] */ | 9663 | /* Forward declarations for builtintab[] */ |
9481 | static int breakcmd(int, char **) FAST_FUNC; | 9664 | static int breakcmd(int, char **) FAST_FUNC; |
9482 | static int dotcmd(int, char **) FAST_FUNC; | 9665 | static int dotcmd(int, char **) FAST_FUNC; |
9483 | static int evalcmd(int, char **) FAST_FUNC; | 9666 | static int evalcmd(int, char **, int) FAST_FUNC; |
9484 | static int exitcmd(int, char **) FAST_FUNC; | 9667 | static int exitcmd(int, char **) FAST_FUNC; |
9485 | static int exportcmd(int, char **) FAST_FUNC; | 9668 | static int exportcmd(int, char **) FAST_FUNC; |
9486 | #if ENABLE_ASH_GETOPTS | 9669 | #if ENABLE_ASH_GETOPTS |
@@ -9550,7 +9733,7 @@ static const struct builtincmd builtintab[] = { | |||
9550 | #if ENABLE_ASH_BUILTIN_ECHO | 9733 | #if ENABLE_ASH_BUILTIN_ECHO |
9551 | { BUILTIN_REGULAR "echo" , echocmd }, | 9734 | { BUILTIN_REGULAR "echo" , echocmd }, |
9552 | #endif | 9735 | #endif |
9553 | { BUILTIN_SPEC_REG "eval" , evalcmd }, | 9736 | { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/ |
9554 | { BUILTIN_SPEC_REG "exec" , execcmd }, | 9737 | { BUILTIN_SPEC_REG "exec" , execcmd }, |
9555 | { BUILTIN_SPEC_REG "exit" , exitcmd }, | 9738 | { BUILTIN_SPEC_REG "exit" , exitcmd }, |
9556 | { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, | 9739 | { BUILTIN_SPEC_REG_ASSG "export" , exportcmd }, |
@@ -9606,27 +9789,28 @@ static const struct builtincmd builtintab[] = { | |||
9606 | 9789 | ||
9607 | /* Should match the above table! */ | 9790 | /* Should match the above table! */ |
9608 | #define COMMANDCMD (builtintab + \ | 9791 | #define COMMANDCMD (builtintab + \ |
9609 | 2 + \ | 9792 | /* . : */ 2 + \ |
9610 | 1 * ENABLE_ASH_BUILTIN_TEST + \ | 9793 | /* [ */ 1 * ENABLE_ASH_BUILTIN_TEST + \ |
9611 | 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ | 9794 | /* [[ */ 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ |
9612 | 1 * ENABLE_ASH_ALIAS + \ | 9795 | /* alias */ 1 * ENABLE_ASH_ALIAS + \ |
9613 | 1 * ENABLE_ASH_JOB_CONTROL + \ | 9796 | /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ |
9614 | 3) | 9797 | /* break cd cddir */ 3) |
9615 | #define EXECCMD (builtintab + \ | 9798 | #define EVALCMD (COMMANDCMD + \ |
9616 | 2 + \ | 9799 | /* command */ 1 * ENABLE_ASH_CMDCMD + \ |
9617 | 1 * ENABLE_ASH_BUILTIN_TEST + \ | 9800 | /* continue */ 1 + \ |
9618 | 1 * ENABLE_ASH_BUILTIN_TEST * ENABLE_ASH_BASH_COMPAT + \ | 9801 | /* echo */ 1 * ENABLE_ASH_BUILTIN_ECHO + \ |
9619 | 1 * ENABLE_ASH_ALIAS + \ | 9802 | 0) |
9620 | 1 * ENABLE_ASH_JOB_CONTROL + \ | 9803 | #define EXECCMD (EVALCMD + \ |
9621 | 3 + \ | 9804 | /* eval */ 1) |
9622 | 1 * ENABLE_ASH_CMDCMD + \ | ||
9623 | 1 + \ | ||
9624 | ENABLE_ASH_BUILTIN_ECHO + \ | ||
9625 | 1) | ||
9626 | 9805 | ||
9627 | /* | 9806 | /* |
9628 | * Search the table of builtin commands. | 9807 | * Search the table of builtin commands. |
9629 | */ | 9808 | */ |
9809 | static int | ||
9810 | pstrcmp1(const void *a, const void *b) | ||
9811 | { | ||
9812 | return strcmp((char*)a, *(char**)b + 1); | ||
9813 | } | ||
9630 | static struct builtincmd * | 9814 | static struct builtincmd * |
9631 | find_builtin(const char *name) | 9815 | find_builtin(const char *name) |
9632 | { | 9816 | { |
@@ -9634,7 +9818,7 @@ find_builtin(const char *name) | |||
9634 | 9818 | ||
9635 | bp = bsearch( | 9819 | bp = bsearch( |
9636 | name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), | 9820 | name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]), |
9637 | pstrcmp | 9821 | pstrcmp1 |
9638 | ); | 9822 | ); |
9639 | return bp; | 9823 | return bp; |
9640 | } | 9824 | } |
@@ -9658,7 +9842,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
9658 | return back_exitstatus; | 9842 | return back_exitstatus; |
9659 | } | 9843 | } |
9660 | 9844 | ||
9661 | static void | 9845 | static int |
9662 | evalcommand(union node *cmd, int flags) | 9846 | evalcommand(union node *cmd, int flags) |
9663 | { | 9847 | { |
9664 | static const struct builtincmd null_bltin = { | 9848 | static const struct builtincmd null_bltin = { |
@@ -9794,6 +9978,9 @@ evalcommand(union node *cmd, int flags) | |||
9794 | nargv = parse_command_args(argv, &path); | 9978 | nargv = parse_command_args(argv, &path); |
9795 | if (!nargv) | 9979 | if (!nargv) |
9796 | break; | 9980 | break; |
9981 | /* It's "command [-p] PROG ARGS" (that is, no -Vv). | ||
9982 | * nargv => "PROG". path is updated if -p. | ||
9983 | */ | ||
9797 | argc -= nargv - argv; | 9984 | argc -= nargv - argv; |
9798 | argv = nargv; | 9985 | argv = nargv; |
9799 | cmd_flag |= DO_NOFUNC; | 9986 | cmd_flag |= DO_NOFUNC; |
@@ -9862,9 +10049,9 @@ evalcommand(union node *cmd, int flags) | |||
9862 | jp = makejob(/*cmd,*/ 1); | 10049 | jp = makejob(/*cmd,*/ 1); |
9863 | if (forkshell(jp, cmd, FORK_FG) != 0) { | 10050 | if (forkshell(jp, cmd, FORK_FG) != 0) { |
9864 | /* parent */ | 10051 | /* parent */ |
9865 | exitstatus = waitforjob(jp); | 10052 | status = waitforjob(jp); |
9866 | INT_ON; | 10053 | INT_ON; |
9867 | TRACE(("forked child exited with %d\n", exitstatus)); | 10054 | TRACE(("forked child exited with %d\n", status)); |
9868 | break; | 10055 | break; |
9869 | } | 10056 | } |
9870 | /* child */ | 10057 | /* child */ |
@@ -9894,7 +10081,7 @@ evalcommand(union node *cmd, int flags) | |||
9894 | * to reap the zombie and make kill detect that it's gone: */ | 10081 | * to reap the zombie and make kill detect that it's gone: */ |
9895 | dowait(DOWAIT_NONBLOCK, NULL); | 10082 | dowait(DOWAIT_NONBLOCK, NULL); |
9896 | 10083 | ||
9897 | if (evalbltin(cmdentry.u.cmd, argc, argv)) { | 10084 | if (evalbltin(cmdentry.u.cmd, argc, argv, flags)) { |
9898 | int exit_status; | 10085 | int exit_status; |
9899 | int i = exception_type; | 10086 | int i = exception_type; |
9900 | if (i == EXEXIT || i == EXEXEC) | 10087 | if (i == EXEXIT || i == EXEXEC) |
@@ -9911,7 +10098,7 @@ evalcommand(union node *cmd, int flags) | |||
9911 | } | 10098 | } |
9912 | FORCE_INT_ON; | 10099 | FORCE_INT_ON; |
9913 | } | 10100 | } |
9914 | break; | 10101 | goto readstatus; |
9915 | 10102 | ||
9916 | case CMDFUNCTION: | 10103 | case CMDFUNCTION: |
9917 | listsetvar(varlist.list, 0); | 10104 | listsetvar(varlist.list, 0); |
@@ -9919,6 +10106,8 @@ evalcommand(union node *cmd, int flags) | |||
9919 | dowait(DOWAIT_NONBLOCK, NULL); | 10106 | dowait(DOWAIT_NONBLOCK, NULL); |
9920 | if (evalfun(cmdentry.u.func, argc, argv, flags)) | 10107 | if (evalfun(cmdentry.u.func, argc, argv, flags)) |
9921 | goto raise; | 10108 | goto raise; |
10109 | readstatus: | ||
10110 | status = exitstatus; | ||
9922 | break; | 10111 | break; |
9923 | } /* switch */ | 10112 | } /* switch */ |
9924 | 10113 | ||
@@ -9932,29 +10121,36 @@ evalcommand(union node *cmd, int flags) | |||
9932 | setvar0("_", lastarg); | 10121 | setvar0("_", lastarg); |
9933 | } | 10122 | } |
9934 | popstackmark(&smark); | 10123 | popstackmark(&smark); |
10124 | |||
10125 | return status; | ||
9935 | } | 10126 | } |
9936 | 10127 | ||
9937 | static int | 10128 | static int |
9938 | evalbltin(const struct builtincmd *cmd, int argc, char **argv) | 10129 | evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags) |
9939 | { | 10130 | { |
9940 | char *volatile savecmdname; | 10131 | char *volatile savecmdname; |
9941 | struct jmploc *volatile savehandler; | 10132 | struct jmploc *volatile savehandler; |
9942 | struct jmploc jmploc; | 10133 | struct jmploc jmploc; |
10134 | int status; | ||
9943 | int i; | 10135 | int i; |
9944 | 10136 | ||
9945 | savecmdname = commandname; | 10137 | savecmdname = commandname; |
10138 | savehandler = exception_handler; | ||
9946 | i = setjmp(jmploc.loc); | 10139 | i = setjmp(jmploc.loc); |
9947 | if (i) | 10140 | if (i) |
9948 | goto cmddone; | 10141 | goto cmddone; |
9949 | savehandler = exception_handler; | ||
9950 | exception_handler = &jmploc; | 10142 | exception_handler = &jmploc; |
9951 | commandname = argv[0]; | 10143 | commandname = argv[0]; |
9952 | argptr = argv + 1; | 10144 | argptr = argv + 1; |
9953 | optptr = NULL; /* initialize nextopt */ | 10145 | optptr = NULL; /* initialize nextopt */ |
9954 | exitstatus = (*cmd->builtin)(argc, argv); | 10146 | if (cmd == EVALCMD) |
10147 | status = evalcmd(argc, argv, flags); | ||
10148 | else | ||
10149 | status = (*cmd->builtin)(argc, argv); | ||
9955 | flush_stdout_stderr(); | 10150 | flush_stdout_stderr(); |
10151 | status |= ferror(stdout); | ||
10152 | exitstatus = status; | ||
9956 | cmddone: | 10153 | cmddone: |
9957 | exitstatus |= ferror(stdout); | ||
9958 | clearerr(stdout); | 10154 | clearerr(stdout); |
9959 | commandname = savecmdname; | 10155 | commandname = savecmdname; |
9960 | exception_handler = savehandler; | 10156 | exception_handler = savehandler; |
@@ -10058,6 +10254,8 @@ pushstring(char *s, struct alias *ap) | |||
10058 | g_parsefile->strpush = sp; | 10254 | g_parsefile->strpush = sp; |
10059 | sp->prev_string = g_parsefile->next_to_pgetc; | 10255 | sp->prev_string = g_parsefile->next_to_pgetc; |
10060 | sp->prev_left_in_line = g_parsefile->left_in_line; | 10256 | sp->prev_left_in_line = g_parsefile->left_in_line; |
10257 | sp->unget = g_parsefile->unget; | ||
10258 | memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc)); | ||
10061 | #if ENABLE_ASH_ALIAS | 10259 | #if ENABLE_ASH_ALIAS |
10062 | sp->ap = ap; | 10260 | sp->ap = ap; |
10063 | if (ap) { | 10261 | if (ap) { |
@@ -10067,6 +10265,7 @@ pushstring(char *s, struct alias *ap) | |||
10067 | #endif | 10265 | #endif |
10068 | g_parsefile->next_to_pgetc = s; | 10266 | g_parsefile->next_to_pgetc = s; |
10069 | g_parsefile->left_in_line = len; | 10267 | g_parsefile->left_in_line = len; |
10268 | g_parsefile->unget = 0; | ||
10070 | INT_ON; | 10269 | INT_ON; |
10071 | } | 10270 | } |
10072 | 10271 | ||
@@ -10094,17 +10293,14 @@ popstring(void) | |||
10094 | #endif | 10293 | #endif |
10095 | g_parsefile->next_to_pgetc = sp->prev_string; | 10294 | g_parsefile->next_to_pgetc = sp->prev_string; |
10096 | g_parsefile->left_in_line = sp->prev_left_in_line; | 10295 | g_parsefile->left_in_line = sp->prev_left_in_line; |
10296 | g_parsefile->unget = sp->unget; | ||
10297 | memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc)); | ||
10097 | g_parsefile->strpush = sp->prev; | 10298 | g_parsefile->strpush = sp->prev; |
10098 | if (sp != &(g_parsefile->basestrpush)) | 10299 | if (sp != &(g_parsefile->basestrpush)) |
10099 | free(sp); | 10300 | free(sp); |
10100 | INT_ON; | 10301 | INT_ON; |
10101 | } | 10302 | } |
10102 | 10303 | ||
10103 | //FIXME: BASH_COMPAT with "...&" does TWO pungetc(): | ||
10104 | //it peeks whether it is &>, and then pushes back both chars. | ||
10105 | //This function needs to save last *next_to_pgetc to buf[0] | ||
10106 | //to make two pungetc() reliable. Currently, | ||
10107 | // pgetc (out of buf: does preadfd), pgetc, pungetc, pungetc won't work... | ||
10108 | static int | 10304 | static int |
10109 | preadfd(void) | 10305 | preadfd(void) |
10110 | { | 10306 | { |
@@ -10189,13 +10385,14 @@ preadfd(void) | |||
10189 | */ | 10385 | */ |
10190 | //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) | 10386 | //#define pgetc_debug(...) bb_error_msg(__VA_ARGS__) |
10191 | #define pgetc_debug(...) ((void)0) | 10387 | #define pgetc_debug(...) ((void)0) |
10388 | static int pgetc(void); | ||
10192 | static int | 10389 | static int |
10193 | preadbuffer(void) | 10390 | preadbuffer(void) |
10194 | { | 10391 | { |
10195 | char *q; | 10392 | char *q; |
10196 | int more; | 10393 | int more; |
10197 | 10394 | ||
10198 | while (g_parsefile->strpush) { | 10395 | if (g_parsefile->strpush) { |
10199 | #if ENABLE_ASH_ALIAS | 10396 | #if ENABLE_ASH_ALIAS |
10200 | if (g_parsefile->left_in_line == -1 | 10397 | if (g_parsefile->left_in_line == -1 |
10201 | && g_parsefile->strpush->ap | 10398 | && g_parsefile->strpush->ap |
@@ -10207,13 +10404,7 @@ preadbuffer(void) | |||
10207 | } | 10404 | } |
10208 | #endif | 10405 | #endif |
10209 | popstring(); | 10406 | popstring(); |
10210 | /* try "pgetc" now: */ | 10407 | return pgetc(); |
10211 | pgetc_debug("preadbuffer internal pgetc at %d:%p'%s'", | ||
10212 | g_parsefile->left_in_line, | ||
10213 | g_parsefile->next_to_pgetc, | ||
10214 | g_parsefile->next_to_pgetc); | ||
10215 | if (--g_parsefile->left_in_line >= 0) | ||
10216 | return (unsigned char)(*g_parsefile->next_to_pgetc++); | ||
10217 | } | 10408 | } |
10218 | /* on both branches above g_parsefile->left_in_line < 0. | 10409 | /* on both branches above g_parsefile->left_in_line < 0. |
10219 | * "pgetc" needs refilling. | 10410 | * "pgetc" needs refilling. |
@@ -10292,27 +10483,41 @@ preadbuffer(void) | |||
10292 | return (unsigned char)*g_parsefile->next_to_pgetc++; | 10483 | return (unsigned char)*g_parsefile->next_to_pgetc++; |
10293 | } | 10484 | } |
10294 | 10485 | ||
10295 | #define pgetc_as_macro() \ | 10486 | static void |
10296 | (--g_parsefile->left_in_line >= 0 \ | 10487 | nlprompt(void) |
10297 | ? (unsigned char)*g_parsefile->next_to_pgetc++ \ | 10488 | { |
10298 | : preadbuffer() \ | 10489 | g_parsefile->linno++; |
10299 | ) | 10490 | setprompt_if(doprompt, 2); |
10491 | } | ||
10492 | static void | ||
10493 | nlnoprompt(void) | ||
10494 | { | ||
10495 | g_parsefile->linno++; | ||
10496 | needprompt = doprompt; | ||
10497 | } | ||
10300 | 10498 | ||
10301 | static int | 10499 | static int |
10302 | pgetc(void) | 10500 | pgetc(void) |
10303 | { | 10501 | { |
10304 | pgetc_debug("pgetc_fast at %d:%p'%s'", | 10502 | int c; |
10503 | |||
10504 | pgetc_debug("pgetc at %d:%p'%s'", | ||
10305 | g_parsefile->left_in_line, | 10505 | g_parsefile->left_in_line, |
10306 | g_parsefile->next_to_pgetc, | 10506 | g_parsefile->next_to_pgetc, |
10307 | g_parsefile->next_to_pgetc); | 10507 | g_parsefile->next_to_pgetc); |
10308 | return pgetc_as_macro(); | 10508 | if (g_parsefile->unget) |
10309 | } | 10509 | return g_parsefile->lastc[--g_parsefile->unget]; |
10310 | 10510 | ||
10311 | #if ENABLE_ASH_OPTIMIZE_FOR_SIZE | 10511 | if (--g_parsefile->left_in_line >= 0) |
10312 | # define pgetc_fast() pgetc() | 10512 | c = (signed char)*g_parsefile->next_to_pgetc++; |
10313 | #else | 10513 | else |
10314 | # define pgetc_fast() pgetc_as_macro() | 10514 | c = preadbuffer(); |
10315 | #endif | 10515 | |
10516 | g_parsefile->lastc[1] = g_parsefile->lastc[0]; | ||
10517 | g_parsefile->lastc[0] = c; | ||
10518 | |||
10519 | return c; | ||
10520 | } | ||
10316 | 10521 | ||
10317 | #if ENABLE_ASH_ALIAS | 10522 | #if ENABLE_ASH_ALIAS |
10318 | static int | 10523 | static int |
@@ -10320,11 +10525,11 @@ pgetc_without_PEOA(void) | |||
10320 | { | 10525 | { |
10321 | int c; | 10526 | int c; |
10322 | do { | 10527 | do { |
10323 | pgetc_debug("pgetc_fast at %d:%p'%s'", | 10528 | pgetc_debug("pgetc at %d:%p'%s'", |
10324 | g_parsefile->left_in_line, | 10529 | g_parsefile->left_in_line, |
10325 | g_parsefile->next_to_pgetc, | 10530 | g_parsefile->next_to_pgetc, |
10326 | g_parsefile->next_to_pgetc); | 10531 | g_parsefile->next_to_pgetc); |
10327 | c = pgetc_fast(); | 10532 | c = pgetc(); |
10328 | } while (c == PEOA); | 10533 | } while (c == PEOA); |
10329 | return c; | 10534 | return c; |
10330 | } | 10535 | } |
@@ -10358,18 +10563,31 @@ pfgets(char *line, int len) | |||
10358 | } | 10563 | } |
10359 | 10564 | ||
10360 | /* | 10565 | /* |
10361 | * Undo the last call to pgetc. Only one character may be pushed back. | 10566 | * Undo a call to pgetc. Only two characters may be pushed back. |
10362 | * PEOF may be pushed back. | 10567 | * PEOF may be pushed back. |
10363 | */ | 10568 | */ |
10364 | static void | 10569 | static void |
10365 | pungetc(void) | 10570 | pungetc(void) |
10366 | { | 10571 | { |
10367 | g_parsefile->left_in_line++; | 10572 | g_parsefile->unget++; |
10368 | g_parsefile->next_to_pgetc--; | 10573 | } |
10369 | pgetc_debug("pushed back to %d:%p'%s'", | 10574 | |
10370 | g_parsefile->left_in_line, | 10575 | /* This one eats backslash+newline */ |
10371 | g_parsefile->next_to_pgetc, | 10576 | static int |
10372 | g_parsefile->next_to_pgetc); | 10577 | pgetc_eatbnl(void) |
10578 | { | ||
10579 | int c; | ||
10580 | |||
10581 | while ((c = pgetc()) == '\\') { | ||
10582 | if (pgetc() != '\n') { | ||
10583 | pungetc(); | ||
10584 | break; | ||
10585 | } | ||
10586 | |||
10587 | nlprompt(); | ||
10588 | } | ||
10589 | |||
10590 | return c; | ||
10373 | } | 10591 | } |
10374 | 10592 | ||
10375 | /* | 10593 | /* |
@@ -10386,6 +10604,7 @@ pushfile(void) | |||
10386 | pf->pf_fd = -1; | 10604 | pf->pf_fd = -1; |
10387 | /*pf->strpush = NULL; - ckzalloc did it */ | 10605 | /*pf->strpush = NULL; - ckzalloc did it */ |
10388 | /*pf->basestrpush.prev = NULL;*/ | 10606 | /*pf->basestrpush.prev = NULL;*/ |
10607 | /*pf->unget = 0;*/ | ||
10389 | g_parsefile = pf; | 10608 | g_parsefile = pf; |
10390 | } | 10609 | } |
10391 | 10610 | ||
@@ -10959,8 +11178,8 @@ static const char * | |||
10959 | tokname(char *buf, int tok) | 11178 | tokname(char *buf, int tok) |
10960 | { | 11179 | { |
10961 | if (tok < TSEMI) | 11180 | if (tok < TSEMI) |
10962 | return tokname_array[tok] + 1; | 11181 | return tokname_array[tok]; |
10963 | sprintf(buf, "\"%s\"", tokname_array[tok] + 1); | 11182 | sprintf(buf, "\"%s\"", tokname_array[tok]); |
10964 | return buf; | 11183 | return buf; |
10965 | } | 11184 | } |
10966 | 11185 | ||
@@ -11017,7 +11236,7 @@ list(int nlflag) | |||
11017 | } | 11236 | } |
11018 | 11237 | ||
11019 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | 11238 | checkkwd = CHKNL | CHKKWD | CHKALIAS; |
11020 | if (nlflag == 2 && tokname_array[peektoken()][0]) | 11239 | if (nlflag == 2 && ((1 << peektoken()) & tokendlist)) |
11021 | return n1; | 11240 | return n1; |
11022 | nlflag |= 2; | 11241 | nlflag |= 2; |
11023 | 11242 | ||
@@ -11399,7 +11618,7 @@ parse_command(void) | |||
11399 | n1->nbinary.ch1 = list(0); | 11618 | n1->nbinary.ch1 = list(0); |
11400 | got = readtoken(); | 11619 | got = readtoken(); |
11401 | if (got != TDO) { | 11620 | if (got != TDO) { |
11402 | TRACE(("expecting DO got '%s' %s\n", tokname_array[got] + 1, | 11621 | TRACE(("expecting DO got '%s' %s\n", tokname_array[got], |
11403 | got == TWORD ? wordtext : "")); | 11622 | got == TWORD ? wordtext : "")); |
11404 | raise_error_unexpected_syntax(TDO); | 11623 | raise_error_unexpected_syntax(TDO); |
11405 | } | 11624 | } |
@@ -11545,7 +11764,8 @@ parse_command(void) | |||
11545 | } | 11764 | } |
11546 | 11765 | ||
11547 | #if ENABLE_ASH_BASH_COMPAT | 11766 | #if ENABLE_ASH_BASH_COMPAT |
11548 | static int decode_dollar_squote(void) | 11767 | static int |
11768 | decode_dollar_squote(void) | ||
11549 | { | 11769 | { |
11550 | static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; | 11770 | static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567"; |
11551 | int c, cnt; | 11771 | int c, cnt; |
@@ -11613,7 +11833,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11613 | /* NB: syntax parameter fits into smallint */ | 11833 | /* NB: syntax parameter fits into smallint */ |
11614 | /* c parameter is an unsigned char or PEOF or PEOA */ | 11834 | /* c parameter is an unsigned char or PEOF or PEOA */ |
11615 | char *out; | 11835 | char *out; |
11616 | int len; | 11836 | size_t len; |
11617 | char line[EOFMARKLEN + 1]; | 11837 | char line[EOFMARKLEN + 1]; |
11618 | struct nodelist *bqlist; | 11838 | struct nodelist *bqlist; |
11619 | smallint quotef; | 11839 | smallint quotef; |
@@ -11656,25 +11876,31 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11656 | if (syntax == BASESYNTAX) | 11876 | if (syntax == BASESYNTAX) |
11657 | goto endword; /* exit outer loop */ | 11877 | goto endword; /* exit outer loop */ |
11658 | USTPUTC(c, out); | 11878 | USTPUTC(c, out); |
11659 | g_parsefile->linno++; | 11879 | nlprompt(); |
11660 | setprompt_if(doprompt, 2); | ||
11661 | c = pgetc(); | 11880 | c = pgetc(); |
11662 | goto loop; /* continue outer loop */ | 11881 | goto loop; /* continue outer loop */ |
11663 | case CWORD: | 11882 | case CWORD: |
11664 | USTPUTC(c, out); | 11883 | USTPUTC(c, out); |
11665 | break; | 11884 | break; |
11666 | case CCTL: | 11885 | case CCTL: |
11667 | if (eofmark == NULL || dblquote) | ||
11668 | USTPUTC(CTLESC, out); | ||
11669 | #if ENABLE_ASH_BASH_COMPAT | 11886 | #if ENABLE_ASH_BASH_COMPAT |
11670 | if (c == '\\' && bash_dollar_squote) { | 11887 | if (c == '\\' && bash_dollar_squote) { |
11671 | c = decode_dollar_squote(); | 11888 | c = decode_dollar_squote(); |
11889 | if (c == '\0') { | ||
11890 | /* skip $'\000', $'\x00' (like bash) */ | ||
11891 | break; | ||
11892 | } | ||
11672 | if (c & 0x100) { | 11893 | if (c & 0x100) { |
11673 | USTPUTC('\\', out); | 11894 | /* Unknown escape. Encode as '\z' */ |
11674 | c = (unsigned char)c; | 11895 | c = (unsigned char)c; |
11896 | if (eofmark == NULL || dblquote) | ||
11897 | USTPUTC(CTLESC, out); | ||
11898 | USTPUTC('\\', out); | ||
11675 | } | 11899 | } |
11676 | } | 11900 | } |
11677 | #endif | 11901 | #endif |
11902 | if (eofmark == NULL || dblquote) | ||
11903 | USTPUTC(CTLESC, out); | ||
11678 | USTPUTC(c, out); | 11904 | USTPUTC(c, out); |
11679 | break; | 11905 | break; |
11680 | case CBACK: /* backslash */ | 11906 | case CBACK: /* backslash */ |
@@ -11684,7 +11910,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11684 | USTPUTC('\\', out); | 11910 | USTPUTC('\\', out); |
11685 | pungetc(); | 11911 | pungetc(); |
11686 | } else if (c == '\n') { | 11912 | } else if (c == '\n') { |
11687 | setprompt_if(doprompt, 2); | 11913 | nlprompt(); |
11688 | } else { | 11914 | } else { |
11689 | #if ENABLE_ASH_EXPAND_PRMT | 11915 | #if ENABLE_ASH_EXPAND_PRMT |
11690 | if (c == '$' && pssyntax) { | 11916 | if (c == '$' && pssyntax) { |
@@ -11752,7 +11978,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11752 | if (parenlevel > 0) { | 11978 | if (parenlevel > 0) { |
11753 | parenlevel--; | 11979 | parenlevel--; |
11754 | } else { | 11980 | } else { |
11755 | if (pgetc() == ')') { | 11981 | if (pgetc_eatbnl() == ')') { |
11756 | c = CTLENDARI; | 11982 | c = CTLENDARI; |
11757 | if (--arinest == 0) { | 11983 | if (--arinest == 0) { |
11758 | syntax = prevsyntax; | 11984 | syntax = prevsyntax; |
@@ -11779,6 +12005,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11779 | if (varnest == 0) { | 12005 | if (varnest == 0) { |
11780 | #if ENABLE_ASH_BASH_COMPAT | 12006 | #if ENABLE_ASH_BASH_COMPAT |
11781 | if (c == '&') { | 12007 | if (c == '&') { |
12008 | //Can't call pgetc_eatbnl() here, this requires three-deep pungetc() | ||
11782 | if (pgetc() == '>') | 12009 | if (pgetc() == '>') |
11783 | c = 0x100 + '>'; /* flag &> */ | 12010 | c = 0x100 + '>'; /* flag &> */ |
11784 | pungetc(); | 12011 | pungetc(); |
@@ -11789,7 +12016,7 @@ readtoken1(int c, int syntax, char *eofmark, int striptabs) | |||
11789 | IF_ASH_ALIAS(if (c != PEOA)) | 12016 | IF_ASH_ALIAS(if (c != PEOA)) |
11790 | USTPUTC(c, out); | 12017 | USTPUTC(c, out); |
11791 | } | 12018 | } |
11792 | c = pgetc_fast(); | 12019 | c = pgetc(); |
11793 | } /* for (;;) */ | 12020 | } /* for (;;) */ |
11794 | endword: | 12021 | endword: |
11795 | 12022 | ||
@@ -11854,8 +12081,7 @@ checkend: { | |||
11854 | continue; | 12081 | continue; |
11855 | if (*p == '\n' && *q == '\0') { | 12082 | if (*p == '\n' && *q == '\0') { |
11856 | c = PEOF; | 12083 | c = PEOF; |
11857 | g_parsefile->linno++; | 12084 | nlnoprompt(); |
11858 | needprompt = doprompt; | ||
11859 | } else { | 12085 | } else { |
11860 | pushstring(line, NULL); | 12086 | pushstring(line, NULL); |
11861 | } | 12087 | } |
@@ -11954,7 +12180,7 @@ parsesub: { | |||
11954 | int typeloc; | 12180 | int typeloc; |
11955 | int flags; | 12181 | int flags; |
11956 | 12182 | ||
11957 | c = pgetc(); | 12183 | c = pgetc_eatbnl(); |
11958 | if (c > 255 /* PEOA or PEOF */ | 12184 | if (c > 255 /* PEOA or PEOF */ |
11959 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) | 12185 | || (c != '(' && c != '{' && !is_name(c) && !is_special(c)) |
11960 | ) { | 12186 | ) { |
@@ -11967,7 +12193,7 @@ parsesub: { | |||
11967 | pungetc(); | 12193 | pungetc(); |
11968 | } else if (c == '(') { | 12194 | } else if (c == '(') { |
11969 | /* $(command) or $((arith)) */ | 12195 | /* $(command) or $((arith)) */ |
11970 | if (pgetc() == '(') { | 12196 | if (pgetc_eatbnl() == '(') { |
11971 | #if ENABLE_SH_MATH_SUPPORT | 12197 | #if ENABLE_SH_MATH_SUPPORT |
11972 | PARSEARITH(); | 12198 | PARSEARITH(); |
11973 | #else | 12199 | #else |
@@ -11984,9 +12210,9 @@ parsesub: { | |||
11984 | USTPUTC(VSNORMAL, out); | 12210 | USTPUTC(VSNORMAL, out); |
11985 | subtype = VSNORMAL; | 12211 | subtype = VSNORMAL; |
11986 | if (c == '{') { | 12212 | if (c == '{') { |
11987 | c = pgetc(); | 12213 | c = pgetc_eatbnl(); |
11988 | if (c == '#') { | 12214 | if (c == '#') { |
11989 | c = pgetc(); | 12215 | c = pgetc_eatbnl(); |
11990 | if (c == '}') | 12216 | if (c == '}') |
11991 | c = '#'; /* ${#} - same as $# */ | 12217 | c = '#'; /* ${#} - same as $# */ |
11992 | else | 12218 | else |
@@ -11999,18 +12225,18 @@ parsesub: { | |||
11999 | /* $[{[#]]NAME[}] */ | 12225 | /* $[{[#]]NAME[}] */ |
12000 | do { | 12226 | do { |
12001 | STPUTC(c, out); | 12227 | STPUTC(c, out); |
12002 | c = pgetc(); | 12228 | c = pgetc_eatbnl(); |
12003 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); | 12229 | } while (c <= 255 /* not PEOA or PEOF */ && is_in_name(c)); |
12004 | } else if (isdigit(c)) { | 12230 | } else if (isdigit(c)) { |
12005 | /* $[{[#]]NUM[}] */ | 12231 | /* $[{[#]]NUM[}] */ |
12006 | do { | 12232 | do { |
12007 | STPUTC(c, out); | 12233 | STPUTC(c, out); |
12008 | c = pgetc(); | 12234 | c = pgetc_eatbnl(); |
12009 | } while (isdigit(c)); | 12235 | } while (isdigit(c)); |
12010 | } else if (is_special(c)) { | 12236 | } else if (is_special(c)) { |
12011 | /* $[{[#]]<specialchar>[}] */ | 12237 | /* $[{[#]]<specialchar>[}] */ |
12012 | USTPUTC(c, out); | 12238 | USTPUTC(c, out); |
12013 | c = pgetc(); | 12239 | c = pgetc_eatbnl(); |
12014 | } else { | 12240 | } else { |
12015 | badsub: | 12241 | badsub: |
12016 | raise_error_syntax("bad substitution"); | 12242 | raise_error_syntax("bad substitution"); |
@@ -12023,15 +12249,18 @@ parsesub: { | |||
12023 | STPUTC('=', out); | 12249 | STPUTC('=', out); |
12024 | flags = 0; | 12250 | flags = 0; |
12025 | if (subtype == 0) { | 12251 | if (subtype == 0) { |
12252 | static const char types[] ALIGN1 = "}-+?="; | ||
12026 | /* ${VAR...} but not $VAR or ${#VAR} */ | 12253 | /* ${VAR...} but not $VAR or ${#VAR} */ |
12027 | /* c == first char after VAR */ | 12254 | /* c == first char after VAR */ |
12028 | switch (c) { | 12255 | switch (c) { |
12029 | case ':': | 12256 | case ':': |
12030 | c = pgetc(); | 12257 | c = pgetc_eatbnl(); |
12031 | #if ENABLE_ASH_BASH_COMPAT | 12258 | #if ENABLE_ASH_BASH_COMPAT |
12032 | if (c == ':' || c == '$' || isdigit(c)) { | 12259 | /* This check is only needed to not misinterpret |
12033 | //TODO: support more general format ${v:EXPR:EXPR}, | 12260 | * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD} |
12034 | // where EXPR follows $(()) rules | 12261 | * constructs. |
12262 | */ | ||
12263 | if (!strchr(types, c)) { | ||
12035 | subtype = VSSUBSTR; | 12264 | subtype = VSSUBSTR; |
12036 | pungetc(); | 12265 | pungetc(); |
12037 | break; /* "goto do_pungetc" is bigger (!) */ | 12266 | break; /* "goto do_pungetc" is bigger (!) */ |
@@ -12040,7 +12269,6 @@ parsesub: { | |||
12040 | flags = VSNUL; | 12269 | flags = VSNUL; |
12041 | /*FALLTHROUGH*/ | 12270 | /*FALLTHROUGH*/ |
12042 | default: { | 12271 | default: { |
12043 | static const char types[] ALIGN1 = "}-+?="; | ||
12044 | const char *p = strchr(types, c); | 12272 | const char *p = strchr(types, c); |
12045 | if (p == NULL) | 12273 | if (p == NULL) |
12046 | goto badsub; | 12274 | goto badsub; |
@@ -12051,7 +12279,7 @@ parsesub: { | |||
12051 | case '#': { | 12279 | case '#': { |
12052 | int cc = c; | 12280 | int cc = c; |
12053 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); | 12281 | subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT); |
12054 | c = pgetc(); | 12282 | c = pgetc_eatbnl(); |
12055 | if (c != cc) | 12283 | if (c != cc) |
12056 | goto do_pungetc; | 12284 | goto do_pungetc; |
12057 | subtype++; | 12285 | subtype++; |
@@ -12063,7 +12291,7 @@ parsesub: { | |||
12063 | //TODO: encode pattern and repl separately. | 12291 | //TODO: encode pattern and repl separately. |
12064 | // Currently ${v/$var_with_slash/repl} is horribly broken | 12292 | // Currently ${v/$var_with_slash/repl} is horribly broken |
12065 | subtype = VSREPLACE; | 12293 | subtype = VSREPLACE; |
12066 | c = pgetc(); | 12294 | c = pgetc_eatbnl(); |
12067 | if (c != '/') | 12295 | if (c != '/') |
12068 | goto do_pungetc; | 12296 | goto do_pungetc; |
12069 | subtype++; /* VSREPLACEALL */ | 12297 | subtype++; /* VSREPLACEALL */ |
@@ -12101,9 +12329,18 @@ parsebackq: { | |||
12101 | str = NULL; | 12329 | str = NULL; |
12102 | savelen = out - (char *)stackblock(); | 12330 | savelen = out - (char *)stackblock(); |
12103 | if (savelen > 0) { | 12331 | if (savelen > 0) { |
12332 | /* | ||
12333 | * FIXME: this can allocate very large block on stack and SEGV. | ||
12334 | * Example: | ||
12335 | * echo "..<100kbytes>..`true` $(true) `true` ..." | ||
12336 | * allocates 100kb for every command subst. With about | ||
12337 | * a hundred command substitutions stack overflows. | ||
12338 | * With larger prepended string, SEGV happens sooner. | ||
12339 | */ | ||
12104 | str = alloca(savelen); | 12340 | str = alloca(savelen); |
12105 | memcpy(str, stackblock(), savelen); | 12341 | memcpy(str, stackblock(), savelen); |
12106 | } | 12342 | } |
12343 | |||
12107 | if (oldstyle) { | 12344 | if (oldstyle) { |
12108 | /* We must read until the closing backquote, giving special | 12345 | /* We must read until the closing backquote, giving special |
12109 | * treatment to some slashes, and then push the string and | 12346 | * treatment to some slashes, and then push the string and |
@@ -12126,8 +12363,7 @@ parsebackq: { | |||
12126 | case '\\': | 12363 | case '\\': |
12127 | pc = pgetc(); | 12364 | pc = pgetc(); |
12128 | if (pc == '\n') { | 12365 | if (pc == '\n') { |
12129 | g_parsefile->linno++; | 12366 | nlprompt(); |
12130 | setprompt_if(doprompt, 2); | ||
12131 | /* | 12367 | /* |
12132 | * If eating a newline, avoid putting | 12368 | * If eating a newline, avoid putting |
12133 | * the newline into the new character | 12369 | * the newline into the new character |
@@ -12152,8 +12388,7 @@ parsebackq: { | |||
12152 | raise_error_syntax("EOF in backquote substitution"); | 12388 | raise_error_syntax("EOF in backquote substitution"); |
12153 | 12389 | ||
12154 | case '\n': | 12390 | case '\n': |
12155 | g_parsefile->linno++; | 12391 | nlnoprompt(); |
12156 | needprompt = doprompt; | ||
12157 | break; | 12392 | break; |
12158 | 12393 | ||
12159 | default: | 12394 | default: |
@@ -12272,7 +12507,7 @@ xxreadtoken(void) | |||
12272 | setprompt_if(needprompt, 2); | 12507 | setprompt_if(needprompt, 2); |
12273 | startlinno = g_parsefile->linno; | 12508 | startlinno = g_parsefile->linno; |
12274 | for (;;) { /* until token or start of word found */ | 12509 | for (;;) { /* until token or start of word found */ |
12275 | c = pgetc_fast(); | 12510 | c = pgetc(); |
12276 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) | 12511 | if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA)) |
12277 | continue; | 12512 | continue; |
12278 | 12513 | ||
@@ -12285,16 +12520,14 @@ xxreadtoken(void) | |||
12285 | pungetc(); | 12520 | pungetc(); |
12286 | break; /* return readtoken1(...) */ | 12521 | break; /* return readtoken1(...) */ |
12287 | } | 12522 | } |
12288 | startlinno = ++g_parsefile->linno; | 12523 | nlprompt(); |
12289 | setprompt_if(doprompt, 2); | ||
12290 | } else { | 12524 | } else { |
12291 | const char *p; | 12525 | const char *p; |
12292 | 12526 | ||
12293 | p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; | 12527 | p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1; |
12294 | if (c != PEOF) { | 12528 | if (c != PEOF) { |
12295 | if (c == '\n') { | 12529 | if (c == '\n') { |
12296 | g_parsefile->linno++; | 12530 | nlnoprompt(); |
12297 | needprompt = doprompt; | ||
12298 | } | 12531 | } |
12299 | 12532 | ||
12300 | p = strchr(xxreadtoken_chars, c); | 12533 | p = strchr(xxreadtoken_chars, c); |
@@ -12335,7 +12568,7 @@ xxreadtoken(void) | |||
12335 | setprompt_if(needprompt, 2); | 12568 | setprompt_if(needprompt, 2); |
12336 | startlinno = g_parsefile->linno; | 12569 | startlinno = g_parsefile->linno; |
12337 | for (;;) { /* until token or start of word found */ | 12570 | for (;;) { /* until token or start of word found */ |
12338 | c = pgetc_fast(); | 12571 | c = pgetc(); |
12339 | switch (c) { | 12572 | switch (c) { |
12340 | case ' ': case '\t': | 12573 | case ' ': case '\t': |
12341 | IF_ASH_ALIAS(case PEOA:) | 12574 | IF_ASH_ALIAS(case PEOA:) |
@@ -12347,15 +12580,13 @@ xxreadtoken(void) | |||
12347 | continue; | 12580 | continue; |
12348 | case '\\': | 12581 | case '\\': |
12349 | if (pgetc() == '\n') { | 12582 | if (pgetc() == '\n') { |
12350 | startlinno = ++g_parsefile->linno; | 12583 | nlprompt(); |
12351 | setprompt_if(doprompt, 2); | ||
12352 | continue; | 12584 | continue; |
12353 | } | 12585 | } |
12354 | pungetc(); | 12586 | pungetc(); |
12355 | goto breakloop; | 12587 | goto breakloop; |
12356 | case '\n': | 12588 | case '\n': |
12357 | g_parsefile->linno++; | 12589 | nlnoprompt(); |
12358 | needprompt = doprompt; | ||
12359 | RETURN(TNL); | 12590 | RETURN(TNL); |
12360 | case PEOF: | 12591 | case PEOF: |
12361 | RETURN(TEOF); | 12592 | RETURN(TEOF); |
@@ -12426,7 +12657,7 @@ readtoken(void) | |||
12426 | pp = findkwd(wordtext); | 12657 | pp = findkwd(wordtext); |
12427 | if (pp) { | 12658 | if (pp) { |
12428 | lasttoken = t = pp - tokname_array; | 12659 | lasttoken = t = pp - tokname_array; |
12429 | TRACE(("keyword '%s' recognized\n", tokname_array[t] + 1)); | 12660 | TRACE(("keyword '%s' recognized\n", tokname_array[t])); |
12430 | goto out; | 12661 | goto out; |
12431 | } | 12662 | } |
12432 | } | 12663 | } |
@@ -12447,9 +12678,9 @@ readtoken(void) | |||
12447 | checkkwd = 0; | 12678 | checkkwd = 0; |
12448 | #if DEBUG | 12679 | #if DEBUG |
12449 | if (!alreadyseen) | 12680 | if (!alreadyseen) |
12450 | TRACE(("token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); | 12681 | TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); |
12451 | else | 12682 | else |
12452 | TRACE(("reread token '%s' %s\n", tokname_array[t] + 1, t == TWORD ? wordtext : "")); | 12683 | TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : "")); |
12453 | #endif | 12684 | #endif |
12454 | return t; | 12685 | return t; |
12455 | } | 12686 | } |
@@ -12536,35 +12767,39 @@ expandstr(const char *ps) | |||
12536 | * Execute a command or commands contained in a string. | 12767 | * Execute a command or commands contained in a string. |
12537 | */ | 12768 | */ |
12538 | static int | 12769 | static int |
12539 | evalstring(char *s, int mask) | 12770 | evalstring(char *s, int flags) |
12540 | { | 12771 | { |
12541 | union node *n; | 12772 | union node *n; |
12542 | struct stackmark smark; | 12773 | struct stackmark smark; |
12543 | int skip; | 12774 | int status; |
12544 | 12775 | ||
12776 | s = sstrdup(s); | ||
12545 | setinputstring(s); | 12777 | setinputstring(s); |
12546 | setstackmark(&smark); | 12778 | setstackmark(&smark); |
12547 | 12779 | ||
12548 | skip = 0; | 12780 | status = 0; |
12549 | while ((n = parsecmd(0)) != NODE_EOF) { | 12781 | while ((n = parsecmd(0)) != NODE_EOF) { |
12550 | evaltree(n, 0); | 12782 | int i; |
12783 | |||
12784 | i = evaltree(n, flags); | ||
12785 | if (n) | ||
12786 | status = i; | ||
12551 | popstackmark(&smark); | 12787 | popstackmark(&smark); |
12552 | skip = evalskip; | 12788 | if (evalskip) |
12553 | if (skip) | ||
12554 | break; | 12789 | break; |
12555 | } | 12790 | } |
12791 | popstackmark(&smark); | ||
12556 | popfile(); | 12792 | popfile(); |
12793 | stunalloc(s); | ||
12557 | 12794 | ||
12558 | skip &= mask; | 12795 | return status; |
12559 | evalskip = skip; | ||
12560 | return skip; | ||
12561 | } | 12796 | } |
12562 | 12797 | ||
12563 | /* | 12798 | /* |
12564 | * The eval command. | 12799 | * The eval command. |
12565 | */ | 12800 | */ |
12566 | static int FAST_FUNC | 12801 | static int FAST_FUNC |
12567 | evalcmd(int argc UNUSED_PARAM, char **argv) | 12802 | evalcmd(int argc UNUSED_PARAM, char **argv, int flags) |
12568 | { | 12803 | { |
12569 | char *p; | 12804 | char *p; |
12570 | char *concat; | 12805 | char *concat; |
@@ -12584,9 +12819,9 @@ evalcmd(int argc UNUSED_PARAM, char **argv) | |||
12584 | STPUTC('\0', concat); | 12819 | STPUTC('\0', concat); |
12585 | p = grabstackstr(concat); | 12820 | p = grabstackstr(concat); |
12586 | } | 12821 | } |
12587 | evalstring(p, ~SKIPEVAL); | 12822 | return evalstring(p, flags & EV_TESTED); |
12588 | } | 12823 | } |
12589 | return exitstatus; | 12824 | return 0; |
12590 | } | 12825 | } |
12591 | 12826 | ||
12592 | /* | 12827 | /* |
@@ -12600,6 +12835,7 @@ cmdloop(int top) | |||
12600 | union node *n; | 12835 | union node *n; |
12601 | struct stackmark smark; | 12836 | struct stackmark smark; |
12602 | int inter; | 12837 | int inter; |
12838 | int status = 0; | ||
12603 | int numeof = 0; | 12839 | int numeof = 0; |
12604 | 12840 | ||
12605 | TRACE(("cmdloop(%d) called\n", top)); | 12841 | TRACE(("cmdloop(%d) called\n", top)); |
@@ -12631,20 +12867,24 @@ cmdloop(int top) | |||
12631 | } | 12867 | } |
12632 | numeof++; | 12868 | numeof++; |
12633 | } else if (nflag == 0) { | 12869 | } else if (nflag == 0) { |
12870 | int i; | ||
12871 | |||
12634 | /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ | 12872 | /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ |
12635 | job_warning >>= 1; | 12873 | job_warning >>= 1; |
12636 | numeof = 0; | 12874 | numeof = 0; |
12637 | evaltree(n, 0); | 12875 | i = evaltree(n, 0); |
12876 | if (n) | ||
12877 | status = i; | ||
12638 | } | 12878 | } |
12639 | popstackmark(&smark); | 12879 | popstackmark(&smark); |
12640 | skip = evalskip; | 12880 | skip = evalskip; |
12641 | 12881 | ||
12642 | if (skip) { | 12882 | if (skip) { |
12643 | evalskip = 0; | 12883 | evalskip &= ~SKIPFUNC; |
12644 | return skip & SKIPEVAL; | 12884 | break; |
12645 | } | 12885 | } |
12646 | } | 12886 | } |
12647 | return 0; | 12887 | return status; |
12648 | } | 12888 | } |
12649 | 12889 | ||
12650 | /* | 12890 | /* |
@@ -12689,32 +12929,38 @@ find_dot_file(char *name) | |||
12689 | } | 12929 | } |
12690 | 12930 | ||
12691 | static int FAST_FUNC | 12931 | static int FAST_FUNC |
12692 | dotcmd(int argc, char **argv) | 12932 | dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM) |
12693 | { | 12933 | { |
12934 | /* "false; . empty_file; echo $?" should print 0, not 1: */ | ||
12935 | int status = 0; | ||
12694 | char *fullname; | 12936 | char *fullname; |
12937 | char **argv; | ||
12695 | struct strlist *sp; | 12938 | struct strlist *sp; |
12696 | volatile struct shparam saveparam; | 12939 | volatile struct shparam saveparam; |
12697 | 12940 | ||
12698 | for (sp = cmdenviron; sp; sp = sp->next) | 12941 | for (sp = cmdenviron; sp; sp = sp->next) |
12699 | setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); | 12942 | setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED); |
12700 | 12943 | ||
12701 | if (!argv[1]) { | 12944 | nextopt(nullstr); /* handle possible "--" */ |
12945 | argv = argptr; | ||
12946 | |||
12947 | if (!argv[0]) { | ||
12702 | /* bash says: "bash: .: filename argument required" */ | 12948 | /* bash says: "bash: .: filename argument required" */ |
12703 | return 2; /* bash compat */ | 12949 | return 2; /* bash compat */ |
12704 | } | 12950 | } |
12705 | 12951 | ||
12706 | /* "false; . empty_file; echo $?" should print 0, not 1: */ | ||
12707 | exitstatus = 0; | ||
12708 | |||
12709 | /* This aborts if file isn't found, which is POSIXly correct. | 12952 | /* This aborts if file isn't found, which is POSIXly correct. |
12710 | * bash returns exitcode 1 instead. | 12953 | * bash returns exitcode 1 instead. |
12711 | */ | 12954 | */ |
12712 | fullname = find_dot_file(argv[1]); | 12955 | fullname = find_dot_file(argv[0]); |
12713 | argv += 2; | 12956 | argv++; |
12714 | argc -= 2; | 12957 | if (argv[0]) { /* . FILE ARGS, ARGS exist */ |
12715 | if (argc) { /* argc > 0, argv[0] != NULL */ | 12958 | int argc; |
12716 | saveparam = shellparam; | 12959 | saveparam = shellparam; |
12717 | shellparam.malloced = 0; | 12960 | shellparam.malloced = 0; |
12961 | argc = 1; | ||
12962 | while (argv[argc]) | ||
12963 | argc++; | ||
12718 | shellparam.nparam = argc; | 12964 | shellparam.nparam = argc; |
12719 | shellparam.p = argv; | 12965 | shellparam.p = argv; |
12720 | }; | 12966 | }; |
@@ -12724,15 +12970,15 @@ dotcmd(int argc, char **argv) | |||
12724 | */ | 12970 | */ |
12725 | setinputfile(fullname, INPUT_PUSH_FILE); | 12971 | setinputfile(fullname, INPUT_PUSH_FILE); |
12726 | commandname = fullname; | 12972 | commandname = fullname; |
12727 | cmdloop(0); | 12973 | status = cmdloop(0); |
12728 | popfile(); | 12974 | popfile(); |
12729 | 12975 | ||
12730 | if (argc) { | 12976 | if (argv[0]) { |
12731 | freeparam(&shellparam); | 12977 | freeparam(&shellparam); |
12732 | shellparam = saveparam; | 12978 | shellparam = saveparam; |
12733 | }; | 12979 | }; |
12734 | 12980 | ||
12735 | return exitstatus; | 12981 | return status; |
12736 | } | 12982 | } |
12737 | 12983 | ||
12738 | static int FAST_FUNC | 12984 | static int FAST_FUNC |
@@ -12994,8 +13240,6 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
12994 | } | 13240 | } |
12995 | 13241 | ||
12996 | 13242 | ||
12997 | /* ============ trap.c */ | ||
12998 | |||
12999 | /* | 13243 | /* |
13000 | * The trap builtin. | 13244 | * The trap builtin. |
13001 | */ | 13245 | */ |
@@ -13101,7 +13345,7 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
13101 | } | 13345 | } |
13102 | } | 13346 | } |
13103 | # endif | 13347 | # endif |
13104 | out1fmt("\n\n"); | 13348 | newline_and_flush(stdout); |
13105 | return EXIT_SUCCESS; | 13349 | return EXIT_SUCCESS; |
13106 | } | 13350 | } |
13107 | #endif | 13351 | #endif |
@@ -13421,15 +13665,10 @@ exitshell(void) | |||
13421 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT | 13665 | #if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT |
13422 | save_history(line_input_state); | 13666 | save_history(line_input_state); |
13423 | #endif | 13667 | #endif |
13424 | |||
13425 | status = exitstatus; | 13668 | status = exitstatus; |
13426 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); | 13669 | TRACE(("pid %d, exitshell(%d)\n", getpid(), status)); |
13427 | if (setjmp(loc.loc)) { | 13670 | if (setjmp(loc.loc)) { |
13428 | if (exception_type == EXEXIT) | 13671 | if (exception_type == EXEXIT) |
13429 | /* dash bug: it just does _exit(exitstatus) here | ||
13430 | * but we have to do setjobctl(0) first! | ||
13431 | * (bug is still not fixed in dash-0.5.3 - if you run dash | ||
13432 | * under Midnight Commander, on exit from dash MC is backgrounded) */ | ||
13433 | status = exitstatus; | 13672 | status = exitstatus; |
13434 | goto out; | 13673 | goto out; |
13435 | } | 13674 | } |
@@ -13437,11 +13676,15 @@ exitshell(void) | |||
13437 | p = trap[0]; | 13676 | p = trap[0]; |
13438 | if (p) { | 13677 | if (p) { |
13439 | trap[0] = NULL; | 13678 | trap[0] = NULL; |
13679 | evalskip = 0; | ||
13440 | evalstring(p, 0); | 13680 | evalstring(p, 0); |
13441 | free(p); | 13681 | /*free(p); - we'll exit soon */ |
13442 | } | 13682 | } |
13443 | flush_stdout_stderr(); | 13683 | flush_stdout_stderr(); |
13444 | out: | 13684 | out: |
13685 | /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}". | ||
13686 | * our setjobctl(0) does not panic if tcsetpgrp fails inside it. | ||
13687 | */ | ||
13445 | setjobctl(0); | 13688 | setjobctl(0); |
13446 | _exit(status); | 13689 | _exit(status); |
13447 | /* NOTREACHED */ | 13690 | /* NOTREACHED */ |
@@ -13454,18 +13697,15 @@ init(int xp) | |||
13454 | init(void) | 13697 | init(void) |
13455 | #endif | 13698 | #endif |
13456 | { | 13699 | { |
13457 | /* from input.c: */ | ||
13458 | /* we will never free this */ | 13700 | /* we will never free this */ |
13459 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); | 13701 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); |
13460 | 13702 | ||
13461 | /* from trap.c: */ | ||
13462 | signal(SIGCHLD, SIG_DFL); | 13703 | signal(SIGCHLD, SIG_DFL); |
13463 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 13704 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. |
13464 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | 13705 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" |
13465 | */ | 13706 | */ |
13466 | signal(SIGHUP, SIG_DFL); | 13707 | signal(SIGHUP, SIG_DFL); |
13467 | 13708 | ||
13468 | /* from var.c: */ | ||
13469 | { | 13709 | { |
13470 | char **envp; | 13710 | char **envp; |
13471 | const char *p; | 13711 | const char *p; |
@@ -13553,11 +13793,14 @@ init(void) | |||
13553 | } | 13793 | } |
13554 | #endif | 13794 | #endif |
13555 | for (envp = environ; envp && *envp; envp++) { | 13795 | for (envp = environ; envp && *envp; envp++) { |
13556 | if (strchr(*envp, '=')) { | 13796 | p = endofname(*envp); |
13797 | if (p != *envp && *p == '=') { | ||
13557 | setvareq(*envp, VEXPORT|VTEXTFIXED); | 13798 | setvareq(*envp, VEXPORT|VTEXTFIXED); |
13558 | } | 13799 | } |
13559 | } | 13800 | } |
13560 | 13801 | ||
13802 | setvareq((char*)defoptindvar, VTEXTFIXED); | ||
13803 | |||
13561 | setvar0("PPID", utoa(getppid())); | 13804 | setvar0("PPID", utoa(getppid())); |
13562 | #if ENABLE_ASH_BASH_COMPAT | 13805 | #if ENABLE_ASH_BASH_COMPAT |
13563 | p = lookupvar("SHLVL"); | 13806 | p = lookupvar("SHLVL"); |
@@ -13570,10 +13813,10 @@ init(void) | |||
13570 | #endif | 13813 | #endif |
13571 | p = lookupvar("PWD"); | 13814 | p = lookupvar("PWD"); |
13572 | if (p) { | 13815 | if (p) { |
13573 | if (*p != '/' || stat(p, &st1) || stat(".", &st2) | 13816 | if (p[0] != '/' || stat(p, &st1) || stat(".", &st2) |
13574 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino | 13817 | || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino |
13575 | ) { | 13818 | ) { |
13576 | p = '\0'; | 13819 | p = NULL; |
13577 | } | 13820 | } |
13578 | } | 13821 | } |
13579 | setpwd(p, 0); | 13822 | setpwd(p, 0); |
@@ -13664,14 +13907,10 @@ procargs(char **argv) | |||
13664 | static void | 13907 | static void |
13665 | read_profile(const char *name) | 13908 | read_profile(const char *name) |
13666 | { | 13909 | { |
13667 | int skip; | ||
13668 | |||
13669 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) | 13910 | if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0) |
13670 | return; | 13911 | return; |
13671 | skip = cmdloop(0); | 13912 | cmdloop(0); |
13672 | popfile(); | 13913 | popfile(); |
13673 | if (skip) | ||
13674 | exitshell(); | ||
13675 | } | 13914 | } |
13676 | 13915 | ||
13677 | /* | 13916 | /* |