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

Re: cd does not update $PWD in a timely fashion.



On Fri, Jun 26, 2020 at 12:32:51PM -0500, Matthew Martin wrote:
> On Fri, Jun 26, 2020 at 07:24:21PM +0200, Han Boetes wrote:
> > I had this program changing the name of the current working directory and
> > noticed cd .; echo $PWD didn't pick up the changes.
> 
> Was first discussed in IRC, so I had a head start on the patch.
> 
> bin_cd may update pwd after calling cd_new_pwd which calls set_pwd_env.
> Since bin_cd is the only caller of cd_new_pwd, I think it's safe to move
> that code into cd_new_pwd prior to the oldpwd, pwd, new_pwd shift. This
> should also call the chpwd hook with the right value.
> 
> Seems to fix the issue locally. I know Daniel will rightfully ask for
> a new test, so I'll work on that tonight.

And with the test


diff --git a/Src/builtin.c b/Src/builtin.c
index 770930579..ff84ce936 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -841,7 +841,6 @@ int
 bin_cd(char *nam, char **argv, Options ops, int func)
 {
     LinkNode dir;
-    struct stat st1, st2;
 
     if (isset(RESTRICTED)) {
 	zwarnnam(nam, "restricted");
@@ -860,23 +859,6 @@ bin_cd(char *nam, char **argv, Options ops, int func)
     }
     cd_new_pwd(func, dir, OPT_ISSET(ops, 'q'));
 
-    if (stat(unmeta(pwd), &st1) < 0) {
-	setjobpwd();
-	zsfree(pwd);
-	pwd = NULL;
-	pwd = metafy(zgetcwd(), -1, META_DUP);
-    } else if (stat(".", &st2) < 0) {
-	if (chdir(unmeta(pwd)) < 0)
-	    zwarn("unable to chdir(%s): %e", pwd, errno);
-    } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
-	if (chasinglinks) {
-	    setjobpwd();
-	    zsfree(pwd);
-	    pwd = NULL;
-	    pwd = metafy(zgetcwd(), -1, META_DUP);
-	} else if (chdir(unmeta(pwd)) < 0)
-	    zwarn("unable to chdir(%s): %e", pwd, errno);
-    }
     unqueue_signals();
     return 0;
 }
@@ -1210,6 +1192,7 @@ static void
 cd_new_pwd(int func, LinkNode dir, int quiet)
 {
     char *new_pwd, *s;
+    struct stat st1, st2;
     int dirstacksize;
 
     if (func == BIN_PUSHD)
@@ -1239,6 +1222,22 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
 	}
     }
 
+    if (stat(unmeta(new_pwd), &st1) < 0) {
+	zsfree(new_pwd);
+	new_pwd = NULL;
+	new_pwd = metafy(zgetcwd(), -1, META_DUP);
+    } else if (stat(".", &st2) < 0) {
+	if (chdir(unmeta(new_pwd)) < 0)
+	    zwarn("unable to chdir(%s): %e", new_pwd, errno);
+    } else if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) {
+	if (chasinglinks) {
+	    zsfree(new_pwd);
+	    new_pwd = NULL;
+	    new_pwd = metafy(zgetcwd(), -1, META_DUP);
+	} else if (chdir(unmeta(new_pwd)) < 0)
+	    zwarn("unable to chdir(%s): %e", new_pwd, errno);
+    }
+
     /* shift around the pwd variables, to make oldpwd and pwd relate to the
        current (i.e. new) pwd */
     zsfree(oldpwd);
diff --git a/Test/B01cd.ztst b/Test/B01cd.ztst
index 21e751dcb..cd0b79fb2 100644
--- a/Test/B01cd.ztst
+++ b/Test/B01cd.ztst
@@ -33,7 +33,7 @@
 #
 # Tests should use subdirectories ending in `.tmp'.  These will be
 # removed with all the contents even if the test is aborted.
- mkdir cdtst.tmp cdtst.tmp/real cdtst.tmp/sub
+ mkdir cdtst.tmp cdtst.tmp/foo cdtst.tmp/real cdtst.tmp/sub
 
  ln -s ../real cdtst.tmp/sub/fake
 
@@ -149,6 +149,21 @@ F:something is broken.  But you already knew that.
 -f:(workers/45367) cd -P squashes multiple leading slashes
 >/dev  
 
+ chpwd_hook() { hook_pwd=$PWD; }
+ chpwd_functions=(chpwd_hook)
+ cd $mydir/cdtst.tmp/foo &&
+ (cd $mydir; mv $mydir/cdtst.tmp/{foo,bar}) &&
+ print $PWD &&
+ print $hook_pwd &&
+ cd . &&
+ print $PWD
+ print $hook_pwd &&
+0q:cd . with moved PWD
+>$mydir/cdtst.tmp/foo
+>$mydir/cdtst.tmp/foo
+>$mydir/cdtst.tmp/bar
+>$mydir/cdtst.tmp/bar
+
 %clean
 # This optional section cleans up after the test, if necessary,
 # e.g. killing processes etc.  This is in addition to the removal of *.tmp



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