summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--Src/builtin.c25
2 files changed, 26 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 3b6b86fe2..2ed7dcc22 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2016-09-30  Barton E. Schaefer  <schaefer@zsh.org>
+
+	* 39509: Src/builtins.c: in the event the current directory has
+	been removed, use chasedots semantics for "cd ..".
+
 2016-09-30  Peter Stephenson  <p.stephenson@samsung.com>
 
 	* 39519: Src/params.c: setting PM_EXPORT was missing in
diff --git a/Src/builtin.c b/Src/builtin.c
index c78fd9b3a..60dc07f25 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1273,7 +1273,23 @@ fixdir(char *src)
 #ifdef __CYGWIN__
     char *s0 = src;
 #endif
-    int ret = 0;
+    /* This function is always called with n path containing at
+     * least one slash, either because one was input by the user or
+     * because the caller has prepended either pwd or a cdpath dir.
+     * If asked to make a relative change and pwd is set to ".",
+     * the current directory has been removed out from under us,
+     * so force links to be chased.
+     *
+     * Ordinarily we can't get here with "../" as the first component
+     * but handle the silly special case of ".." in cdpath.
+     *
+     * Order of comparisons here looks funny, but it short-circuits
+     * most rapidly in the event of a false condition.  Set to 2
+     * here so we still obey the (lack of) CHASEDOTS option after
+     * the first "../" is preserved (test chasedots > 1 below).
+     */
+    int chasedots = (src[0] == '.' && pwd[0] == '.' && pwd[1] == '\0' &&
+		     (src[1] == '/' || (src[1] == '.' && src[2] == '/'))) * 2;
 
 /*** if have RFS superroot directory ***/
 #ifdef HAVE_SUPERROOT
@@ -1305,12 +1321,12 @@ fixdir(char *src)
 	    while (dest > d0 + 1 && dest[-1] == '/')
 		dest--;
 	    *dest = '\0';
-	    return ret;
+	    return chasedots;
 	}
 	if (src[0] == '.' && src[1] == '.' &&
 	    (src[2] == '\0' || src[2] == '/')) {
-	    if (isset(CHASEDOTS)) {
-		ret = 1;
+	    if (isset(CHASEDOTS) || chasedots > 1) {
+		chasedots = 1;
 		/* and treat as normal path segment */
 	    } else {
 		if (dest > d0 + 1) {
@@ -1348,6 +1364,7 @@ fixdir(char *src)
 		    dest[-1] = *src++ ^ 32;
 	}
     }
+    /* unreached */
 }
 
 /**/