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

[PATCH] Add option like tcsh's dextract



Hi all!

This is my first post to the group. I’d been using tcsh as an interactive shell since way back when it was still cool, before zsh or bash even existed. Zsh is the first shell that’s a worthy successor. One of the small things that kept me from jumping to bash (along with everyone else in the Linux world) is that it doesn’t have a proper implementation of asynchronous notify of job completion (-b), which zsh does. Another thing that bugged me about bash was that there is no clean way to emulate tcsh’s dextract option, which rearranges the pushd stack differently. I eventually discovered that zsh can do the basic function through the cd/chdir builtin with the auto_pushd option set, but coding a pushd replacement function was complicated to get right for all option cases (see attached).

However, I found that adding this pushd mode to zsh natively was trivial (simply testing for the new option in one place), and I’m still baffled why it wasn’t included a long time ago while someone was looking for ways to increase compatibility with other shells. The attached patch (based on the current master branch) does just that, and I hope you see fit to merge it into the codebase. I believe I’ve done all the appropriate option handling, documentation, and unit test to make this painless. I didn’t write a ChangeLog entry since I wasn’t sure of the appropriate format, or how to derive the number. (Is that an SVN revision number?)

Tim

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 5393cb149..47c15583b 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1459,6 +1459,7 @@ pindex(PUSHD_TO_HOME, use of)
 pindex(PUSHD_MINUS, use of)
 pindex(CDABLE_VARS, use of)
 pindex(PUSHD_SILENT, use of)
+pindex(PUSHD_EXTRACT, use of)
 xitem(tt(pushd) [ tt(-qsLP) ] [ var(arg) ])
 xitem(tt(pushd) [ tt(-qsLP) ] var(old) var(new))
 item(tt(pushd) [ tt(-qsLP) ] {tt(PLUS())|tt(-)}var(n))(
@@ -1473,8 +1474,10 @@ Otherwise, var(arg) is interpreted as it would be by tt(cd).
 The meaning of var(old) and var(new) in the second form is also
 the same as for tt(cd).
 
-The third form of tt(pushd) changes directory by rotating the
-directory list.  An argument of the form `tt(PLUS())var(n)' identifies a stack
+The third form of tt(pushd) changes directory either by rotating the
+directory list (the default), or by extracting an entry from the directory
+list and pushing it on top (when the tt(PUSHD_EXTRACT) option is set).
+An argument of the form `tt(PLUS())var(n)' identifies a stack
 entry by counting from the left of the list shown by the tt(dirs)
 command, starting with zero.  An argument of the form `tt(-)var(n)' counts
 from the right.  If the tt(PUSHD_MINUS) option is set, the meanings
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index cbd3d0f8e..3a3e4b629 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -162,6 +162,18 @@ shells); and any use of a component of tt(CDPATH), including a `tt(.)' but
 excluding an empty component that is otherwise treated as `tt(.)', causes
 the directory to be printed.
 )
+pindex(PUSHD_EXTRACT)
+pindex(NO_PUSHD_EXTRACT)
+pindex(PUSHDEXTRACT)
+pindex(NOPUSHDEXTRACT)
+cindex(directory stack, altering reordering)
+item(tt(PUSHD_EXTRACT))(
+A push using `tt(PLUS())' or `tt(-)' will reorder by moving the specified
+entry to the top, preserving the order of the remainder below (like the
+tt(dextract) option of bf(tcsh)), instead of the default which rotates the
+stack so that the specified entry is on top. This option makes tt(pushd)
+using `tt(PLUS())' or `tt(-)' behave like tt(cd) with tt(AUTO_PUSHD) set.
+)
 pindex(PUSHD_IGNORE_DUPS)
 pindex(NO_PUSHD_IGNORE_DUPS)
 pindex(PUSHDIGNOREDUPS)
diff --git a/NEWS b/NEWS
index 0e726699f..47c063b40 100644
--- a/NEWS
+++ b/NEWS
@@ -18,6 +18,9 @@ consistent and better aligned with the POSIX-2017 specification of
 `set -e`. For details on what exactly changed, see the list of
 incompatibilities in the README file.
 
+The option PUSHD_EXTRACT was added to alter how pushd reorders the stack,
+in the same way as the dextract option of tcsh.
+
 Changes since 5.8.1
 -------------------
 
diff --git a/Src/builtin.c b/Src/builtin.c
index 669a47092..3b1705a0f 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1194,7 +1194,7 @@ cd_new_pwd(int func, LinkNode dir, int quiet)
     struct stat st1, st2;
     int dirstacksize;
 
-    if (func == BIN_PUSHD)
+    if (func == BIN_PUSHD && unset(PUSHDEXTRACT))
 	rolllist(dirstack, dir);
     new_pwd = remnode(dirstack, dir);
 
diff --git a/Src/options.c b/Src/options.c
index a994b563e..b0bee7e6d 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -231,6 +231,7 @@ static struct optname optns[] = {
 {{NULL, "promptpercent",      OPT_NONBOURNE},		 PROMPTPERCENT},
 {{NULL, "promptsp",	      OPT_ALL},			 PROMPTSP},
 {{NULL, "promptsubst",	      OPT_BOURNE},		 PROMPTSUBST},
+{{NULL, "pushdextract",       OPT_EMULATE},		 PUSHDEXTRACT},
 {{NULL, "pushdignoredups",    OPT_EMULATE},		 PUSHDIGNOREDUPS},
 {{NULL, "pushdminus",	      OPT_EMULATE},		 PUSHDMINUS},
 {{NULL, "pushdsilent",	      0},			 PUSHDSILENT},
diff --git a/Src/zsh.h b/Src/zsh.h
index a0243e98e..85bd97fba 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2508,6 +2508,7 @@ enum {
     PROMPTPERCENT,
     PROMPTSP,
     PROMPTSUBST,
+    PUSHDEXTRACT,
     PUSHDIGNOREDUPS,
     PUSHDMINUS,
     PUSHDSILENT,
diff --git a/Test/E01options.ztst b/Test/E01options.ztst
index 533e08773..89860c78f 100644
--- a/Test/E01options.ztst
+++ b/Test/E01options.ztst
@@ -938,6 +938,25 @@
 >waaah
 >`echo waaah`
 
+  mkdir newpd
+  cd $mydir
+  pushd $mydir/tmpcd
+  pushd $mydir/newpd
+  dirs
+  pushd +1
+  dirs
+  setopt pushdextract
+  pushd +1
+  dirs
+  unsetopt pushdextract
+  popd >/dev/null
+  popd >/dev/null
+  cd $mydir
+0q:PUSHD_EXTRACT option
+>$mydirt/newpd $mydirt/tmpcd $mydirt
+>$mydirt/tmpcd $mydirt $mydirt/newpd
+>$mydirt $mydirt/tmpcd $mydirt/newpd
+
   dirs
   pushd $mydir/tmpcd
   dirs
@@ -1459,7 +1478,7 @@ F:If this test fails at the first unsetopt, refer to P01privileged.ztst.
   fi
   BEL=$'\a'
 0q:RM_STAR_SILENT
-*>zsh: sure you want to delete all 15 files in ${PWD:h}/options.tmp \[yn\]\? ${BEL}(|n)
+*>zsh: sure you want to delete all 16 files in ${PWD:h}/options.tmp \[yn\]\? ${BEL}(|n)
 *>zsh: sure you want to delete (all <->|more than <->) files in / \[yn\]\? ${BEL}(|n)
 
   () {
# In zsh, emulate the behavior of pushd with tcsh's dextract option
# Version 1.0, written by Timothy R. Eliseo

# Don't need the function def if native option exists
if ! setopt pushd_extract 2> /dev/null; then
    pushd() {
	setopt local_options extended_glob auto_pushd cd_silent
	zmodload zsh/parameter      # Need $dirstack[]

	# Gather any option flags, duplicating builtin behavior of only recognizing
	# certain options, and only before non-option arguments.
	local -a opts
	while [[ $# -gt 0 && $1 == -[qsLP]## ]]; do
	    opts+=("$1")
	    [[ $1 != *q* ]] || setopt pushd_silent
	    shift
	done

	# The chdir/cd builtin, with one argument in [+|-]n form and with
	# the auto_pushd option set, has the desired stack extract behavior,
	# instead of pushd's stack rotation. For better error output, we also
	# check if the index is in the range that would have different behavior than
	# pushd. This range check is the same regardless of +/- because pushing
	# either the first or last entry has the same result with extraction or
	# rotation.
	if [[ $# -eq 1 && ! -o posix_cd &&
	  $1 == [+-][0-9]## && $1 -ne 0 && ${1:1} -lt ${#dirstack[@]} ]]; then
	    # Use chdir to pushd with extract. cd_silent suppresses its normal
	    # output, and then we execute dirs, as pushd would, if appropriate.
	    builtin chdir "${opts[@]}" "$@" && { [[ ! -o interactive ||
	      -o pushd_silent ]] || builtin dirs; }
	else        # Otherwise just execute pushd with original args
	    builtin pushd "${opts[@]}" "$@"
	fi
    }
fi


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