# zdirs -- Directory stack manipulation functions for zsh # Brian Katzung 20 March 1993 # # Csh compatible: # dirs [-l] Stack listing (long) # popd Cd to top of stack and pop # popd +n Pop the nth item from the stack # pushd Swap cwd and top of stack # pushd +n Move cwd and 1..n-1 to end; cd top and pop # pushd directory Push cwd and cd to directory # # Extensions: # dirs [-muv] Stack listing (implement move, implement unique, # list vertically) # sd pushd -mn # pd pushd -n # pushd . pattern Push the first directory containing pattern # pushd -n +n Push cwd and cd to former nth item # pushd oldpat newpat push cwd and cd oldpath newpat #DIRS= # WORKAROUND ("case $something in" may expand to more than three tokens!) case "$HOME" in /) # Home is /; no ~ substitution dirsmap= ;; *) # Replace $HOME with ~ by default dirsmap=("$HOME" "~") ;; esac # Utility functions to avoid having to exec cut, expr, sed, etc. # Set stack to directories not matching the first argument _d_except () { local _which="$1" _dir= shift dirs= for _dir do case "$_dir" in $_which) ;; *) dirs=(${=dirs} "$_dir") ;; esac done return 0 } # Returns arguments except the first match _d_notfirst () { local _which="$1" _dir= shift dirs= for _dir do case "$_dir" in # At the first match, print everything after the match $_which) shift dirs=(${=dirs} "$@") return 0 ;; esac # Print each item before the first match dirs=(${=dirs} "$_dir") shift done return 0 } # Display the directory stack dirs () { local _d_flags= _dir= _ind= _from= _to= # Accumulate flags for _dir do case "$_dir" in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done # Implement -m (move mode) and -u (unique mode) case "$_d_flags" in *u*) _d_except "$PWD" ${=dirs} ;; *m*) _d_notfirst "$PWD" ${=dirs} ;; esac # Set up for short or long format case "$_d_flags" in *l*) # Long listing (no ~ substitution) set -- "$PWD" ${=dirs} ;; *) # Shortened listing shift $# for _dir in "$PWD" ${=dirs} do (( _ind = 1 )) while (( $_ind < $#dirsmap )) do _from=$dirsmap[$_ind] (( ++_ind )) _to=$dirsmap[$_ind] (( ++_ind )) eval _dir=\${_dir:s"$_from\\$_to"} done # WORKAROUND (${1+"$@"} -> "" if $# == 0!) set -- ${1:+"$@"} "$_dir" done ;; esac # Print in horizontal or vertical format case "$_d_flags" in *v*) # Vertical listing (1/line) for _dir do echo "$_dir" done ;; *) # Horizontal echo "$*" ;; esac return 0 } # Push directory or rearrange the directory stack pushd () { local _d_flags= _dir= _pwd= # Accumulate flags for _dir do case "$_dir" in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done if (( $# > 2 )) then echo "Too many arguments to pushd." return 1 fi _pwd="$PWD" case "${1-}" in "") # Swap top two directories if (( $#dirs == 0 )) then echo "No other directory." return 1 fi if cd "$dirs[1]" then # New location off stack and old one on set -- ${=dirs} dirs=("$_pwd" ${=@[2,$#]}) fi ;; +[0-9]*)# Rearrange nth or nth..last item(s) on the stack if (( $1 > $#dirs )) then echo "Directory stack not that deep." return 1 fi if cd "$dirs[$1]" then case "$_d_flags" in *n*) # New behavior - just push nth item dirs=("$_pwd" ${=dirs}) ;; *) # Move cwd and 1..n-1 to end dirs=("$_pwd" ${=dirs}) set -- $1 $[$1+2] $#dirs dirs=(${=dirs[$2,$3]} ${=dirs[1,$1]}) ;; esac fi ;; .) case "${2-}" in "") # pushd . (backward compatibility) dirs=("$_pwd" ${=dirs}) ;; *) # Push first stack item containing $2 for _dir in ${=dirs} do case "$_dir" in *"$2"*) _pwd="$PWD" if cd "$_dir" then dirs=("$_pwd" ${=dirs}) fi break ;; esac done ;; esac ;; *) # Push new directory _pwd="$PWD" if cd "$1" ${2:+"$2"} then dirs=("$_pwd" ${=dirs}) fi ;; esac dirs $_d_flags return 0 } # Remove the top directory or a middle directory from the directory stack popd () { local _d_flags= _dir= for _dir do case "$_dir" in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done if (( $# > 1 )) then echo "Too many arguments to popd." return 1 fi case "${1-}" in "") # Pop the top directory if (( $#dirs == 0 )) then echo "Directory stack empty." return 1 fi if cd "$dirs[1]" then set -- ${=dirs} dirs=(${=dirs[2,$#]}) fi ;; +[0-9]*)# Discard the nth item from the stack if (( $1 > $#dirs )) then echo "Directory stack not that deep." return 1 fi set -- $[$1-1] $[$1+1] $#dirs if (( $1 == 0 )) then # WORKAROUND ($dirs[1,0] gives $dirs[1]!) dirs=(${=dirs[$2,$3]}) else dirs=(${=dirs[1,$1]} ${=dirs[$2,$3]}) fi ;; esac dirs $_d_flags return 0 } # Shorthand functions sd () { pushd -mn ${1:+"$@"} return $? } pd () { pushd -n ${1:+"$@"} return $? }