zsh-workers
 help / color / mirror / code / Atom feed
* ksh compatibility: initial value of $_
@ 2023-01-28  1:52 Bart Schaefer
  2023-03-23 10:40 ` Jun T
  0 siblings, 1 reply; 24+ messages in thread
From: Bart Schaefer @ 2023-01-28  1:52 UTC (permalink / raw)
  To: Zsh hackers list

Docs for ksh93 say that $_ should be initialized to the absolute path
of what we put in $ZSH_ARGZERO.

Src/init.c explicitly initializes $_ to the empty string.

Any reason not to change this?  If so, where's the right place to do
it?  I tried doing it in createparamtab() but that led to memory
errors.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-01-28  1:52 ksh compatibility: initial value of $_ Bart Schaefer
@ 2023-03-23 10:40 ` Jun T
  2023-03-31  8:18   ` Jun T
  0 siblings, 1 reply; 24+ messages in thread
From: Jun T @ 2023-03-23 10:40 UTC (permalink / raw)
  To: zsh-workers


> 2023/01/28 10:52, Bart Schaefer <schaefer@brasslantern.com> wrote:
> 
> Docs for ksh93 say that $_ should be initialized to the absolute path
> of what we put in $ZSH_ARGZERO.
> 
> Src/init.c explicitly initializes $_ to the empty string.
> 
> Any reason not to change this?

No reason, I think, but I don't know any portable way to obtain
the full path name of the current zsh executable.

>  If so, where's the right place to do
> it?  I tried doing it in createparamtab() but that led to memory
> errors.

Once the full path name is obtained, then I guess we can initialize
$_ in setupvals() as in the following pseudo-patch:

diff --git a/Src/init.c b/Src/init.c
index 68621a0ad..03cbe4d0e 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1084,9 +1084,12 @@ 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';
+    /* assume full path name of this zsh is in 'mypath' */
+    underscoreused = strlen(mypath) + 1;
+    underscorelen = (underscoreused + 31) & ~31;
+    zunderscore = (char *) zalloc(underscorelen);
+    strcpy(zunderscore, mypath);
+    /* free 'mypath' if necessary */
 
     zoptarg = ztrdup("");
     zoptind = 1;





^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-23 10:40 ` Jun T
@ 2023-03-31  8:18   ` Jun T
  2023-03-31 14:31     ` Jun. T
  2023-04-03 12:13     ` Jun. T
  0 siblings, 2 replies; 24+ messages in thread
From: Jun T @ 2023-03-31  8:18 UTC (permalink / raw)
  To: zsh-workers


> 2023/03/23 19:40, I wrote:
> 
>> 2023/01/28 10:52, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> 
>> Src/init.c explicitly initializes $_ to the empty string.
>> 
>> Any reason not to change this?
> 
> No reason, I think, but I don't know any portable way to obtain
> the full path name of the current zsh executable.

$_ need to be initialized to the full pathname of either
current zsh executable, or the script file.
I have some difficulty with both.

[1] full pathname of the zsh executable:

On Linux we can use /proc/self/exe.

By some Googling I've found that Solaris, NetBSD, and DragonFly have
similar symlinks in /proc. On macOS we can use _NSGetExecutablePath().

On FreeBSD, it seems porcfs is obsolete.
On OpenBSD there seems to be no reliable way?

But, for {Free,Open}BSD just guessing from argv[0] and $PATH or $PWD
would be enough, although argv[0] can be set to arbitray value in
execve(2).


[2] full pathname of the script file

We can guess it from argv[1] (after removing options) and $PWD, and the
variable 'runscript' is set to argv[1] at line 290 in init.c.

But, suppose we have a ksh script 'foo' in the current directory:

% cat foo
#!/usr/bin/ksh
echo $_
% ./foo
/home/takimoto/tmp/./foo
% ksh ./foo
/usr/bin/ksh

How can we distinguish these two cases? In both cases runscript is
'./foo', I think. Probably there is a simple way that I just don't know.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31  8:18   ` Jun T
@ 2023-03-31 14:31     ` Jun. T
  2023-03-31 17:45       ` Bart Schaefer
  2023-03-31 19:02       ` Bart Schaefer
  2023-04-03 12:13     ` Jun. T
  1 sibling, 2 replies; 24+ messages in thread
From: Jun. T @ 2023-03-31 14:31 UTC (permalink / raw)
  To: zsh-workers

Sorry, maybe I completely misunderstood the ksh behavior.

    _    Initially,  the value of _ is an absolute pathname of the
         shell or script being executed as passed in the environment.

So zsh can just do the same thing?
With the following patch, zsh behaves similarly with ksh?


diff --git a/Src/init.c b/Src/init.c
index 68621a0ad..fcfa7c3c9 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1084,9 +1084,18 @@ 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';
+    zunderscore = getenv("_");
+    if (zunderscore) {
+	zunderscore = metafy(ztrdup(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';
+    }
 
     zoptarg = ztrdup("");
     zoptind = 1;



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31 14:31     ` Jun. T
@ 2023-03-31 17:45       ` Bart Schaefer
  2023-04-03 11:16         ` Jun. T
  2023-03-31 19:02       ` Bart Schaefer
  1 sibling, 1 reply; 24+ messages in thread
From: Bart Schaefer @ 2023-03-31 17:45 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Fri, Mar 31, 2023 at 7:32 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
>     _    Initially,  the value of _ is an absolute pathname of the
>          shell or script being executed as passed in the environment.
>
> So zsh can just do the same thing?
> With the following patch, zsh behaves similarly with ksh?

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 $_ appropriately (which
evidently zsh already does, though I can't immediately find the code).

% typeset -p _
typeset _=_
% printenv _
/bin/printenv
%

There's nothing in POSIX (that I can find) specifying any behavior of $_ at all.

One other notable difference (which I think has been mentioned before)
is demonstrated thus:

ksh93:
$ for _ in a b c; do echo $_; done
a
b
c
$ for _ in a b c; do echo X; echo $_; done
X
a
X
b
X
c
$

zsh (even with ARGV0=ksh):
% for _ in a b c; do echo $_; done

echo
echo
% for _ in a b c; do echo X; echo $_; done
X
X
X
X
X
X
%

So ... ksh assigns $_ only after each full complex command, and does
not otherwise treat it as special, whereas zsh assigns it after every
pipeline (and also after the "do" keyword, it seems) and cannot use it
as a normal parameter in between.  (This can be partly worked around
in a function body by using "local -h _" but then all special behavior
is lost.)

Incidentally, as far as I can tell from a quick bit of research, if
you want to compare zsh and ksh behavior it is not advisable to
install "ksh2020" (which is unfortunately the default "ksh" for Ubuntu
at least as of 20.04).  Explicitly install "ksh93" instead if you have
that option.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31 14:31     ` Jun. T
  2023-03-31 17:45       ` Bart Schaefer
@ 2023-03-31 19:02       ` Bart Schaefer
  2023-03-31 19:03         ` Bart Schaefer
  1 sibling, 1 reply; 24+ messages in thread
From: Bart Schaefer @ 2023-03-31 19:02 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Fri, Mar 31, 2023 at 7:32 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> +++ b/Src/init.c
> @@ -1084,9 +1084,18 @@ setupvals(char *cmd, char *runscript, char *zsh_name)
> +    zunderscore = getenv("_");
> +    if (zunderscore) {
> +       zunderscore = metafy(ztrdup(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';
> +    }

Is there a simple way to call setunderscore() here instead of
duplicating most of it?  The complication mostly seems to be that
metafy() doesn't handle a NULL string, so we can't just
metafy(getenv()).

> +       zunderscore = metafy(ztrdup(zunderscore), -1, META_REALLOC);

This at least could just be

       zunderscore = metafy(zunderscore, -1, META_ALLOC);

??


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31 19:02       ` Bart Schaefer
@ 2023-03-31 19:03         ` Bart Schaefer
  0 siblings, 0 replies; 24+ messages in thread
From: Bart Schaefer @ 2023-03-31 19:03 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Fri, Mar 31, 2023 at 12:02 PM Bart Schaefer
<schaefer@brasslantern.com> wrote:
>
> > +       zunderscore = metafy(ztrdup(zunderscore), -1, META_REALLOC);
>
> This at least could just be
>
>        zunderscore = metafy(zunderscore, -1, META_ALLOC);

Sorry, I guess that would need to be META_DUP.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31 17:45       ` Bart Schaefer
@ 2023-04-03 11:16         ` Jun. T
  2023-04-03 16:48           ` Bart Schaefer
  0 siblings, 1 reply; 24+ messages in thread
From: Jun. T @ 2023-04-03 11:16 UTC (permalink / raw)
  To: zsh-workers


> 2023/04/01 2:45, Bart Schaefer <schaefer@brasslantern.com> 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    */
 




^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-03-31  8:18   ` Jun T
  2023-03-31 14:31     ` Jun. T
@ 2023-04-03 12:13     ` Jun. T
  2023-04-03 16:50       ` Bart Schaefer
  1 sibling, 1 reply; 24+ messages in thread
From: Jun. T @ 2023-04-03 12:13 UTC (permalink / raw)
  To: zsh-workers


> 2023/03/31 17:18, I wrote:
> 
> But, suppose we have a ksh script 'foo' in the current directory:
> 
> % cat foo
> #!/usr/bin/ksh
> echo $_
> % ./foo
> /home/takimoto/tmp/./foo
> % ksh ./foo
> /usr/bin/ksh

Both ksh and bash behave this way, but I personally feel that
getting the script name /path/to/foo for both cases is more
natural and useful. 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.

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.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-03 11:16         ` Jun. T
@ 2023-04-03 16:48           ` Bart Schaefer
  0 siblings, 0 replies; 24+ messages in thread
From: Bart Schaefer @ 2023-04-03 16:48 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Mon, Apr 3, 2023 at 4:17 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> > 2023/04/01 2:45, Bart Schaefer <schaefer@brasslantern.com> wrote:
> >
> > Similar behavior from ksh, so it really is whatever happens to be in
> > the environment for '_' rather than an actual pathname.
>
> 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.
>
> Or we can use this only if $_ is not in environment (I guess bash
> behaves this way).

I would choose that last, but I suppose it could also be contingent on
emulation mode.

RE the draft patch:

> +       /* search each dir in PARH */
> +       const char *path, *sep;
> +       char *real;
> +       int pathlen, dirlen;
> +       path = getenv("PATH");

I take it this is happening early enough that zsh's internal setting
of the path is not yet done.

After consideration I don't think any of my other specific comments
would be helpful, so I'll just make the general observation that when
I brought up the topic I didn't think it would require this much
effort!


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-03 12:13     ` Jun. T
@ 2023-04-03 16:50       ` Bart Schaefer
  2023-04-04 16:24         ` Jun. T
  0 siblings, 1 reply; 24+ messages in thread
From: Bart Schaefer @ 2023-04-03 16:50 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Mon, Apr 3, 2023 at 5:14 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> Both ksh and bash behave this way, but I personally feel that
> getting the script name /path/to/foo for both cases is more
> natural and useful.

It wouldn't be portable across shells, though, and ...

> 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.

> 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.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-03 16:50       ` Bart Schaefer
@ 2023-04-04 16:24         ` Jun. T
  2023-04-05  1:03           ` Oliver Kiddle
  2023-04-05  8:14           ` dana
  0 siblings, 2 replies; 24+ messages in thread
From: Jun. T @ 2023-04-04 16:24 UTC (permalink / raw)
  To: zsh-workers


> 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 ---------------------------------





^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-04 16:24         ` Jun. T
@ 2023-04-05  1:03           ` Oliver Kiddle
  2023-04-05  8:15             ` zeurkous
  2023-04-05 16:24             ` Jun. T
  2023-04-05  8:14           ` dana
  1 sibling, 2 replies; 24+ messages in thread
From: Oliver Kiddle @ 2023-04-05  1:03 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

"Jun. T" wrote:
> diff --git a/configure.ac b/configure.ac
> index e6ced85d9..d33ea6945 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -1,4 +1,4 @@
> -dnl
> +dnexe

Is that part of the patch intentional?

> +[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

Given that you're ignoring the output, test -L is likely more portable
in this case. Specifically it would work on Solaris 10 which doesn't
have readlink (but does have /proc/self/path/a.out). test -L is covered
by the POSIX standard while readlink isn't.

Oliver


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-04 16:24         ` Jun. T
  2023-04-05  1:03           ` Oliver Kiddle
@ 2023-04-05  8:14           ` dana
  2023-04-05 18:16             ` Jun. T
  1 sibling, 1 reply; 24+ messages in thread
From: dana @ 2023-04-05  8:14 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

On Tue 4 Apr 2023, at 11:24, Jun. T wrote:
> 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).

I had written a patch very very similar to this in 2018 because of
workers/42193 (as well as users/22875, users/18199, &c.). I wasn't thinking
about $_ at the time; instead, i had created a ZSH_EXECUTABLE_PATH parameter
that resolved the path the first time it was referenced, because i was worried
that it was too niche to waste I/O on it for every invocation of the shell. In
the end i didn't submit the patch because it still felt too elaborate...

Anyway, would it make sense to revive the idea of having a dedicated variable
for it, so you can get the absolute path to the shell regardless of the
environment or what commands might have run before you could capture $_? That
would somewhat address your fear that it's too excessive for how rarely it'd
be used, at least

PS: bash has a similar dedicated parameter, just called BASH... but it doesn't
try to be this accurate about it. If it's a log-in shell, it will use $SHELL,
otherwise it basically does the 'guess the absolute pathname' part of your
getmypath()

dana


^ permalink raw reply	[flat|nested] 24+ messages in thread

* RE: Re: ksh compatibility: initial value of $_
  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
  1 sibling, 1 reply; 24+ messages in thread
From: zeurkous @ 2023-04-05  8:15 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: zsh-workers, Jun. T

Haai,

On Wed, 05 Apr 2023 03:03:35 +0200, Oliver Kiddle <opk@zsh.org> wrote:
> "Jun. T" wrote:
> Given that you're ignoring the output, test -L is likely more portable
> in this case. Specifically it would work on Solaris 10 which doesn't
> have readlink (but does have /proc/self/path/a.out). test -L is covered
> by the POSIX standard while readlink isn't.

FWIW, OpenBSD test(1) says--

     -\b-L\bL _\bf_\bi_\bl_\be
             True if _\bf_\bi_\bl_\be exists and is a symbolic link.  This operator is for
             compatibility purposes.  Do not rely on its existence; use -\b-h\bh
             instead.

Note that last part. Is "-L" actually more standard? (Even on other
*cough* Unices, me's always used "-h").

There's also stat(1), but that appears to be even less standard than
readlink(1).

> Oliver

        --zeurkous.

-- 
Friggin' Machines!


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-05  8:15             ` zeurkous
@ 2023-04-05  9:00               ` Oliver Kiddle
  0 siblings, 0 replies; 24+ messages in thread
From: Oliver Kiddle @ 2023-04-05  9:00 UTC (permalink / raw)
  To: zeurkous; +Cc: zsh-workers, Jun. T

zeurkous@blaatscaahp.org wrote:
> FWIW, OpenBSD test(1) says--
>
>      -\b-L\bL _\bf_\bi_\bl_\be
>              True if _\bf_\bi_\bl_\be exists and is a symbolic link.  This operator is for
>              compatibility purposes.  Do not rely on its existence; use -\b-h\bh
>              instead.
>
> Note that last part. Is "-L" actually more standard? (Even on other
> *cough* Unices, me's always used "-h").

Any system I have access to appears to support both. The other BSD's
also have the same recommendation and on that basis, you're probably
right that we should use -h.

Oliver



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-05  1:03           ` Oliver Kiddle
  2023-04-05  8:15             ` zeurkous
@ 2023-04-05 16:24             ` Jun. T
  1 sibling, 0 replies; 24+ messages in thread
From: Jun. T @ 2023-04-05 16:24 UTC (permalink / raw)
  To: zsh-workers


> 2023/04/05 10:03, Oliver Kiddle <opk@zsh.org> wrote:
> 
> "Jun. T" wrote:
>> diff --git a/configure.ac b/configure.ac
>> index e6ced85d9..d33ea6945 100644
>> --- a/configure.ac
>> +++ b/configure.ac
>> @@ -1,4 +1,4 @@
>> -dnl
>> +dnexe
> 
> Is that part of the patch intentional?

Oops, sorry.

>> +[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
> 
> Given that you're ignoring the output, test -L is likely more portable

Thanks. and:

> 2023/04/05 18:00、Oliver Kiddle <opk@zsh.org>のメール:
> 
> zeurkous@blaatscaahp.org wrote:
>> FWIW, OpenBSD test(1) says--
(snip)
>> Note that last part. Is "-L" actually more standard? (Even on other
>> *cough* Unices, me's always used "-h").
> 
> Any system I have access to appears to support both. The other BSD's
> also have the same recommendation and on that basis, you're probably
> right that we should use -h.

On OpenBSD and NetBSD, the manpage says -L is for compatibility and
-h is recommended.
But on FreeBSD, DragonFly and macOS -h is for compatibility and
-L is recommended.

The source code of test command for OpenBSD:
https://github.com/openbsd/src/blob/master/bin/test/test.c

line 113:	{"-h", FILSYM, UNOP},	/* for backwards compat */

So I guess the manpage for OpenBSD (and NetBSD) is wrong?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-05  8:14           ` dana
@ 2023-04-05 18:16             ` Jun. T
  2023-04-08  4:03               ` dana
  2023-04-15 22:24               ` Bart Schaefer
  0 siblings, 2 replies; 24+ messages in thread
From: Jun. T @ 2023-04-05 18:16 UTC (permalink / raw)
  To: zsh-workers


> 2023/04/05 17:14, dana <dana@dana.is> wrote:

> would it make sense to revive the idea of having a dedicated variable
> for it,

Yes, I've been thinking exactly the same thing.
If we add, say ZSH_EXEPATH, then I think just copying _ from envionment is
enough.
# For the script name, if ZSH_SCRIPT is not an absolute path, then we can
# use ${ZSH_SCRIPT:a} before cd-ing from the initial directory.

The following is the patch for initializing $_ from environment.
If this is OK, and if ZSH_EXEPATH (or any other name?) would be usefull,
then I will try to prepare the patch for it separately.


diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 528c27f93..07529aa35 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -779,7 +779,10 @@ last pipeline.
 )
 vindex(_)
 item(tt(_) <S>)(
-The last argument of the previous command.
+Initially, if tt(_) exists in the environment, then this parameter is set to
+its value. This value may be the absolute pathname of the current zsh
+executable or the script command file.
+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..7e98af44c 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1084,9 +1084,12 @@ 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';
+    /* If _ is set in environment then initialize our $_ by copying it */
+    zunderscore = getenv("_");
+    zunderscore = zunderscore ? metafy(zunderscore, -1, META_DUP) : ztrdup("");
+    underscoreused = strlen(zunderscore) + 1;
+    underscorelen = (underscoreused + 31) & ~31;
+    zunderscore = (char *)zrealloc(zunderscore, underscorelen);
 
     zoptarg = ztrdup("");
     zoptind = 1;





^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-05 18:16             ` Jun. T
@ 2023-04-08  4:03               ` dana
  2023-04-08 16:22                 ` Oliver Kiddle
  2023-04-15 22:24               ` Bart Schaefer
  1 sibling, 1 reply; 24+ messages in thread
From: dana @ 2023-04-08  4:03 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

On Wed 5 Apr 2023, at 13:16, Jun. T wrote:
> If we add, say ZSH_EXEPATH, then I think just copying _ from envionment is
> enough.

I don't know if/how people are using $_ within the shell, i don't really use
it myself, but if you've got the code in there to make it match ksh/bash
anyway, what's the reason not to do so? Aside from being worried about wasting
I/O on it like i was... but i'm not sure that was a valid concern

On Wed 5 Apr 2023, at 13:16, Jun. T wrote:
> If this is OK, and if ZSH_EXEPATH (or any other name?) would be usefull,

I would personally like to see something like that go in, with or without a
similar change to $_, if it doesn't bother anyone. I've needed the shell's
path a few times in the past, and although it's simple enough to use procfs
where available, i think you have to resort to parsing lsof or whatever to do
it on macOS

As for the name, here's what the equivalent feature is called in other
interpreters:

- bash: $BASH
- Node: process.execPath
- Perl: $^X
- PHP: PHP_BINARY
- Python: sys.executable
- Raku (Perl 6): $*EXECUTABLE
- TCL: [info nameofexecutable]

Maybe ZSH_EXECUTABLE? That kind of matches ZSH_SCRIPT too

dana


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-08  4:03               ` dana
@ 2023-04-08 16:22                 ` Oliver Kiddle
  2023-04-09 13:30                   ` Jun. T
  2023-04-15  5:02                   ` Felipe Contreras
  0 siblings, 2 replies; 24+ messages in thread
From: Oliver Kiddle @ 2023-04-08 16:22 UTC (permalink / raw)
  To: zsh-workers

dana wrote:
> Maybe ZSH_EXECUTABLE? That kind of matches ZSH_SCRIPT too

That sounds good. Unless we want to take advantage of Bart's recent
changes and put it in a namespace. A question Bart asked about what
namespaces we may want to reserve went unanswered.

One disadvantage of prefixing variables with "BASH" or "ZSH" is that it
makes it harder for different shells to later be compatible with each
other.

Oliver


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  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
  1 sibling, 1 reply; 24+ messages in thread
From: Jun. T @ 2023-04-09 13:30 UTC (permalink / raw)
  To: zsh-workers


> 2023/04/09 1:22、Oliver Kiddle <opk@zsh.org>のメール:
> 
> dana wrote:
>> Maybe ZSH_EXECUTABLE? That kind of matches ZSH_SCRIPT too
> 
> That sounds good. Unless we want to take advantage of Bart's recent
> changes and put it in a namespace.

In the path below I used ZSH_EXEPATH to make it (somewhat) easier to
guess that it is a full pathname, but ZSH_EXECUTABLE (or any other
name) is OK for me.
If it will be put in a namespace, is it available only in the namespace,
or also in the toplevel namespace?


diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 2db4210eb..08b3e79f6 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 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..f8acf2ff1 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,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 ---------------------------------





^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-09 13:30                   ` Jun. T
@ 2023-04-10  0:51                     ` Jun T
  0 siblings, 0 replies; 24+ messages in thread
From: Jun T @ 2023-04-10  0:51 UTC (permalink / raw)
  To: zsh-workers

Sorry, there was a serious typo:

> 2023/04/09 22:30、Jun. T <takimoto-j@kba.biglobe.ne.jp>のメール:

> diff --git a/Src/init.c b/Src/init.c
(snip)
> @@ -893,6 +896,106 @@ init_term(void)
(snip)
> +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))

the last line should be

	if ((namelen = strlen(name)) == 0)

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-08 16:22                 ` Oliver Kiddle
  2023-04-09 13:30                   ` Jun. T
@ 2023-04-15  5:02                   ` Felipe Contreras
  1 sibling, 0 replies; 24+ messages in thread
From: Felipe Contreras @ 2023-04-15  5:02 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: zsh-workers

On Sat, Apr 8, 2023 at 11:22 AM Oliver Kiddle <opk@zsh.org> wrote:

> One disadvantage of prefixing variables with "BASH" or "ZSH" is that it
> makes it harder for different shells to later be compatible with each
> other.

Has that actually happened with any variable in the last 20 years? (my
living memory)

I tried to push the git project to do something like
`ARGZERO=${ZSH_ARGZERO-$0}` for their sh testing framework to work on
zsh and it did not fly at all. I wonder if any such consensus has
actually taken place.

I think it's fine to use a ZSH_ prefix until there's consensus.

-- 
Felipe Contreras


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: ksh compatibility: initial value of $_
  2023-04-05 18:16             ` Jun. T
  2023-04-08  4:03               ` dana
@ 2023-04-15 22:24               ` Bart Schaefer
  1 sibling, 0 replies; 24+ messages in thread
From: Bart Schaefer @ 2023-04-15 22:24 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

On Wed, Apr 5, 2023 at 11:17 AM Jun. T <takimoto-j@kba.biglobe.ne.jp> wrote:
>
> Yes, I've been thinking exactly the same thing.
> If we add, say ZSH_EXEPATH, then I think just copying _ from envionment is
> enough.

In Src/params.c, the "_" variable is marked PM_DONTIMPORT.  Is it
worth commenting that this is because it is conditionally imported
elsewhere?


^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2023-04-15 22:25 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-28  1:52 ksh compatibility: initial value of $_ 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
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

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).