diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2018-01-12 14:41:45 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2018-01-12 14:41:45 +0100 |
commit | 5700029c65f2218b5fceca77a9140aff6d882aab (patch) | |
tree | 39c98b5ad7a7b0eab4ec18e4111b8b6677066f19 | |
parent | cca7c611f26d98415c0f986e5a5e731ab5e379ff (diff) | |
download | busybox-w32-5700029c65f2218b5fceca77a9140aff6d882aab.tar.gz busybox-w32-5700029c65f2218b5fceca77a9140aff6d882aab.tar.bz2 busybox-w32-5700029c65f2218b5fceca77a9140aff6d882aab.zip |
hush: implement "command -v -V"
function old new delta
pseudo_exec_argv 231 374 +143
if_command_vV_print_and_exit - 127 +127
builtin_set 267 273 +6
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 2/0 up/down: 276/0) Total: 276 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | shell/hush.c | 115 |
1 files changed, 84 insertions, 31 deletions
diff --git a/shell/hush.c b/shell/hush.c index 93779ba1e..196bdbe97 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -47,18 +47,13 @@ | |||
47 | * follow IFS rules more precisely, including update semantics | 47 | * follow IFS rules more precisely, including update semantics |
48 | * tilde expansion | 48 | * tilde expansion |
49 | * aliases | 49 | * aliases |
50 | * builtins mandated by standards we don't support: | 50 | * "command" missing features: |
51 | * [un]alias, command, fc: | 51 | * command -p CMD: run CMD using default $PATH |
52 | * command -v CMD: print "/path/to/CMD" | 52 | * (can use this to override standalone shell as well?) |
53 | * prints "CMD" for builtins | ||
54 | * prints "alias ALIAS='EXPANSION'" for aliases | ||
55 | * prints nothing and sets $? to 1 if not found | ||
56 | * command -V CMD: print "CMD is /path/CMD|a shell builtin|etc" | ||
57 | * command [-p] CMD: run CMD, even if a function CMD also exists | ||
58 | * (can use this to override standalone shell as well) | ||
59 | * -p: use default $PATH | ||
60 | * command BLTIN: disables special-ness (e.g. errors do not abort) | 53 | * command BLTIN: disables special-ness (e.g. errors do not abort) |
61 | * NB: so far, only naked "command CMD" is implemented. | 54 | * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard) |
55 | * builtins mandated by standards we don't support: | ||
56 | * [un]alias, fc: | ||
62 | * fc -l[nr] [BEG] [END]: list range of commands in history | 57 | * fc -l[nr] [BEG] [END]: list range of commands in history |
63 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands | 58 | * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands |
64 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP | 59 | * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP |
@@ -7337,6 +7332,30 @@ static void dump_cmd_in_x_mode(char **argv) | |||
7337 | # define dump_cmd_in_x_mode(argv) ((void)0) | 7332 | # define dump_cmd_in_x_mode(argv) ((void)0) |
7338 | #endif | 7333 | #endif |
7339 | 7334 | ||
7335 | #if ENABLE_HUSH_COMMAND | ||
7336 | static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation) | ||
7337 | { | ||
7338 | char *to_free; | ||
7339 | if (!opt_vV) | ||
7340 | return; | ||
7341 | |||
7342 | to_free = NULL; | ||
7343 | if (!explanation) { | ||
7344 | char *path = getenv("PATH"); | ||
7345 | explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */ | ||
7346 | if (opt_vV != 'V') | ||
7347 | cmd = to_free; /* -v PROG prints "/path/to/PROG" */ | ||
7348 | } | ||
7349 | if (explanation) | ||
7350 | printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation); | ||
7351 | free(to_free); | ||
7352 | fflush_all(); | ||
7353 | _exit(explanation == NULL); /* exit 1 if PROG was not found */ | ||
7354 | } | ||
7355 | #else | ||
7356 | # define if_command_vV_print_and_exit(a,b,c) ((void)0) | ||
7357 | #endif | ||
7358 | |||
7340 | #if BB_MMU | 7359 | #if BB_MMU |
7341 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ | 7360 | #define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \ |
7342 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) | 7361 | pseudo_exec_argv(argv, assignment_cnt, argv_expanded) |
@@ -7357,7 +7376,11 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7357 | char **argv, int assignment_cnt, | 7376 | char **argv, int assignment_cnt, |
7358 | char **argv_expanded) | 7377 | char **argv_expanded) |
7359 | { | 7378 | { |
7379 | const struct built_in_command *x; | ||
7360 | char **new_env; | 7380 | char **new_env; |
7381 | #if ENABLE_HUSH_COMMAND | ||
7382 | char opt_vV = 0; | ||
7383 | #endif | ||
7361 | 7384 | ||
7362 | new_env = expand_assignments(argv, assignment_cnt); | 7385 | new_env = expand_assignments(argv, assignment_cnt); |
7363 | dump_cmd_in_x_mode(new_env); | 7386 | dump_cmd_in_x_mode(new_env); |
@@ -7406,30 +7429,58 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7406 | } | 7429 | } |
7407 | #endif | 7430 | #endif |
7408 | 7431 | ||
7432 | #if ENABLE_HUSH_COMMAND | ||
7433 | /* "command BAR": run BAR without looking it up among functions | ||
7434 | * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1 | ||
7435 | * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}" | ||
7436 | */ | ||
7437 | while (strcmp(argv[0], "command") == 0 && argv[1]) { | ||
7438 | char *p; | ||
7439 | |||
7440 | argv++; | ||
7441 | p = *argv; | ||
7442 | if (p[0] != '-' || !p[1]) | ||
7443 | continue; /* bash allows "command command command [-OPT] BAR" */ | ||
7444 | |||
7445 | for (;;) { | ||
7446 | p++; | ||
7447 | switch (*p) { | ||
7448 | case '\0': | ||
7449 | argv++; | ||
7450 | p = *argv; | ||
7451 | if (p[0] != '-' || !p[1]) | ||
7452 | goto after_opts; | ||
7453 | continue; /* next arg is also -opts, process it too */ | ||
7454 | case 'v': | ||
7455 | case 'V': | ||
7456 | opt_vV = *p; | ||
7457 | continue; | ||
7458 | default: | ||
7459 | bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]); | ||
7460 | } | ||
7461 | } | ||
7462 | } | ||
7463 | after_opts: | ||
7464 | # if ENABLE_HUSH_FUNCTIONS | ||
7465 | if (opt_vV && find_function(argv[0])) | ||
7466 | if_command_vV_print_and_exit(opt_vV, argv[0], "a function"); | ||
7467 | # endif | ||
7468 | #endif | ||
7469 | |||
7409 | /* Check if the command matches any of the builtins. | 7470 | /* Check if the command matches any of the builtins. |
7410 | * Depending on context, this might be redundant. But it's | 7471 | * Depending on context, this might be redundant. But it's |
7411 | * easier to waste a few CPU cycles than it is to figure out | 7472 | * easier to waste a few CPU cycles than it is to figure out |
7412 | * if this is one of those cases. | 7473 | * if this is one of those cases. |
7413 | */ | 7474 | */ |
7414 | { | 7475 | /* Why "BB_MMU ? :" difference in logic? - |
7415 | const struct built_in_command *x; | 7476 | * On NOMMU, it is more expensive to re-execute shell |
7416 | 7477 | * just in order to run echo or test builtin. | |
7417 | #if ENABLE_HUSH_COMMAND | 7478 | * It's better to skip it here and run corresponding |
7418 | /* This loop effectively makes "command BAR" run BAR without | 7479 | * non-builtin later. */ |
7419 | * looking it up among functions. | 7480 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); |
7420 | */ | 7481 | if (x) { |
7421 | while (strcmp(argv[0], "command") == 0 && argv[1]) | 7482 | if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin"); |
7422 | argv++; | 7483 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); |
7423 | //TODO: implement -Vvp and "disable dying if BAR is a builtin" behavior | ||
7424 | #endif | ||
7425 | /* On NOMMU, it is more expensive to re-execute shell | ||
7426 | * just in order to run echo or test builtin. | ||
7427 | * It's better to skip it here and run corresponding | ||
7428 | * non-builtin later. */ | ||
7429 | x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]); | ||
7430 | if (x) { | ||
7431 | exec_builtin(&nommu_save->argv_from_re_execing, x, argv); | ||
7432 | } | ||
7433 | } | 7484 | } |
7434 | 7485 | ||
7435 | #if ENABLE_FEATURE_SH_STANDALONE | 7486 | #if ENABLE_FEATURE_SH_STANDALONE |
@@ -7437,6 +7488,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7437 | { | 7488 | { |
7438 | int a = find_applet_by_name(argv[0]); | 7489 | int a = find_applet_by_name(argv[0]); |
7439 | if (a >= 0) { | 7490 | if (a >= 0) { |
7491 | if_command_vV_print_and_exit(opt_vV, argv[0], "an applet"); | ||
7440 | # if BB_MMU /* see above why on NOMMU it is not allowed */ | 7492 | # if BB_MMU /* see above why on NOMMU it is not allowed */ |
7441 | if (APPLET_IS_NOEXEC(a)) { | 7493 | if (APPLET_IS_NOEXEC(a)) { |
7442 | /* Do not leak open fds from opened script files etc. | 7494 | /* Do not leak open fds from opened script files etc. |
@@ -7466,6 +7518,7 @@ static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save, | |||
7466 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU | 7518 | #if ENABLE_FEATURE_SH_STANDALONE || BB_MMU |
7467 | skip: | 7519 | skip: |
7468 | #endif | 7520 | #endif |
7521 | if_command_vV_print_and_exit(opt_vV, argv[0], NULL); | ||
7469 | execvp_or_die(argv); | 7522 | execvp_or_die(argv); |
7470 | } | 7523 | } |
7471 | 7524 | ||
@@ -9895,7 +9948,7 @@ static int FAST_FUNC builtin_set(char **argv) | |||
9895 | 9948 | ||
9896 | /* Nothing known, so abort */ | 9949 | /* Nothing known, so abort */ |
9897 | error: | 9950 | error: |
9898 | bb_error_msg("set: %s: invalid option", arg); | 9951 | bb_error_msg("%s: %s: invalid option", "set", arg); |
9899 | return EXIT_FAILURE; | 9952 | return EXIT_FAILURE; |
9900 | } | 9953 | } |
9901 | #endif | 9954 | #endif |