about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog6
-rw-r--r--Doc/Zsh/params.yo4
-rw-r--r--Src/init.c117
-rw-r--r--configure.ac19
4 files changed, 145 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 32fd0780d..b2c847da5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2023-04-11  Jun-ichi Takimoto  <takimoto-j@kba.biglobe.ne.jp>
+
+	* 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  <takimoto-j@kba.biglobe.ne.jp>
 
 	* 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 <mach-o/dyld.h>
+#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 ---------------------------------