diff options
Diffstat (limited to 'shell/hush.c')
-rw-r--r-- | shell/hush.c | 270 |
1 files changed, 239 insertions, 31 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9f946d82f..cdc3a8618 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -48,7 +48,7 @@ | |||
48 | * tilde expansion | 48 | * tilde expansion |
49 | * aliases | 49 | * aliases |
50 | * builtins mandated by standards we don't support: | 50 | * builtins mandated by standards we don't support: |
51 | * [un]alias, command, fc, getopts, times: | 51 | * [un]alias, command, fc: |
52 | * command -v CMD: print "/path/to/CMD" | 52 | * command -v CMD: print "/path/to/CMD" |
53 | * prints "CMD" for builtins | 53 | * prints "CMD" for builtins |
54 | * prints "alias ALIAS='EXPANSION'" for aliases | 54 | * prints "alias ALIAS='EXPANSION'" for aliases |
@@ -58,8 +58,6 @@ | |||
58 | * (can use this to override standalone shell as well) | 58 | * (can use this to override standalone shell as well) |
59 | * -p: use default $PATH | 59 | * -p: use default $PATH |
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 60 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
61 | * getopts: getopt() for shells | ||
62 | * times: print getrusage(SELF/CHILDREN).ru_utime/ru_stime | ||
63 | * fc -l[nr] [BEG] [END]: list range of commands in history | 61 | * fc -l[nr] [BEG] [END]: list range of commands in history |
64 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 62 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
65 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 63 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
@@ -265,6 +263,11 @@ | |||
265 | //config: default y | 263 | //config: default y |
266 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 264 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
267 | //config: | 265 | //config: |
266 | //config:config HUSH_TIMES | ||
267 | //config: bool "times builtin" | ||
268 | //config: default y | ||
269 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
270 | //config: | ||
268 | //config:config HUSH_READ | 271 | //config:config HUSH_READ |
269 | //config: bool "read builtin" | 272 | //config: bool "read builtin" |
270 | //config: default y | 273 | //config: default y |
@@ -290,6 +293,11 @@ | |||
290 | //config: default y | 293 | //config: default y |
291 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | 294 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH |
292 | //config: | 295 | //config: |
296 | //config:config HUSH_GETOPTS | ||
297 | //config: bool "getopts builtin" | ||
298 | //config: default y | ||
299 | //config: depends on HUSH || SH_IS_HUSH || BASH_IS_HUSH | ||
300 | //config: | ||
293 | //config:config HUSH_MEMLEAK | 301 | //config:config HUSH_MEMLEAK |
294 | //config: bool "memleak builtin (debugging)" | 302 | //config: bool "memleak builtin (debugging)" |
295 | //config: default n | 303 | //config: default n |
@@ -325,6 +333,7 @@ | |||
325 | #if ENABLE_HUSH_CASE | 333 | #if ENABLE_HUSH_CASE |
326 | # include <fnmatch.h> | 334 | # include <fnmatch.h> |
327 | #endif | 335 | #endif |
336 | #include <sys/times.h> | ||
328 | #include <sys/utsname.h> /* for setting $HOSTNAME */ | 337 | #include <sys/utsname.h> /* for setting $HOSTNAME */ |
329 | 338 | ||
330 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 339 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
@@ -352,6 +361,7 @@ | |||
352 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT | 361 | #define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT |
353 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT | 362 | #define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT |
354 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) | 363 | #define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST) |
364 | #define BASH_READ_D ENABLE_HUSH_BASH_COMPAT | ||
355 | 365 | ||
356 | 366 | ||
357 | /* Build knobs */ | 367 | /* Build knobs */ |
@@ -977,6 +987,9 @@ static int builtin_readonly(char **argv) FAST_FUNC; | |||
977 | static int builtin_fg_bg(char **argv) FAST_FUNC; | 987 | static int builtin_fg_bg(char **argv) FAST_FUNC; |
978 | static int builtin_jobs(char **argv) FAST_FUNC; | 988 | static int builtin_jobs(char **argv) FAST_FUNC; |
979 | #endif | 989 | #endif |
990 | #if ENABLE_HUSH_GETOPTS | ||
991 | static int builtin_getopts(char **argv) FAST_FUNC; | ||
992 | #endif | ||
980 | #if ENABLE_HUSH_HELP | 993 | #if ENABLE_HUSH_HELP |
981 | static int builtin_help(char **argv) FAST_FUNC; | 994 | static int builtin_help(char **argv) FAST_FUNC; |
982 | #endif | 995 | #endif |
@@ -1010,6 +1023,9 @@ static int builtin_trap(char **argv) FAST_FUNC; | |||
1010 | #if ENABLE_HUSH_TYPE | 1023 | #if ENABLE_HUSH_TYPE |
1011 | static int builtin_type(char **argv) FAST_FUNC; | 1024 | static int builtin_type(char **argv) FAST_FUNC; |
1012 | #endif | 1025 | #endif |
1026 | #if ENABLE_HUSH_TIMES | ||
1027 | static int builtin_times(char **argv) FAST_FUNC; | ||
1028 | #endif | ||
1013 | static int builtin_true(char **argv) FAST_FUNC; | 1029 | static int builtin_true(char **argv) FAST_FUNC; |
1014 | #if ENABLE_HUSH_UMASK | 1030 | #if ENABLE_HUSH_UMASK |
1015 | static int builtin_umask(char **argv) FAST_FUNC; | 1031 | static int builtin_umask(char **argv) FAST_FUNC; |
@@ -1070,6 +1086,9 @@ static const struct built_in_command bltins1[] = { | |||
1070 | #if ENABLE_HUSH_JOB | 1086 | #if ENABLE_HUSH_JOB |
1071 | BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), | 1087 | BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"), |
1072 | #endif | 1088 | #endif |
1089 | #if ENABLE_HUSH_GETOPTS | ||
1090 | BLTIN("getopts" , builtin_getopts , NULL), | ||
1091 | #endif | ||
1073 | #if ENABLE_HUSH_HELP | 1092 | #if ENABLE_HUSH_HELP |
1074 | BLTIN("help" , builtin_help , NULL), | 1093 | BLTIN("help" , builtin_help , NULL), |
1075 | #endif | 1094 | #endif |
@@ -1104,6 +1123,9 @@ static const struct built_in_command bltins1[] = { | |||
1104 | #if BASH_SOURCE | 1123 | #if BASH_SOURCE |
1105 | BLTIN("source" , builtin_source , NULL), | 1124 | BLTIN("source" , builtin_source , NULL), |
1106 | #endif | 1125 | #endif |
1126 | #if ENABLE_HUSH_TIMES | ||
1127 | BLTIN("times" , builtin_times , NULL), | ||
1128 | #endif | ||
1107 | #if ENABLE_HUSH_TRAP | 1129 | #if ENABLE_HUSH_TRAP |
1108 | BLTIN("trap" , builtin_trap , "Trap signals"), | 1130 | BLTIN("trap" , builtin_trap , "Trap signals"), |
1109 | #endif | 1131 | #endif |
@@ -1272,7 +1294,7 @@ static void xxfree(void *ptr) | |||
1272 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. | 1294 | * HUSH_DEBUG >= 2 prints line number in this file where it was detected. |
1273 | */ | 1295 | */ |
1274 | #if HUSH_DEBUG < 2 | 1296 | #if HUSH_DEBUG < 2 |
1275 | # define die_if_script(lineno, ...) die_if_script(__VA_ARGS__) | 1297 | # define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__) |
1276 | # define syntax_error(lineno, msg) syntax_error(msg) | 1298 | # define syntax_error(lineno, msg) syntax_error(msg) |
1277 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) | 1299 | # define syntax_error_at(lineno, msg) syntax_error_at(msg) |
1278 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) | 1300 | # define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch) |
@@ -1280,7 +1302,16 @@ static void xxfree(void *ptr) | |||
1280 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) | 1302 | # define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch) |
1281 | #endif | 1303 | #endif |
1282 | 1304 | ||
1283 | static void die_if_script(unsigned lineno, const char *fmt, ...) | 1305 | static void die_if_script(void) |
1306 | { | ||
1307 | if (!G_interactive_fd) { | ||
1308 | if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */ | ||
1309 | xfunc_error_retval = G.last_exitcode; | ||
1310 | xfunc_die(); | ||
1311 | } | ||
1312 | } | ||
1313 | |||
1314 | static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...) | ||
1284 | { | 1315 | { |
1285 | va_list p; | 1316 | va_list p; |
1286 | 1317 | ||
@@ -1290,8 +1321,7 @@ static void die_if_script(unsigned lineno, const char *fmt, ...) | |||
1290 | va_start(p, fmt); | 1321 | va_start(p, fmt); |
1291 | bb_verror_msg(fmt, p, NULL); | 1322 | bb_verror_msg(fmt, p, NULL); |
1292 | va_end(p); | 1323 | va_end(p); |
1293 | if (!G_interactive_fd) | 1324 | die_if_script(); |
1294 | xfunc_die(); | ||
1295 | } | 1325 | } |
1296 | 1326 | ||
1297 | static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) | 1327 | static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) |
@@ -1300,16 +1330,20 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg) | |||
1300 | bb_error_msg("syntax error: %s", msg); | 1330 | bb_error_msg("syntax error: %s", msg); |
1301 | else | 1331 | else |
1302 | bb_error_msg("syntax error"); | 1332 | bb_error_msg("syntax error"); |
1333 | die_if_script(); | ||
1303 | } | 1334 | } |
1304 | 1335 | ||
1305 | static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) | 1336 | static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg) |
1306 | { | 1337 | { |
1307 | bb_error_msg("syntax error at '%s'", msg); | 1338 | bb_error_msg("syntax error at '%s'", msg); |
1339 | die_if_script(); | ||
1308 | } | 1340 | } |
1309 | 1341 | ||
1310 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) | 1342 | static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s) |
1311 | { | 1343 | { |
1312 | bb_error_msg("syntax error: unterminated %s", s); | 1344 | bb_error_msg("syntax error: unterminated %s", s); |
1345 | //? source4.tests fails: in bash, echo ${^} in script does not terminate the script | ||
1346 | // die_if_script(); | ||
1313 | } | 1347 | } |
1314 | 1348 | ||
1315 | static void syntax_error_unterm_ch(unsigned lineno, char ch) | 1349 | static void syntax_error_unterm_ch(unsigned lineno, char ch) |
@@ -1327,17 +1361,18 @@ static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch) | |||
1327 | bb_error_msg("hush.c:%u", lineno); | 1361 | bb_error_msg("hush.c:%u", lineno); |
1328 | #endif | 1362 | #endif |
1329 | bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); | 1363 | bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg); |
1364 | die_if_script(); | ||
1330 | } | 1365 | } |
1331 | 1366 | ||
1332 | #if HUSH_DEBUG < 2 | 1367 | #if HUSH_DEBUG < 2 |
1333 | # undef die_if_script | 1368 | # undef msg_and_die_if_script |
1334 | # undef syntax_error | 1369 | # undef syntax_error |
1335 | # undef syntax_error_at | 1370 | # undef syntax_error_at |
1336 | # undef syntax_error_unterm_ch | 1371 | # undef syntax_error_unterm_ch |
1337 | # undef syntax_error_unterm_str | 1372 | # undef syntax_error_unterm_str |
1338 | # undef syntax_error_unexpected_ch | 1373 | # undef syntax_error_unexpected_ch |
1339 | #else | 1374 | #else |
1340 | # define die_if_script(...) die_if_script(__LINE__, __VA_ARGS__) | 1375 | # define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__) |
1341 | # define syntax_error(msg) syntax_error(__LINE__, msg) | 1376 | # define syntax_error(msg) syntax_error(__LINE__, msg) |
1342 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) | 1377 | # define syntax_error_at(msg) syntax_error_at(__LINE__, msg) |
1343 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) | 1378 | # define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch) |
@@ -1800,7 +1835,7 @@ static void restore_ttypgrp_and__exit(void) | |||
1800 | * echo END_OF_SCRIPT | 1835 | * echo END_OF_SCRIPT |
1801 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". | 1836 | * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT". |
1802 | * This makes "echo END_OF_SCRIPT" executed twice. | 1837 | * This makes "echo END_OF_SCRIPT" executed twice. |
1803 | * Similar problems can be seen with die_if_script() -> xfunc_die() | 1838 | * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die() |
1804 | * and in `cmd` handling. | 1839 | * and in `cmd` handling. |
1805 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): | 1840 | * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit(): |
1806 | */ | 1841 | */ |
@@ -1966,6 +2001,9 @@ static int check_and_run_traps(void) | |||
1966 | break; | 2001 | break; |
1967 | #if ENABLE_HUSH_JOB | 2002 | #if ENABLE_HUSH_JOB |
1968 | case SIGHUP: { | 2003 | case SIGHUP: { |
2004 | //TODO: why are we doing this? ash and dash don't do this, | ||
2005 | //they have no handler for SIGHUP at all, | ||
2006 | //they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups | ||
1969 | struct pipe *job; | 2007 | struct pipe *job; |
1970 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); | 2008 | debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig); |
1971 | /* bash is observed to signal whole process groups, | 2009 | /* bash is observed to signal whole process groups, |
@@ -2411,18 +2449,17 @@ static int get_user_input(struct in_str *i) | |||
2411 | /* buglet: SIGINT will not make new prompt to appear _at once_, | 2449 | /* buglet: SIGINT will not make new prompt to appear _at once_, |
2412 | * only after <Enter>. (^C works immediately) */ | 2450 | * only after <Enter>. (^C works immediately) */ |
2413 | r = read_line_input(G.line_input_state, prompt_str, | 2451 | r = read_line_input(G.line_input_state, prompt_str, |
2414 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1, | 2452 | G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1 |
2415 | /*timeout*/ -1 | ||
2416 | ); | 2453 | ); |
2417 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ | 2454 | /* read_line_input intercepts ^C, "convert" it to SIGINT */ |
2418 | if (r == 0) { | 2455 | if (r == 0) |
2419 | write(STDOUT_FILENO, "^C", 2); | ||
2420 | raise(SIGINT); | 2456 | raise(SIGINT); |
2421 | } | ||
2422 | check_and_run_traps(); | 2457 | check_and_run_traps(); |
2423 | if (r != 0 && !G.flag_SIGINT) | 2458 | if (r != 0 && !G.flag_SIGINT) |
2424 | break; | 2459 | break; |
2425 | /* ^C or SIGINT: repeat */ | 2460 | /* ^C or SIGINT: repeat */ |
2461 | /* bash prints ^C even on real SIGINT (non-kbd generated) */ | ||
2462 | write(STDOUT_FILENO, "^C", 2); | ||
2426 | G.last_exitcode = 128 + SIGINT; | 2463 | G.last_exitcode = 128 + SIGINT; |
2427 | } | 2464 | } |
2428 | if (r < 0) { | 2465 | if (r < 0) { |
@@ -3384,7 +3421,7 @@ static int done_command(struct parse_context *ctx) | |||
3384 | #if 0 /* Instead we emit error message at run time */ | 3421 | #if 0 /* Instead we emit error message at run time */ |
3385 | if (ctx->pending_redirect) { | 3422 | if (ctx->pending_redirect) { |
3386 | /* For example, "cmd >" (no filename to redirect to) */ | 3423 | /* For example, "cmd >" (no filename to redirect to) */ |
3387 | die_if_script("syntax error: %s", "invalid redirect"); | 3424 | syntax_error("invalid redirect"); |
3388 | ctx->pending_redirect = NULL; | 3425 | ctx->pending_redirect = NULL; |
3389 | } | 3426 | } |
3390 | #endif | 3427 | #endif |
@@ -3950,7 +3987,7 @@ static int parse_redirect(struct parse_context *ctx, | |||
3950 | #if 0 /* Instead we emit error message at run time */ | 3987 | #if 0 /* Instead we emit error message at run time */ |
3951 | if (ctx->pending_redirect) { | 3988 | if (ctx->pending_redirect) { |
3952 | /* For example, "cmd > <file" */ | 3989 | /* For example, "cmd > <file" */ |
3953 | die_if_script("syntax error: %s", "invalid redirect"); | 3990 | syntax_error("invalid redirect"); |
3954 | } | 3991 | } |
3955 | #endif | 3992 | #endif |
3956 | /* Set ctx->pending_redirect, so we know what to do at the | 3993 | /* Set ctx->pending_redirect, so we know what to do at the |
@@ -5022,10 +5059,16 @@ static struct pipe *parse_stream(char **pstring, | |||
5022 | else | 5059 | else |
5023 | o_free_unsafe(&ctx.as_string); | 5060 | o_free_unsafe(&ctx.as_string); |
5024 | #endif | 5061 | #endif |
5025 | debug_leave(); | 5062 | if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) { |
5063 | /* Example: bare "{ }", "()" */ | ||
5064 | G.last_exitcode = 2; /* bash compat */ | ||
5065 | syntax_error_unexpected_ch(ch); | ||
5066 | goto parse_error2; | ||
5067 | } | ||
5026 | debug_printf_parse("parse_stream return %p: " | 5068 | debug_printf_parse("parse_stream return %p: " |
5027 | "end_trigger char found\n", | 5069 | "end_trigger char found\n", |
5028 | ctx.list_head); | 5070 | ctx.list_head); |
5071 | debug_leave(); | ||
5029 | return ctx.list_head; | 5072 | return ctx.list_head; |
5030 | } | 5073 | } |
5031 | } | 5074 | } |
@@ -5283,8 +5326,8 @@ static struct pipe *parse_stream(char **pstring, | |||
5283 | /* proper use of this character is caught by end_trigger: | 5326 | /* proper use of this character is caught by end_trigger: |
5284 | * if we see {, we call parse_group(..., end_trigger='}') | 5327 | * if we see {, we call parse_group(..., end_trigger='}') |
5285 | * and it will match } earlier (not here). */ | 5328 | * and it will match } earlier (not here). */ |
5286 | syntax_error_unexpected_ch(ch); | ||
5287 | G.last_exitcode = 2; | 5329 | G.last_exitcode = 2; |
5330 | syntax_error_unexpected_ch(ch); | ||
5288 | goto parse_error2; | 5331 | goto parse_error2; |
5289 | default: | 5332 | default: |
5290 | if (HUSH_DEBUG) | 5333 | if (HUSH_DEBUG) |
@@ -5514,7 +5557,7 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) | |||
5514 | if (errmsg_p) | 5557 | if (errmsg_p) |
5515 | *errmsg_p = math_state.errmsg; | 5558 | *errmsg_p = math_state.errmsg; |
5516 | if (math_state.errmsg) | 5559 | if (math_state.errmsg) |
5517 | die_if_script(math_state.errmsg); | 5560 | msg_and_die_if_script(math_state.errmsg); |
5518 | return res; | 5561 | return res; |
5519 | } | 5562 | } |
5520 | #endif | 5563 | #endif |
@@ -5781,7 +5824,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5781 | /* in bash, len=-n means strlen()-n */ | 5824 | /* in bash, len=-n means strlen()-n */ |
5782 | len = (arith_t)strlen(val) - beg + len; | 5825 | len = (arith_t)strlen(val) - beg + len; |
5783 | if (len < 0) /* bash compat */ | 5826 | if (len < 0) /* bash compat */ |
5784 | die_if_script("%s: substring expression < 0", var); | 5827 | msg_and_die_if_script("%s: substring expression < 0", var); |
5785 | } | 5828 | } |
5786 | if (len <= 0 || !val || beg >= strlen(val)) { | 5829 | if (len <= 0 || !val || beg >= strlen(val)) { |
5787 | arith_err: | 5830 | arith_err: |
@@ -5795,7 +5838,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5795 | } | 5838 | } |
5796 | debug_printf_varexp("val:'%s'\n", val); | 5839 | debug_printf_varexp("val:'%s'\n", val); |
5797 | #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ | 5840 | #else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */ |
5798 | die_if_script("malformed ${%s:...}", var); | 5841 | msg_and_die_if_script("malformed ${%s:...}", var); |
5799 | val = NULL; | 5842 | val = NULL; |
5800 | #endif | 5843 | #endif |
5801 | } else { /* one of "-=+?" */ | 5844 | } else { /* one of "-=+?" */ |
@@ -5832,7 +5875,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5832 | exp_word = to_be_freed; | 5875 | exp_word = to_be_freed; |
5833 | if (exp_op == '?') { | 5876 | if (exp_op == '?') { |
5834 | /* mimic bash message */ | 5877 | /* mimic bash message */ |
5835 | die_if_script("%s: %s", | 5878 | msg_and_die_if_script("%s: %s", |
5836 | var, | 5879 | var, |
5837 | exp_word[0] | 5880 | exp_word[0] |
5838 | ? exp_word | 5881 | ? exp_word |
@@ -5849,7 +5892,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
5849 | /* ${var=[word]} or ${var:=[word]} */ | 5892 | /* ${var=[word]} or ${var:=[word]} */ |
5850 | if (isdigit(var[0]) || var[0] == '#') { | 5893 | if (isdigit(var[0]) || var[0] == '#') { |
5851 | /* mimic bash message */ | 5894 | /* mimic bash message */ |
5852 | die_if_script("$%s: cannot assign in this way", var); | 5895 | msg_and_die_if_script("$%s: cannot assign in this way", var); |
5853 | val = NULL; | 5896 | val = NULL; |
5854 | } else { | 5897 | } else { |
5855 | char *new_var = xasprintf("%s=%s", var, val); | 5898 | char *new_var = xasprintf("%s=%s", var, val); |
@@ -6698,7 +6741,8 @@ static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd) | |||
6698 | int moved_to; | 6741 | int moved_to; |
6699 | int i; | 6742 | int i; |
6700 | 6743 | ||
6701 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 6744 | i = 0; |
6745 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | ||
6702 | /* If we collide with an already moved fd... */ | 6746 | /* If we collide with an already moved fd... */ |
6703 | if (fd == sq[i].moved_to) { | 6747 | if (fd == sq[i].moved_to) { |
6704 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); | 6748 | sq[i].moved_to = fcntl_F_DUPFD(sq[i].moved_to, avoid_fd); |
@@ -6726,7 +6770,8 @@ static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd) | |||
6726 | { | 6770 | { |
6727 | int i; | 6771 | int i; |
6728 | 6772 | ||
6729 | if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) { | 6773 | i = 0; |
6774 | if (sq) for (; sq[i].orig_fd >= 0; i++) { | ||
6730 | /* If we collide with an already moved fd... */ | 6775 | /* If we collide with an already moved fd... */ |
6731 | if (fd == sq[i].orig_fd) { | 6776 | if (fd == sq[i].orig_fd) { |
6732 | /* Examples: | 6777 | /* Examples: |
@@ -6863,7 +6908,7 @@ static int setup_redirects(struct command *prog, struct squirrel **sqp) | |||
6863 | * "cmd >" (no filename) | 6908 | * "cmd >" (no filename) |
6864 | * "cmd > <file" (2nd redirect starts too early) | 6909 | * "cmd > <file" (2nd redirect starts too early) |
6865 | */ | 6910 | */ |
6866 | die_if_script("syntax error: %s", "invalid redirect"); | 6911 | syntax_error("invalid redirect"); |
6867 | continue; | 6912 | continue; |
6868 | } | 6913 | } |
6869 | mode = redir_table[redir->rd_type].mode; | 6914 | mode = redir_table[redir->rd_type].mode; |
@@ -7363,8 +7408,10 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7363 | */ | 7408 | */ |
7364 | close_saved_fds_and_FILE_fds(); | 7409 | close_saved_fds_and_FILE_fds(); |
7365 | //FIXME: should also close saved redir fds | 7410 | //FIXME: should also close saved redir fds |
7411 | /* Without this, "rm -i FILE" can't be ^C'ed: */ | ||
7412 | switch_off_special_sigs(G.special_sig_mask); | ||
7366 | debug_printf_exec("running applet '%s'\n", argv[0]); | 7413 | debug_printf_exec("running applet '%s'\n", argv[0]); |
7367 | run_applet_no_and_exit(a, argv[0], argv); | 7414 | run_noexec_applet_and_exit(a, argv[0], argv); |
7368 | } | 7415 | } |
7369 | # endif | 7416 | # endif |
7370 | /* Re-exec ourselves */ | 7417 | /* Re-exec ourselves */ |
@@ -8045,6 +8092,24 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8045 | add_vars(old_vars); | 8092 | add_vars(old_vars); |
8046 | /* clean_up_and_ret0: */ | 8093 | /* clean_up_and_ret0: */ |
8047 | restore_redirects(squirrel); | 8094 | restore_redirects(squirrel); |
8095 | /* | ||
8096 | * Try "usleep 99999999" + ^C + "echo $?" | ||
8097 | * with FEATURE_SH_NOFORK=y. | ||
8098 | */ | ||
8099 | if (!funcp) { | ||
8100 | /* It was builtin or nofork. | ||
8101 | * if this would be a real fork/execed program, | ||
8102 | * it should have died if a fatal sig was received. | ||
8103 | * But OTOH, there was no separate process, | ||
8104 | * the sig was sent to _shell_, not to non-existing | ||
8105 | * child. | ||
8106 | * Let's just handle ^C only, this one is obvious: | ||
8107 | * we aren't ok with exitcode 0 when ^C was pressed | ||
8108 | * during builtin/nofork. | ||
8109 | */ | ||
8110 | if (sigismember(&G.pending_set, SIGINT)) | ||
8111 | rcode = 128 + SIGINT; | ||
8112 | } | ||
8048 | clean_up_and_ret1: | 8113 | clean_up_and_ret1: |
8049 | free(argv_expanded); | 8114 | free(argv_expanded); |
8050 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) | 8115 | IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;) |
@@ -8060,6 +8125,14 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
8060 | if (rcode == 0) { | 8125 | if (rcode == 0) { |
8061 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", | 8126 | debug_printf_exec(": run_nofork_applet '%s' '%s'...\n", |
8062 | argv_expanded[0], argv_expanded[1]); | 8127 | argv_expanded[0], argv_expanded[1]); |
8128 | /* | ||
8129 | * Note: signals (^C) can't interrupt here. | ||
8130 | * We remember them and they will be acted upon | ||
8131 | * after applet returns. | ||
8132 | * This makes applets which can run for a long time | ||
8133 | * and/or wait for user input ineligible for NOFORK: | ||
8134 | * for example, "yes" or "rm" (rm -i waits for input). | ||
8135 | */ | ||
8063 | rcode = run_nofork_applet(n, argv_expanded); | 8136 | rcode = run_nofork_applet(n, argv_expanded); |
8064 | } | 8137 | } |
8065 | goto clean_up_and_ret; | 8138 | goto clean_up_and_ret; |
@@ -8491,7 +8564,7 @@ static int run_list(struct pipe *pi) | |||
8491 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; | 8564 | G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid; |
8492 | G.last_bg_pid_exitcode = 0; | 8565 | G.last_bg_pid_exitcode = 0; |
8493 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); | 8566 | debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n"); |
8494 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash says 0 */ | 8567 | /* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */ |
8495 | rcode = EXIT_SUCCESS; | 8568 | rcode = EXIT_SUCCESS; |
8496 | goto check_traps; | 8569 | goto check_traps; |
8497 | } else { | 8570 | } else { |
@@ -8600,6 +8673,10 @@ static void install_sighandlers(unsigned mask) | |||
8600 | */ | 8673 | */ |
8601 | if (sig == SIGCHLD) | 8674 | if (sig == SIGCHLD) |
8602 | continue; | 8675 | continue; |
8676 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | ||
8677 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | ||
8678 | */ | ||
8679 | //if (sig == SIGHUP) continue; - TODO? | ||
8603 | if (old_handler == SIG_IGN) { | 8680 | if (old_handler == SIG_IGN) { |
8604 | /* oops... restore back to IGN, and record this fact */ | 8681 | /* oops... restore back to IGN, and record this fact */ |
8605 | install_sighandler(sig, old_handler); | 8682 | install_sighandler(sig, old_handler); |
@@ -9381,13 +9458,20 @@ static int FAST_FUNC builtin_read(char **argv) | |||
9381 | char *opt_p = NULL; | 9458 | char *opt_p = NULL; |
9382 | char *opt_t = NULL; | 9459 | char *opt_t = NULL; |
9383 | char *opt_u = NULL; | 9460 | char *opt_u = NULL; |
9461 | char *opt_d = NULL; /* optimized out if !BASH */ | ||
9384 | const char *ifs; | 9462 | const char *ifs; |
9385 | int read_flags; | 9463 | int read_flags; |
9386 | 9464 | ||
9387 | /* "!": do not abort on errors. | 9465 | /* "!": do not abort on errors. |
9388 | * Option string must start with "sr" to match BUILTIN_READ_xxx | 9466 | * Option string must start with "sr" to match BUILTIN_READ_xxx |
9389 | */ | 9467 | */ |
9390 | read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u); | 9468 | read_flags = getopt32(argv, |
9469 | #if BASH_READ_D | ||
9470 | "!srn:p:t:u:d:", &opt_n, &opt_p, &opt_t, &opt_u, &opt_d | ||
9471 | #else | ||
9472 | "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u | ||
9473 | #endif | ||
9474 | ); | ||
9391 | if (read_flags == (uint32_t)-1) | 9475 | if (read_flags == (uint32_t)-1) |
9392 | return EXIT_FAILURE; | 9476 | return EXIT_FAILURE; |
9393 | argv += optind; | 9477 | argv += optind; |
@@ -9401,7 +9485,8 @@ static int FAST_FUNC builtin_read(char **argv) | |||
9401 | opt_n, | 9485 | opt_n, |
9402 | opt_p, | 9486 | opt_p, |
9403 | opt_t, | 9487 | opt_t, |
9404 | opt_u | 9488 | opt_u, |
9489 | opt_d | ||
9405 | ); | 9490 | ); |
9406 | 9491 | ||
9407 | if ((uintptr_t)r == 1 && errno == EINTR) { | 9492 | if ((uintptr_t)r == 1 && errno == EINTR) { |
@@ -9786,6 +9871,93 @@ static int FAST_FUNC builtin_shift(char **argv) | |||
9786 | return EXIT_FAILURE; | 9871 | return EXIT_FAILURE; |
9787 | } | 9872 | } |
9788 | 9873 | ||
9874 | #if ENABLE_HUSH_GETOPTS | ||
9875 | static int FAST_FUNC builtin_getopts(char **argv) | ||
9876 | { | ||
9877 | /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html | ||
9878 | |||
9879 | TODO: | ||
9880 | If a required argument is not found, and getopts is not silent, | ||
9881 | a question mark (?) is placed in VAR, OPTARG is unset, and a | ||
9882 | diagnostic message is printed. If getopts is silent, then a | ||
9883 | colon (:) is placed in VAR and OPTARG is set to the option | ||
9884 | character found. | ||
9885 | |||
9886 | Test that VAR is a valid variable name? | ||
9887 | |||
9888 | "Whenever the shell is invoked, OPTIND shall be initialized to 1" | ||
9889 | */ | ||
9890 | char cbuf[2]; | ||
9891 | const char *cp, *optstring, *var; | ||
9892 | int c, exitcode; | ||
9893 | |||
9894 | optstring = *++argv; | ||
9895 | if (!optstring || !(var = *++argv)) { | ||
9896 | bb_error_msg("usage: getopts OPTSTRING VAR [ARGS]"); | ||
9897 | return EXIT_FAILURE; | ||
9898 | } | ||
9899 | |||
9900 | c = 0; | ||
9901 | if (optstring[0] != ':') { | ||
9902 | cp = get_local_var_value("OPTERR"); | ||
9903 | /* 0 if "OPTERR=0", 1 otherwise */ | ||
9904 | c = (!cp || NOT_LONE_CHAR(cp, '0')); | ||
9905 | } | ||
9906 | opterr = c; | ||
9907 | cp = get_local_var_value("OPTIND"); | ||
9908 | optind = cp ? atoi(cp) : 0; | ||
9909 | optarg = NULL; | ||
9910 | cbuf[1] = '\0'; | ||
9911 | |||
9912 | /* getopts stops on first non-option. Add "+" to force that */ | ||
9913 | /*if (optstring[0] != '+')*/ { | ||
9914 | char *s = alloca(strlen(optstring) + 2); | ||
9915 | sprintf(s, "+%s", optstring); | ||
9916 | optstring = s; | ||
9917 | } | ||
9918 | |||
9919 | if (argv[1]) | ||
9920 | argv[0] = G.global_argv[0]; /* for error messages */ | ||
9921 | else | ||
9922 | argv = G.global_argv; | ||
9923 | c = getopt(string_array_len(argv), argv, optstring); | ||
9924 | |||
9925 | /* Set OPTARG */ | ||
9926 | /* Always set or unset, never left as-is, even on exit/error: | ||
9927 | * "If no option was found, or if the option that was found | ||
9928 | * does not have an option-argument, OPTARG shall be unset." | ||
9929 | */ | ||
9930 | cp = optarg; | ||
9931 | if (c == '?') { | ||
9932 | /* If ":optstring" and unknown option is seen, | ||
9933 | * it is stored to OPTARG. | ||
9934 | */ | ||
9935 | if (optstring[1] == ':') { | ||
9936 | cbuf[0] = optopt; | ||
9937 | cp = cbuf; | ||
9938 | } | ||
9939 | } | ||
9940 | if (cp) | ||
9941 | set_local_var_from_halves("OPTARG", cp); | ||
9942 | else | ||
9943 | unset_local_var("OPTARG"); | ||
9944 | |||
9945 | /* Convert -1 to "?" */ | ||
9946 | exitcode = EXIT_SUCCESS; | ||
9947 | if (c < 0) { /* -1: end of options */ | ||
9948 | exitcode = EXIT_FAILURE; | ||
9949 | c = '?'; | ||
9950 | } | ||
9951 | |||
9952 | /* Set VAR and OPTIND */ | ||
9953 | cbuf[0] = c; | ||
9954 | set_local_var_from_halves(var, cbuf); | ||
9955 | set_local_var_from_halves("OPTIND", utoa(optind)); | ||
9956 | |||
9957 | return exitcode; | ||
9958 | } | ||
9959 | #endif | ||
9960 | |||
9789 | static int FAST_FUNC builtin_source(char **argv) | 9961 | static int FAST_FUNC builtin_source(char **argv) |
9790 | { | 9962 | { |
9791 | char *arg_path, *filename; | 9963 | char *arg_path, *filename; |
@@ -10178,6 +10350,7 @@ static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid | |||
10178 | /* So, did we get a signal? */ | 10350 | /* So, did we get a signal? */ |
10179 | sig = check_and_run_traps(); | 10351 | sig = check_and_run_traps(); |
10180 | if (sig /*&& sig != SIGCHLD - always true */) { | 10352 | if (sig /*&& sig != SIGCHLD - always true */) { |
10353 | /* Do this for any (non-ignored) signal, not only for ^C */ | ||
10181 | ret = 128 + sig; | 10354 | ret = 128 + sig; |
10182 | break; | 10355 | break; |
10183 | } | 10356 | } |
@@ -10344,6 +10517,41 @@ static int FAST_FUNC builtin_return(char **argv) | |||
10344 | } | 10517 | } |
10345 | #endif | 10518 | #endif |
10346 | 10519 | ||
10520 | #if ENABLE_HUSH_TIMES | ||
10521 | static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM) | ||
10522 | { | ||
10523 | static const uint8_t times_tbl[] ALIGN1 = { | ||
10524 | ' ', offsetof(struct tms, tms_utime), | ||
10525 | '\n', offsetof(struct tms, tms_stime), | ||
10526 | ' ', offsetof(struct tms, tms_cutime), | ||
10527 | '\n', offsetof(struct tms, tms_cstime), | ||
10528 | 0 | ||
10529 | }; | ||
10530 | const uint8_t *p; | ||
10531 | unsigned clk_tck; | ||
10532 | struct tms buf; | ||
10533 | |||
10534 | clk_tck = bb_clk_tck(); | ||
10535 | |||
10536 | times(&buf); | ||
10537 | p = times_tbl; | ||
10538 | do { | ||
10539 | unsigned sec, frac; | ||
10540 | unsigned long t; | ||
10541 | t = *(clock_t *)(((char *) &buf) + p[1]); | ||
10542 | sec = t / clk_tck; | ||
10543 | frac = t % clk_tck; | ||
10544 | printf("%um%u.%03us%c", | ||
10545 | sec / 60, sec % 60, | ||
10546 | (frac * 1000) / clk_tck, | ||
10547 | p[0]); | ||
10548 | p += 2; | ||
10549 | } while (*p); | ||
10550 | |||
10551 | return EXIT_SUCCESS; | ||
10552 | } | ||
10553 | #endif | ||
10554 | |||
10347 | #if ENABLE_HUSH_MEMLEAK | 10555 | #if ENABLE_HUSH_MEMLEAK |
10348 | static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) | 10556 | static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM) |
10349 | { | 10557 | { |