zsh-workers
 help / color / mirror / code / Atom feed
From: "Jun. T" <takimoto-j@kba.biglobe.ne.jp>
To: zsh-workers@zsh.org
Subject: Re: ksh compatibility: initial value of $_
Date: Wed, 5 Apr 2023 01:24:13 +0900	[thread overview]
Message-ID: <2A0716F2-F80B-4357-87AA-0A9881A52CEC@kba.biglobe.ne.jp> (raw)
In-Reply-To: <CAH+w=7YDy2KRAA9WUs=PaVxvb4+TdHigMHqk_a9rtozhdDDHDQ@mail.gmail.com>


> 2023/04/04 1:50, Bart Schaefer <schaefer@brasslantern.com> wrote:
> 
> On Mon, Apr 3, 2023 at 5:14 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> 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 ---------------------------------





  reply	other threads:[~2023-04-04 16:24 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-28  1:52 Bart Schaefer
2023-03-23 10:40 ` Jun T
2023-03-31  8:18   ` Jun T
2023-03-31 14:31     ` Jun. T
2023-03-31 17:45       ` Bart Schaefer
2023-04-03 11:16         ` Jun. T
2023-04-03 16:48           ` Bart Schaefer
2023-03-31 19:02       ` Bart Schaefer
2023-03-31 19:03         ` Bart Schaefer
2023-04-03 12:13     ` Jun. T
2023-04-03 16:50       ` Bart Schaefer
2023-04-04 16:24         ` Jun. T [this message]
2023-04-05  1:03           ` Oliver Kiddle
2023-04-05  8:15             ` zeurkous
2023-04-05  9:00               ` Oliver Kiddle
2023-04-05 16:24             ` Jun. T
2023-04-05  8:14           ` dana
2023-04-05 18:16             ` Jun. T
2023-04-08  4:03               ` dana
2023-04-08 16:22                 ` Oliver Kiddle
2023-04-09 13:30                   ` Jun. T
2023-04-10  0:51                     ` Jun T
2023-04-15  5:02                   ` Felipe Contreras
2023-04-15 22:24               ` Bart Schaefer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=2A0716F2-F80B-4357-87AA-0A9881A52CEC@kba.biglobe.ne.jp \
    --to=takimoto-j@kba.biglobe.ne.jp \
    --cc=zsh-workers@zsh.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).