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

Re: ksh compatibility: initial value of $_



> 2023/04/01 2:45, Bart Schaefer <schaefer@xxxxxxxxxxxxxxxx> wrote:
> 
> With your patch:
> 
> % /bin/sh
> $ Src/zsh -fc 'print $_'
> /bin/sh
> $ Src/zsh -fc 'echo $_'
> print $_
> $
> 
> Similar behavior from ksh, so it really is whatever happens to be in
> the environment for '_' rather than an actual pathname.  This means
> you only get the documented behavior when a new shell or script was
> invoked via a previous shell that exports $_

Well, I think this is _the_ behavior ksh document specifies. If what
we want to achieve is just the ksh compatibility then this is enough?

But of course we can do better. The patch below ignores the $_ in
environment and tries to guess the executable/script pathname.
(this is just a draft; it would be better to set PROC_SELF_EXE in
configure if we are going to use this).

Or we can use this only if $_ is not in environment (I guess bash
behaves this way).


diff --git a/Src/init.c b/Src/init.c
index 68621a0ad..5fa8985e0 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -246,6 +246,9 @@ loop(int toplevel, int justonce)
 
 static int restricted;
 
+/* save original argv[0] for initialization of $_ */
+static char *argv0;
+
 /**/
 static void
 parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
@@ -258,6 +261,7 @@ parseargs(char *zsh_name, char **argv, char **runscript, char **cmdptr,
 	flags |= PARSEARGS_LOGIN;
 
     argzero = posixzero = *argv++;
+    argv0 = unmetafy(ztrdup(argzero), NULL);
     SHIN = 0;
 
     /*
@@ -893,6 +897,109 @@ init_term(void)
     return 1;
 }
 
+/*
+ * Get (or guess) the full pathname of myself.  If runscript is non-NULL,
+ * use it as a script file name, and guess its full pathname.
+ * Otherwize, get the full pathname of the current zsh executable by
+ * OS-specific method, and if it fails, guess the full pathname of argv0.
+ * argv0, runscript and cwd are all unmetafied.
+ * Returns a zalloc()ed string, or NULL if failed.
+ */
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#elif defined(__linux)
+#define PROC_SELF_EXE "/proc/self/exe"
+#endif
+
+/**/
+static char *
+getmypath(const char *argv0, const char *runscript, const char *cwd)
+{
+    char *buf;
+    const char *name;
+    int isscript, namelen;
+
+    if (!runscript) {
+	isscript = 0;
+	name = argv0;
+	if (name && *name == '-')
+	    ++name;
+#if defined(__APPLE__)
+	unsigned int n;
+	int ret;
+	buf = (char *)zalloc(PATH_MAX);
+	n = PATH_MAX;
+	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)
+	int 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;
+	}
+#endif
+	free(buf);
+    }
+    else {
+	isscript = 1;
+	name = runscript;
+    }
+
+    /* guess the full 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 a fullpath */
+	buf = (char *)zalloc(namelen + 1);
+	strcpy(buf, name);
+	return buf;
+    }
+    else if (isscript || strchr(name, '/')) {
+	/* realative path */
+	if (!cwd)
+	    return NULL;
+	buf = (char *)zalloc(strlen(cwd) + namelen + 2);
+	strcpy(buf, cwd);
+	strcat(buf, "/");
+	strcat(buf, name);
+	return buf;
+    }
+    else {
+	/* search each dir in PARH */
+	const char *path, *sep;
+	char *real;
+	int pathlen, dirlen;
+	path = getenv("PATH");
+	if (!path || (pathlen = strlen(path)) == 0)
+	    return NULL;
+	buf = (char *)zalloc(pathlen + namelen + 2);
+	do {
+	    sep = strchr(path, ':');
+	    dirlen = sep ? sep - path : strlen(path);
+	    strncpy(buf, path, dirlen);
+	    buf[dirlen] = '/';
+	    buf[dirlen+1] = '\0';
+	    strcat(buf, name);
+	    real = realpath(buf, NULL);
+	    if (sep)
+		path = sep + 1;
+	} while (!real && sep);
+	free(buf);
+	return real;	/* this may be NULL */
+    }
+}
+
 /* Initialize lots of global variables and hash tables */
 
 /**/
@@ -903,7 +1010,7 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
     struct passwd *pswd;
 #endif
     struct timezone dummy_tz;
-    char *ptr;
+    char *ptr, *mypath;
     int i, j;
 #if defined(SITEFPATH_DIR) || defined(FPATH_DIR) || defined (ADDITIONAL_FPATH) || defined(FIXED_FPATH_DIR)
 # define FPATH_NEEDS_INIT 1
@@ -1084,9 +1191,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 +1242,34 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
 
     oldpwd = ztrdup(pwd);  /* initialize `OLDPWD' = `PWD' */
 
+    /* initialize $_ */
+    {
+	char *exename = argv0, *scriptname = runscript, *cwd = pwd;
+	if (exename)
+	    exename = unmetafy(ztrdup(exename), NULL);
+	if (scriptname)
+	    scriptname = unmetafy(ztrdup(scriptname), NULL);
+	if (cwd)
+	    cwd = unmetafy(ztrdup(cwd), NULL);
+	zunderscore = getmypath(exename, scriptname, cwd);
+	free(exename);
+	free(scriptname);
+	free(cwd);
+    }
+    if (zunderscore) {
+	zunderscore = metafy(zunderscore, -1, META_REALLOC);
+	underscoreused = strlen(zunderscore) + 1;
+	underscorelen = (underscoreused + 31) & ~31;
+	zunderscore = (char *)zrealloc(zunderscore, underscorelen);
+    }
+    else {
+	zunderscore  = (char *) zalloc(underscorelen = 32);
+	underscoreused = 1;
+	*zunderscore = '\0';
+    }
+
+
+
     inittyptab();     /* initialize the ztypes table */
     initlextabs();    /* initialize lexing tables    */
 






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