From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 20354 invoked by alias); 8 Aug 2013 01:23:29 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 31637 Received: (qmail 7450 invoked from network); 8 Aug 2013 01:23:23 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 Received-SPF: none (ns1.primenet.com.au: domain at closedmail.com does not designate permitted sender hosts) From: Bart Schaefer Message-id: <130807182304.ZM18723@torch.brasslantern.com> Date: Wed, 07 Aug 2013 18:23:04 -0700 In-reply-to: <20130807084938.GA94638@redoubt.spodhuis.org> Comments: In reply to Phil Pennock "cdpath and empty string" (Aug 7, 4:49am) References: <20130807084938.GA94638@redoubt.spodhuis.org> X-Mailer: OpenZMail Classic (0.9.2 24April2005) To: Phil Pennock , zsh-workers@zsh.org Subject: Re: cdpath and empty string MIME-version: 1.0 Content-type: text/plain; charset=us-ascii 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.)