From 6d87be4d760ceda18d354c6d4c523a9adf50c04b Mon Sep 17 00:00:00 2001 From: Ron Yorston Date: Sun, 1 May 2022 10:20:16 +0100 Subject: which,ash: changes to which/command/type Change how 'which' detects if it was run from a standalone shell: the shell passes the undocumented '-s' option. This is stricter and more reliable than the previous method of checking the name of the binary. Add a function to determine the binary associated with a given applet name. This makes it possible for 'which' and 'command -v' to list the correct binary even for applets other than 'busybox'. For example, when the binary is called 'sh.exe' 'which sh' will report its path. In standalone shell mode 'command -V' and 'type' now report "xxx is a builtin applet" rather than "xxx is xxx", which is true but not very illuminating. (GitHub issue #248) --- debianutils/which.c | 50 +++++++++++++++++++++++++++++--------------------- include/mingw.h | 1 + shell/ash.c | 18 ++++++++++++++++-- win32/mingw.c | 21 +++++++++++++++++++++ 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/debianutils/which.c b/debianutils/which.c index 815ac71da..d00b92e0b 100644 --- a/debianutils/which.c +++ b/debianutils/which.c @@ -12,7 +12,12 @@ //config: which is used to find programs in your PATH and //config: print out their pathnames. -//applet:IF_WHICH(APPLET_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which)) +// NOTE: For WIN32 this applet is NOEXEC as alloc_system_drive() and +// find_executable() both allocate memory. And find_executable() +// calls alloc_system_drive(). + +//applet:IF_PLATFORM_MINGW32(IF_WHICH(APPLET_NOEXEC(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which))) +//applet:IF_PLATFORM_POSIX(IF_WHICH(APPLET_NOFORK(which, which, BB_DIR_USR_BIN, BB_SUID_DROP, which))) //kbuild:lib-$(CONFIG_WHICH) += which.o @@ -28,6 +33,13 @@ #include "libbb.h" +#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE +enum { + OPT_a = (1 << 0), + OPT_s = (1 << 1) +}; +#endif + int which_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int which_main(int argc UNUSED_PARAM, char **argv) { @@ -36,9 +48,7 @@ int which_main(int argc UNUSED_PARAM, char **argv) /* This sizeof(): bb_default_root_path is shorter than BB_PATH_ROOT_PATH */ char buf[sizeof(BB_PATH_ROOT_PATH)]; #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE - /* If we were run as 'which.exe' skip standalone shell behaviour */ - int sh_standalone = - is_suffixed_with_case(bb_busybox_exec_path, "which.exe") == NULL; + int sh_standalone; #endif env_path = getenv("PATH"); @@ -46,28 +56,25 @@ int which_main(int argc UNUSED_PARAM, char **argv) /* env_path must be writable, and must not alloc, so... */ env_path = strcpy(buf, bb_default_root_path); +#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE + /* '-s' option indicates we were run from a standalone shell */ + getopt32(argv, "^" "as" "\0" "-1"/*at least one arg*/); + sh_standalone = option_mask32 & OPT_s; + option_mask32 &= ~OPT_s; +#else getopt32(argv, "^" "a" "\0" "-1"/*at least one arg*/); +#endif argv += optind; do { int missing = 1; #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE - if (sh_standalone) { - if (strcmp(*argv, "busybox") == 0 && - is_prefixed_with_case(bb_basename(bb_busybox_exec_path), - "busybox")) { - missing = 0; - puts(bb_busybox_exec_path); - if (!option_mask32) /* -a not set */ - break; - } - else if (find_applet_by_name(*argv) >= 0) { - missing = 0; - puts(*argv); - if (!option_mask32) /* -a not set */ - break; - } + if (sh_standalone && find_applet_by_name(*argv) >= 0) { + missing = 0; + puts(applet_to_exe(*argv)); + if (!option_mask32) /* -a not set */ + break; } #endif @@ -87,15 +94,16 @@ int which_main(int argc UNUSED_PARAM, char **argv) puts(bs_to_slash(path)); } # if ENABLE_FEATURE_SH_STANDALONE - else if (sh_standalone) { + else if (sh_standalone && unix_path(*argv)) { const char *name = bb_basename(*argv); - if (unix_path(*argv) && find_applet_by_name(name) >= 0) { + if (find_applet_by_name(name) >= 0) { missing = 0; puts(name); } } # endif + free(path); #endif } else { char *path; diff --git a/include/mingw.h b/include/mingw.h index 8a9610898..5e01b874e 100644 --- a/include/mingw.h +++ b/include/mingw.h @@ -584,3 +584,4 @@ int unix_path(const char *path); int has_path(const char *file); int is_relative_path(const char *path); char *get_last_slash(const char *path); +const char *applet_to_exe(const char *name); diff --git a/shell/ash.c b/shell/ash.c index afb865146..97075ed5f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -8904,6 +8904,16 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c (fs && fs->fpid == FS_SHELLEXEC)) { /* mingw-w64's getopt() uses __argv[0] as the program name */ __argv[0] = (char *)cmd; + /* 'which' wants to know if it was invoked from a standalone + * shell. Use the spare element of argv to add a flag, but + * not if the first argument is '--help', that's a special + * case. */ + if (strcmp(argv[0], "which") == 0 && + (argv[1] == NULL || strcmp(argv[1], "--help") != 0)) { + --argv; + argv[0] = argv[1]; + argv[1] = (char *)"-s"; + } # else if (APPLET_IS_NOEXEC(applet_no)) { # endif @@ -9456,7 +9466,12 @@ describe_command(char *command, const char *path, int describe_command_verbose) #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE if (j < -1) { p = (char *)bb_basename(command); - goto describe; + if (describe_command_verbose) { + out1fmt(" is a builtin applet"); + } else { + out1str(applet_to_exe(p)); + } + break; } #endif if (j < 0) { @@ -9474,7 +9489,6 @@ describe_command(char *command, const char *path, int describe_command_verbose) #if ENABLE_PLATFORM_MINGW32 add_win32_extension(p); bs_to_slash(p); - IF_FEATURE_SH_STANDALONE(describe:) #endif if (describe_command_verbose) { out1fmt(" is %s", p); diff --git a/win32/mingw.c b/win32/mingw.c index 063a0c546..0fa2a1b8f 100644 --- a/win32/mingw.c +++ b/win32/mingw.c @@ -2130,3 +2130,24 @@ int is_relative_path(const char *path) { return !is_dir_sep(path[0]) && !has_dos_drive_prefix(path); } + +#if ENABLE_FEATURE_SH_STANDALONE +/* + * In standalone shell mode it's possible there's no binary file + * corresponding to an applet name. There's one case where it's + * easy to determine the corresponding binary: if the applet name + * matches the file name from bb_busybox_exec_path (with appropriate + * allowance for 'busybox*.exe'). + */ +const char *applet_to_exe(const char *name) +{ + const char *exefile = bb_basename(bb_busybox_exec_path); + const char *exesuff = is_prefixed_with_case(exefile, name); + + if (exesuff && (strcmp(name, "busybox") == 0 || + strcasecmp(exesuff, ".exe") == 0)) { + return bb_busybox_exec_path; + } + return name; +} +#endif -- cgit v1.2.3-55-g6feb