From e212269ab1b29a1456ce47c335ae57fb5789ea79 Mon Sep 17 00:00:00 2001 From: Mahmoud Al-Qudsi Date: Tue, 9 Oct 2018 22:33:20 -0500 Subject: [PATCH] Add `status fish-path` Retrieves the fully resolved path to the currently executing fish binary (regardless of PATH). Can be used to ensure that the same fish is launched again from a script. `get_executable_path()` moved from fish binary to libfish, also cleaned up some duplicated (but differing!) definitions of PATH_MAX (which was used by that function) in the process. --- doc_src/status.txt | 3 +++ src/builtin_status.cpp | 17 ++++++++++++- src/common.cpp | 50 ++++++++++++++++++++++++++++++++++++++ src/common.h | 13 ++++++++++ src/fish.cpp | 54 ------------------------------------------ src/wutil.cpp | 9 ------- 6 files changed, 82 insertions(+), 64 deletions(-) diff --git a/doc_src/status.txt b/doc_src/status.txt index 905cfb2f3..b69996d57 100644 --- a/doc_src/status.txt +++ b/doc_src/status.txt @@ -12,6 +12,7 @@ status is-no-job-control status is-full-job-control status is-interactive-job-control status filename +status fish-path status function status line-number status stack-trace @@ -44,6 +45,8 @@ The following operations (sub-commands) are available: - `filename` prints the filename of the currently running script. Also `current-filename`, `-f` or `--current-filename`. +- `fish-path` prints the absolute path to the currently executing instance of fish. + - `function` prints the name of the currently called function if able, when missing displays "Not a function" (or equivalent translated string). Also `current-function`, `-u` or `--current-function`. diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index adc358af8..c9dba63cc 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -21,6 +21,7 @@ enum status_cmd_t { STATUS_CURRENT_CMD = 1, STATUS_FEATURES, STATUS_FILENAME, + STATUS_FISH_PATH, STATUS_FUNCTION, STATUS_IS_BLOCK, STATUS_IS_BREAKPOINT, @@ -45,6 +46,7 @@ const enum_map status_enum_map[] = { {STATUS_LINE_NUMBER, L"current-line-number"}, {STATUS_FEATURES, L"features"}, {STATUS_FILENAME, L"filename"}, + {STATUS_FISH_PATH, L"fish-path"}, {STATUS_FUNCTION, L"function"}, {STATUS_IS_BLOCK, L"is-block"}, {STATUS_IS_BREAKPOINT, L"is-breakpoint"}, @@ -103,6 +105,7 @@ static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, {L"current-filename", no_argument, NULL, 'f'}, {L"current-line-number", no_argument, NULL, 'n'}, {L"filename", no_argument, NULL, 'f'}, + {L"fish-path", no_argument, NULL, STATUS_FISH_PATH}, {L"is-block", no_argument, NULL, 'b'}, {L"is-command-substitution", no_argument, NULL, 'c'}, {L"is-full-job-control", no_argument, NULL, STATUS_IS_FULL_JOB_CTRL}, @@ -169,6 +172,12 @@ static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high } break; } + case STATUS_FISH_PATH: { + if (!set_status_cmd(cmd, opts, STATUS_FISH_PATH, streams)) { + return STATUS_CMD_ERROR; + } + break; + } case 'L': { opts.level = fish_wcstoi(w.woptarg); if (opts.level < 0 || errno == ERANGE) { @@ -417,7 +426,13 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { streams.out.append(program_name); streams.out.push_back(L'\n'); break; - } + } + case STATUS_FISH_PATH: { + CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) + streams.out.append(str2wcstring(get_executable_path("fish")).c_str()); + streams.out.push_back(L'\n'); + break; + } } return retval; diff --git a/src/common.cpp b/src/common.cpp index ffdbb49e0..7b6ea1f84 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -30,6 +30,10 @@ #include #endif +#ifdef __FreeBSD__ +#include +#endif + #include #include // IWYU pragma: keep #include @@ -2248,3 +2252,49 @@ bool valid_func_name(const wcstring &str) { if (str.find_first_of(L'/') != wcstring::npos) return false; return true; } + +/// Return the path to the current executable. This needs to be realpath'd. +std::string get_executable_path(const char *argv0) { + char buff[PATH_MAX]; + +#if __APPLE__ + // On OS X use it's proprietary API to get the path to the executable. + // This is basically grabbing exec_path after argc, argv, envp, ...: for us + // https://opensource.apple.com/source/adv_cmds/adv_cmds-163/ps/print.c + uint32_t buffSize = sizeof buff; + if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); +#elif __FreeBSD__ + // FreeBSD does not have /proc by default, but it can be mounted as procfs via the + // Linux compatibility layer. Per sysctl(3), passing in a process ID of -1 returns + // the value for the current process. + size_t buff_size = sizeof buff; + int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + int result = sysctl(name, sizeof(name) / sizeof(int), buff, &buff_size, nullptr, 0); + if (result != 0) { + wperror(L"sysctl KERN_PROC_PATHNAME"); + } + else { + return std::string(buff); + } +#else + // On other unixes, fall back to the Linux-ish /proc/ directory + ssize_t len; + len = readlink("/proc/self/exe", buff, sizeof buff - 1); // Linux + if (len == -1) { + len = readlink("/proc/curproc/file", buff, sizeof buff - 1); // other BSDs + if (len == -1) { + len = readlink("/proc/self/path/a.out", buff, sizeof buff - 1); // Solaris + } + } + if (len > 0) { + buff[len] = '\0'; + return std::string(buff); + } +#endif + + // Just return argv0, which probably won't work (i.e. it's not an absolute path or a path + // relative to the working directory, but instead something the caller found via $PATH). We'll + // eventually fall back to the compile time paths. + return std::string(argv0 ? argv0 : ""); +} + diff --git a/src/common.h b/src/common.h index dfc4c7068..91f79b8c2 100644 --- a/src/common.h +++ b/src/common.h @@ -106,6 +106,16 @@ static_assert(false, "Neither NAME_MAX nor MAXNAMELEN is defined!"); #endif #endif +// PATH_MAX may not exist. +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +/// Fallback length of MAXPATHLEN. Hopefully a sane value. +#define PATH_MAX 4096 +#endif +#endif + enum escape_string_style_t { STRING_STYLE_SCRIPT, STRING_STYLE_URL, STRING_STYLE_VAR }; // Flags for unescape_string functions. @@ -1007,4 +1017,7 @@ struct hash { } // namespace std #endif +/// Get the absolute path to the fish executable itself +std::string get_executable_path(const char *fallback); + #endif diff --git a/src/fish.cpp b/src/fish.cpp index bed624932..de90cd2a3 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -54,15 +54,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA #include "signal.h" #include "wutil.h" // IWYU pragma: keep -#ifdef __FreeBSD__ -#include -#endif - -// PATH_MAX may not exist. -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - // container to hold the options specified within the command line class fish_cmd_opts_t { public: @@ -98,51 +89,6 @@ extern "C" { int _NSGetExecutablePath(char *buf, uint32_t *bufsize); } -/// Return the path to the current executable. This needs to be realpath'd. -static std::string get_executable_path(const char *argv0) { - char buff[PATH_MAX]; - -#if __APPLE__ - // On OS X use it's proprietary API to get the path to the executable. - // This is basically grabbing exec_path after argc, argv, envp, ...: for us - // https://opensource.apple.com/source/adv_cmds/adv_cmds-163/ps/print.c - uint32_t buffSize = sizeof buff; - if (_NSGetExecutablePath(buff, &buffSize) == 0) return std::string(buff); -#elif __FreeBSD__ - // FreeBSD does not have /proc by default, but it can be mounted as procfs via the - // Linux compatibility layer. Per sysctl(3), passing in a process ID of -1 returns - // the value for the current process. - size_t buff_size = sizeof buff; - int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - int result = sysctl(name, sizeof(name) / sizeof(int), buff, &buff_size, nullptr, 0); - if (result != 0) { - wperror(L"sysctl KERN_PROC_PATHNAME"); - } - else { - return std::string(buff); - } -#else - // On other unixes, fall back to the Linux-ish /proc/ directory - ssize_t len; - len = readlink("/proc/self/exe", buff, sizeof buff - 1); // Linux - if (len == -1) { - len = readlink("/proc/curproc/file", buff, sizeof buff - 1); // other BSDs - if (len == -1) { - len = readlink("/proc/self/path/a.out", buff, sizeof buff - 1); // Solaris - } - } - if (len > 0) { - buff[len] = '\0'; - return std::string(buff); - } -#endif - - // Just return argv0, which probably won't work (i.e. it's not an absolute path or a path - // relative to the working directory, but instead something the caller found via $PATH). We'll - // eventually fall back to the compile time paths. - return std::string(argv0 ? argv0 : ""); -} - static struct config_paths_t determine_config_directory_paths(const char *argv0) { struct config_paths_t paths; bool done = false; diff --git a/src/wutil.cpp b/src/wutil.cpp index 807a82d50..4a180fca2 100644 --- a/src/wutil.cpp +++ b/src/wutil.cpp @@ -33,15 +33,6 @@ typedef std::string cstring; const file_id_t kInvalidFileID = {(dev_t)-1LL, (ino_t)-1LL, (uint64_t)-1LL, -1, -1, -1, -1}; -#ifndef PATH_MAX -#ifdef MAXPATHLEN -#define PATH_MAX MAXPATHLEN -#else -/// Fallback length of MAXPATHLEN. Hopefully a sane value. -#define PATH_MAX 4096 -#endif -#endif - /// Map used as cache by wgettext. static owning_lock> wgettext_map;