zsh-workers
 help / color / mirror / code / Atom feed
* [BUG] cdpath=(.) breaks cd from / on Cygwin
@ 2015-02-15 20:18 Andrew Janke
  2015-02-15 22:57 ` Bart Schaefer
  0 siblings, 1 reply; 3+ messages in thread
From: Andrew Janke @ 2015-02-15 20:18 UTC (permalink / raw)
  To: zsh-workers

Hi, zsh workers,

This is my first time sending to this list. Sorry if I've got any of the 
conventions wrong.

I think I've found a bug in zsh's handling of path normalization on 
Cygwin when there's an explicit '.' in $cdpath. When $cdpath contains ., 
and your pwd is /, doing a cd using a relative path to an immediate 
subdir like `cd bin` or `cd tmp` errors out, and slowly.

Reproduction
====================

In zsh on Cygwin, do this:

cd /
cdpath=(.)
cd bin
cd lib
cdpath=()
cd bin

Symptoms
====================

On my system (Windows 7, zsh 5.0.6 (x86_64-unknown-cygwin), running 
inside MinTTY, inside a vmware VM on Mac OS X 10.9 on a MacBook Pro), if 
I have '.' in $cdpath and try to do a cd from / to an immediate subdir, 
the command sits there for up to several seconds and then errors out 
with a "no such dir" error.

Here's an example run. This is with an empty ~/.zshrc file so I'd have a 
default zsh configuration. gwydion is my win7 machine's hostname.

gwydion% cd /
gwydion% cdpath=(.)
gwydion% cd bin
cd: no such file or directory: bin
gwydion% cd lib
cd: no such file or directory: lib
gwydion% pwd
/
gwydion% cdpath=()
gwydion% cd bin
gwydion% pwd
/bin
gwydion%

I would expect the behavior with `cdpath=(.)` to be identical to the 
behavior with `cdpath=()`, and to have all these cd commands succeed.


Analysis
===================

On OS X 10.9 and Debian Linux 7, doing a cd from / with cdpath=(.) works 
fine for me, so I think this is Cygwin-specific.

In Src/builtin.c, function cd_try_chdir(), lines 1080-1093, there's some 
special case handling for Cygwin and the prefix '/'. But when the prefix 
is '.', it goes down a different code path. It constructs the path 
starting from pwd (which is '/' in this case), and then goes through the 
path normalization which elides '.' segments. But it doesn't have a 
special case check for Cygwin to check for leading '/' again. And I 
think the elision of the first '.' segment will result in a computed 
path starting with '//' but not further escaped.

I think what's happening is in this case, the path is ending up with 
'//' at the beginning and it's treating it as a UNC path and ends up 
looking for a machine named 'bin' or 'lib' or whatever.

I'm not good enough with C and path normalization to try my hand at 
making a patch for this.

References
===================

I found this while working with oh-my-zsh, where another Cygwin user ran 
in to this behavior. https://github.com/robbyrussell/oh-my-zsh/issues/3550

(When I tested this, I had oh-my-zsh disabled, and an empty .zshrc, so I 
don't think oh-my-zsh interaction is relevant.)

Cheers,
Andrew Janke


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

* Re: [BUG] cdpath=(.) breaks cd from / on Cygwin
  2015-02-15 20:18 [BUG] cdpath=(.) breaks cd from / on Cygwin Andrew Janke
@ 2015-02-15 22:57 ` Bart Schaefer
  2015-02-16 17:01   ` Andrew Janke
  0 siblings, 1 reply; 3+ messages in thread
From: Bart Schaefer @ 2015-02-15 22:57 UTC (permalink / raw)
  To: Andrew Janke, zsh-workers

On Feb 15,  3:18pm, Andrew Janke wrote:
}
} In Src/builtin.c, function cd_try_chdir(), lines 1080-1093, there's some 
} special case handling for Cygwin and the prefix '/'. But when the prefix 
} is '.', it goes down a different code path. It constructs the path 
} starting from pwd (which is '/' in this case), and then goes through the 
} path normalization which elides '.' segments. But it doesn't have a 
} special case check for Cygwin to check for leading '/' again. And I 
} think the elision of the first '.' segment will result in a computed 
} path starting with '//' but not further escaped.

That's a pretty close analysis.  The real problem is that when pwd is
"/", cd_try_chdir() unnecessarily inserts yet another "/" into the
path before appending a cdpath segment (the "pfix").  So the path has
already become "//./" before normalization even begins.  This is a
waste of effort when pwd is "/" on any platform.

I'm not sure why we bother compiling different branches for cygwin and
otherwise in cd_try_chdir().  Doesn't appear the cygwin variation would
be wrong anywhere else.  But I won't mess with that here.

Try this patch:


diff --git a/Src/builtin.c b/Src/builtin.c
index c4e4b94..614b17d 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1093,9 +1093,11 @@ cd_try_chdir(char *pfix, char *dest, int hard)
 	} else {
 	    int pfl = strlen(pfix);
 	    dlen = strlen(pwd);
-
+	    if (dlen == 1 && *pwd == '/')
+		dlen = 0;
 	    buf = zalloc(dlen + pfl + strlen(dest) + 3);
-	    strcpy(buf, pwd);
+	    if (dlen)
+		strcpy(buf, pwd);
 	    buf[dlen] = '/';
 	    strcpy(buf + dlen + 1, pfix);
 	    buf[dlen + 1 + pfl] = '/';


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

* Re: [BUG] cdpath=(.) breaks cd from / on Cygwin
  2015-02-15 22:57 ` Bart Schaefer
@ 2015-02-16 17:01   ` Andrew Janke
  0 siblings, 0 replies; 3+ messages in thread
From: Andrew Janke @ 2015-02-16 17:01 UTC (permalink / raw)
  To: Bart Schaefer, zsh-workers

Looks good to me. When I build with this patch, cd from / on Cygwin 
works as expected, even with an explicit '.' in $cdpath.

Thanks!

Andrew


On 2/15/15 5:57 PM, Bart Schaefer wrote:
> On Feb 15,  3:18pm, Andrew Janke wrote:
> }
> } In Src/builtin.c, function cd_try_chdir(), lines 1080-1093, there's some
> } special case handling for Cygwin and the prefix '/'. But when the prefix
> } is '.', it goes down a different code path. It constructs the path
> } starting from pwd (which is '/' in this case), and then goes through the
> } path normalization which elides '.' segments. But it doesn't have a
> } special case check for Cygwin to check for leading '/' again. And I
> } think the elision of the first '.' segment will result in a computed
> } path starting with '//' but not further escaped.
>
> That's a pretty close analysis.  The real problem is that when pwd is
> "/", cd_try_chdir() unnecessarily inserts yet another "/" into the
> path before appending a cdpath segment (the "pfix").  So the path has
> already become "//./" before normalization even begins.  This is a
> waste of effort when pwd is "/" on any platform.
>
> I'm not sure why we bother compiling different branches for cygwin and
> otherwise in cd_try_chdir().  Doesn't appear the cygwin variation would
> be wrong anywhere else.  But I won't mess with that here.
>
> Try this patch:
>
>
> diff --git a/Src/builtin.c b/Src/builtin.c
> index c4e4b94..614b17d 100644
> --- a/Src/builtin.c
> +++ b/Src/builtin.c
> @@ -1093,9 +1093,11 @@ cd_try_chdir(char *pfix, char *dest, int hard)
>   	} else {
>   	    int pfl = strlen(pfix);
>   	    dlen = strlen(pwd);
> -
> +	    if (dlen == 1 && *pwd == '/')
> +		dlen = 0;
>   	    buf = zalloc(dlen + pfl + strlen(dest) + 3);
> -	    strcpy(buf, pwd);
> +	    if (dlen)
> +		strcpy(buf, pwd);
>   	    buf[dlen] = '/';
>   	    strcpy(buf + dlen + 1, pfix);
>   	    buf[dlen + 1 + pfl] = '/';


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

end of thread, other threads:[~2015-02-16 17:01 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-15 20:18 [BUG] cdpath=(.) breaks cd from / on Cygwin Andrew Janke
2015-02-15 22:57 ` Bart Schaefer
2015-02-16 17:01   ` Andrew Janke

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