Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: ksh compatibility: initial value of $_



> 2023/04/04 1:50, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
> 
> On Mon, Apr 3, 2023 at 5:14 AM Jun. T <takimoto-j@xxxxxxxxxxxxxxxxx> wrote:
> 
>> There may be a situation where a script
>> wants to know the pathname of itself, but for almost always
>> it would not be necessary to distinguish the above two cases.
> 
> ... we already have $ZSH_SCRIPT for that if portability is not an issue.

$ZSH_SCRIPT may not be the absolute pathname, but:

>> But probably compatibility with other shells would be more
>> important? Then guessing executable/script name only if
>> $_ does not exist in environment would be the way to go.
> 
> This is my feeling as well.


The following is my revised path.
# Since $_ is almost always set in environment (though sometimes
# not useful), this may be going too far (or to excess, don't know
# the correct words).

To test getmypath() we need to explicitly unset _ before starting zsh:

zsh% sh                   # sh on Ubuntu is symlink so dash
$ unset _
$ zsh -fc 'echo $_'
/path/to/zsh
Or
zsh% env -i zsh -f foo    # 'foo' contains 'echo $_'
/path/to/foo



diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 528c27f93..9e9aa724f 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -779,7 +779,11 @@ last pipeline.
 )
 vindex(_)
 item(tt(_) <S>)(
-The last argument of the previous command.
+Initially, this parameter is set to the full pathname of the current
+zsh executable or the script command file. If tt(_) already exists in the
+environment then its value is copied to this parameter; otherwise
+the full pathname is inferred from the argument list.
+Later, this parameter is set to the last argument of the previous command.
 Also, this parameter is set in the environment of every command
 executed to the full pathname of the command.
 )
diff --git a/Src/init.c b/Src/init.c
index 68621a0ad..f9240eb16 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -246,6 +246,9 @@ loop(int toplevel, int justonce)
 
 static int restricted;
 
+/* original argv[0] for initialization of $_. 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,117 @@ init_term(void)
     return 1;
 }
 
+/*
+ * Get (or guess) the absolute pathname of myself.
+ * If scriptname is non-NULL, guess its absolute pathname.
+ * Otherwise, get the absolute pathname of the current zsh executable by OS-
+ * specific method, and if it fails, guess the absolute pathname of exename.
+ * exename, scriptname and cwd are all unmetafied.
+ * Returns a zalloc()ed string, or NULL if failed.
+ */
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+
+/**/
+static char *
+getmypath(const char *exename, const char *scriptname, const char *cwd)
+{
+    char *buf;
+    const char *name;
+    int isscript, namelen;
+
+    if (!scriptname) {
+	isscript = 0;
+	buf = (char *)zalloc(PATH_MAX);
+	name = exename;
+	if (name && *name == '-')
+	    ++name;
+#if defined(__APPLE__)
+	{
+	    uint32_t n = PATH_MAX;
+	    int ret;
+	    if ((ret = _NSGetExecutablePath(buf, &n)) < 0) {
+		/* try again with increased buffer size */
+		free(buf);
+		buf = (char *)zalloc(n);
+		ret = _NSGetExecutablePath(buf, &n);
+	    }
+	    if (ret == 0 && strlen(buf) > 0)
+		return buf;
+	}
+#elif defined(PROC_SELF_EXE)
+	{
+	    ssize_t n;
+	    n = readlink(PROC_SELF_EXE, buf, PATH_MAX);
+	    if (n > 0 && n < PATH_MAX) {
+		buf[n] = '\0';
+		return buf;
+	    }
+	}
+#endif
+	free(buf);
+    }
+    else {
+	isscript = 1;
+	name = scriptname;
+    }
+
+    /* guess the absolute pathname of 'name' */
+    namelen = strlen(name);
+    if (namelen == 0)
+	return NULL;
+    else if (name[namelen-1] == '/')    /* name should not end with '/' */
+	return NULL;
+    else if (name[0] == '/') {
+	/* name is already an absolute pathname */
+	buf = (char *)zalloc(namelen + 1);
+	strcpy(buf, name);
+	return buf;
+    }
+    else if (isscript || strchr(name, '/')) {
+	/* relative path */
+	if (!cwd)
+	    return NULL;
+	buf = (char *)zalloc(strlen(cwd) + namelen + 2);
+	strcpy(buf, cwd);
+	strcat(buf, "/");
+	strcat(buf, 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 */
 
 /**/
@@ -1084,9 +1198,6 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
 	ztrdup(DEFAULT_IFS_SH) : ztrdup(DEFAULT_IFS);
     wordchars   = ztrdup(DEFAULT_WORDCHARS);
     postedit    = ztrdup("");
-    zunderscore  = (char *) zalloc(underscorelen = 32);
-    underscoreused = 1;
-    *zunderscore = '\0';
 
     zoptarg = ztrdup("");
     zoptind = 1;
@@ -1138,6 +1249,30 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
 
     oldpwd = ztrdup(pwd);  /* initialize `OLDPWD' = `PWD' */
 
+    /* initialize $_. If $_ is set in environment then use it.
+     * Otherwise guess from runscript or argv0. */
+    zunderscore = getenv("_");
+    if (zunderscore && *zunderscore) {
+	zunderscore = metafy(ztrdup(zunderscore), -1, META_REALLOC);
+    }
+    else {
+	char *exename, *scriptname, *cwd;
+	exename = unmetafy(ztrdup(argv0), NULL);
+	scriptname = runscript ? unmetafy(ztrdup(runscript), NULL) : NULL;
+	cwd = pwd ? unmetafy(ztrdup(pwd), NULL) : NULL;
+	zunderscore = getmypath(exename, scriptname, cwd);
+	free(exename);
+	free(scriptname);
+	free(cwd);
+	if (zunderscore)
+	    zunderscore = metafy(zunderscore, -1, META_REALLOC);
+    }
+    if (!zunderscore)
+	zunderscore = ztrdup("");
+    underscoreused = strlen(zunderscore) + 1;
+    underscorelen = (underscoreused + 31) & ~31;
+    zunderscore = (char *)zrealloc(zunderscore, underscorelen);
+
     inittyptab();     /* initialize the ztypes table */
     initlextabs();    /* initialize lexing tables    */
 
diff --git a/configure.ac b/configure.ac
index e6ced85d9..d33ea6945 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,4 +1,4 @@
-dnl
+dnexe
 dnl  configure.ac: Configure template for zsh.
 dnl  Process this file with autoconf to produce a configure script.
 dnl
@@ -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
+   readlink $zsh_cv_proc_self_exe >/dev/null && 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 ---------------------------------







Messages sorted by: Reverse Date, Date, Thread, Author