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.
This commit is contained in:
Mahmoud Al-Qudsi 2018-10-09 22:33:20 -05:00
parent c6230ddfde
commit e212269ab1
6 changed files with 82 additions and 64 deletions

View file

@ -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`.

View file

@ -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_cmd_t> 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;

View file

@ -30,6 +30,10 @@
#include <sys/ioctl.h>
#endif
#ifdef __FreeBSD__
#include <sys/sysctl.h>
#endif
#include <algorithm>
#include <memory> // IWYU pragma: keep
#include <type_traits>
@ -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 : "");
}

View file

@ -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<const wcstring> {
} // namespace std
#endif
/// Get the absolute path to the fish executable itself
std::string get_executable_path(const char *fallback);
#endif

View file

@ -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 <sys/sysctl.h>
#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;

View file

@ -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<std::unordered_map<wcstring, wcstring>> wgettext_map;