From e5f8cc99f524db8db7c7fbe5957609db63869d0e Mon Sep 17 00:00:00 2001 From: Jun-ichi Takimoto Date: Tue, 11 Apr 2023 21:43:15 +0900 Subject: 51639: new parameter ZSH_EXEPATH (full path of zsh executable) The full pathname is obatined by a reliable method on macOS and systems that support procfs. But on other systems (FreeBSD, OpenBSD, ...) it is guessed from argv[0], PWD and PATH. --- ChangeLog | 6 +++ Doc/Zsh/params.yo | 4 ++ Src/init.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++++- configure.ac | 19 +++++++++ 4 files changed, 145 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 32fd0780d..b2c847da5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2023-04-11 Jun-ichi Takimoto + + * 51639: Doc/Zsh/params.yo, Src/init.c, configure.ac: add new + parameter ZSH_EXEPATH that is set to the full pathname of the + executable file of the current zsh + 2023-04-09 Jun-ichi Takimoto * 51631: Doc/Zsh/params.yo, Src/init.c: initialize $_ by copying diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo index 2db4210eb..57d10b8bd 100644 --- a/Doc/Zsh/params.yo +++ b/Doc/Zsh/params.yo @@ -1112,6 +1112,10 @@ item(tt(ZSH_EXECUTION_STRING))( If the shell was started with the option tt(-c), this contains the argument passed to the option. Otherwise it is not set. ) +vindex(ZSH_EXEPATH) +item(tt(ZSH_EXEPATH))( +Full pathname of the executable file of the current zsh process. +) vindex(ZSH_NAME) item(tt(ZSH_NAME))( Expands to the basename of the command used to invoke this instance diff --git a/Src/init.c b/Src/init.c index 7e98af44c..ffb017e22 100644 --- a/Src/init.c +++ b/Src/init.c @@ -246,6 +246,9 @@ loop(int toplevel, int justonce) static int restricted; +/* original argv[0]. This is already metafied */ +static char *argv0; + /**/ static void parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, @@ -257,7 +260,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr, if (**argv == '-') flags |= PARSEARGS_LOGIN; - argzero = posixzero = *argv++; + argv0 = argzero = posixzero = *argv++; SHIN = 0; /* @@ -893,6 +896,106 @@ init_term(void) return 1; } +/* + * Get (or guess) the absolute pathname of the current zsh exeutable. + * Try OS-specific method, and if it fails, guess the absolute pathname + * from argv0, pwd, and PATH. 'name' and 'cwd' are unmetefied versions of + * argv0 and pwd. + * Returns a zalloc()ed string (not metafied), or NULL if failed. + */ +#ifdef __APPLE__ +#include +#endif + +/**/ +static char * +getmypath(const char *name, const char *cwd) +{ + char *buf; + int namelen; + + if (!name) + return NULL; + if (*name == '-') + ++name; + if ((namelen = strlen(name)) == 0) + return NULL; +#if defined(__APPLE__) + { + uint32_t n = PATH_MAX; + int ret; + buf = (char *)zalloc(PATH_MAX); + if ((ret = _NSGetExecutablePath(buf, &n)) < 0) { + /* try again with increased buffer size */ + buf = (char *)zrealloc(buf, n); + ret = _NSGetExecutablePath(buf, &n); + } + if (ret == 0 && strlen(buf) > 0) + return buf; + else + free(buf); + } +#elif defined(PROC_SELF_EXE) + { + ssize_t n; + buf = (char *)zalloc(PATH_MAX); + n = readlink(PROC_SELF_EXE, buf, PATH_MAX); + if (n > 0 && n < PATH_MAX) { + buf[n] = '\0'; + return buf; + } + else + free(buf); + } +#endif + /* guess the absolute pathname of 'name' */ + if (name[namelen-1] == '/') /* name should not end with '/' */ + return NULL; + else if (name[0] == '/') { + /* name is already an absolute pathname */ + return ztrdup(name); + } + else if (strchr(name, '/')) { + /* relative path */ + if (!cwd) + return NULL; + buf = (char *)zalloc(strlen(cwd) + namelen + 2); + sprintf(buf, "%s/%s", cwd, name); + return buf; + } +#ifdef HAVE_REALPATH + else { + /* search each dir in PARH */ + const char *path, *sep; + char *real, *try; + int pathlen, dirlen; + + path = getenv("PATH"); + if (!path || (pathlen = strlen(path)) == 0) + return NULL; + /* for simplicity, allocate buf even if REALPATH_ACCEPTS_NULL is on */ + buf = (char *)zalloc(PATH_MAX); + try = (char *)zalloc(pathlen + namelen + 2); + do { + sep = strchr(path, ':'); + dirlen = sep ? sep - path : strlen(path); + strncpy(try, path, dirlen); + try[dirlen] = '/'; + try[dirlen+1] = '\0'; + strcat(try, name); + real = realpath(try, buf); + if (sep) + path = sep + 1; + } while (!real && sep); + free(try); + if (!real) + free(buf); + return real; /* this may be NULL */ + } +#endif + return NULL; +} + /* Initialize lots of global variables and hash tables */ /**/ @@ -1195,6 +1298,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name) /* Colour sequences for outputting colours in prompts and zle */ set_default_colour_sequences(); + /* ZSH_EXEPATH */ + { + char *mypath, *exename, *cwd; + exename = unmetafy(ztrdup(argv0), NULL); + cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL; + mypath = getmypath(exename, cwd); + free(exename); + free(cwd); + if (mypath) { + setsparam("ZSH_EXEPATH", metafy(mypath, -1, META_REALLOC)); + } + } if (cmd) setsparam("ZSH_EXECUTION_STRING", ztrdup(cmd)); if (runscript) diff --git a/configure.ac b/configure.ac index e6ced85d9..d8a17791a 100644 --- a/configure.ac +++ b/configure.ac @@ -2011,6 +2011,25 @@ if test x$zsh_cv_sys_path_dev_fd != xno; then AC_DEFINE_UNQUOTED(PATH_DEV_FD, "$zsh_cv_sys_path_dev_fd") fi +dnl ---------------------------------------------------- +dnl CHECK FOR SYMLINK TO THE CURRENT EXECUTABLE IN /proc +dnl ---------------------------------------------------- +dnl Linux: /proc/self/exe +dnl NetBSD: /proc/curproc/exe (or /proc/self/exe, but not /proc/curproc/file) +dnl DragonFly: /proc/curproc/file +dnl Solaris: /proc/self/path/a.out +AH_TEMPLATE([PROC_SELF_EXE], +[Define to the path of the symlink to the current executable file.]) +AC_CACHE_CHECK(for symlink to the current executable in /proc, +zsh_cv_proc_self_exe, +[for zsh_cv_proc_self_exe in /proc/self/exe /proc/curproc/exe \ + /proc/curproc/file /proc/self/path/a.out no; do + test -L $zsh_cv_proc_self_exe && break +done]) +if test x$zsh_cv_proc_self_exe != xno; then + AC_DEFINE_UNQUOTED(PROC_SELF_EXE, "$zsh_cv_proc_self_exe") +fi + dnl --------------------------------- dnl CHECK FOR RFS SUPERROOT DIRECTORY dnl --------------------------------- -- cgit 1.4.1