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

Re: cdpath and empty string



On Aug 7,  4:49am, Phil Pennock wrote:
}
} If $cdpath has an empty string at the end (in my case, using
} "${cdpath[@]}" instead of ${cdpath[@]} when prepending elements), then
} "cd foo" for unqualified 'foo' which is only found relative to cwd will
} always print $cdpath[1]/foo while cd'ing into ./foo.

This gets rather confusing ... I've been wanting to rewrite the guts of
builtin.c:cd_do_chdir for years, but every time I try to start on it I
find some rathole that it would take me down to untangle its interaction
with something else.

In this case an empty-string element in $cdpath is supposed to get
treated as if it were '.' -- and indeed the same symptoms occur if there
is an explicit '.' instead of empty.  Several things are going on:

(1) If there is not a dot, then
    (1a) POSIXCD says to search cdpath first, then try the current dir.
    (1b) Otherwise, zsh tries the current dir first all is well.
(2) If there IS a dot, then the local directory is supposed to be tried
    when the position of dot in the cdpath is reached (POSXCD irrelevant).

So cd_do_chdir scans cdpath and discovers the empty string / dot, and
takes branch (2) which means it immediately calls cd_try_chdir with a
prefix of /tmp/T1 [first element of cdpath].  This assembles the path
"/tmp/T1/T1" and calls utils.c:lchdir on it.  This is where things go
wrong; that call to lchdir fails (as it should), so cd_do_chdir then
calls lchdir on the original path "T1", which succeeds.

Code snippet with comment:

    /* We try the full path first.  If that fails, try the
     * argument to cd relatively.  This is useful if the cwd
     * or a parent directory is renamed in the interim.
     */
    if (lchdir(buf, NULL, hard) && lchdir(dest, NULL, hard)) {
	free(buf);
	return NULL;
    }

A potential fix is

    if (lchdir(buf, NULL, hard) && (pfix || lchdir(dest, NULL, hard))) {

because I can't think of a case where the "useful" comment applies when
we're pasting together a path.  However, this makes a redundant call if
dest is already a full path, so perhaps

    if (lchdir(buf, NULL, hard) &&
        (pfix || *dest == '/' || lchdir(dest, NULL, hard))) {

would be better.  Are there any other potential callers of cd_try_chdir
that would be messed up by this?  Ramifications of calling cd_try_chdir
with BOTH pfix AND *dest == '/' that would need the second call to
lchdir to discover that the prefix should not have been pasted onto
the front of dest in the first place?  (Which might be better fixed by
testing for *dest == '/' before gluing on the prefix.)



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