# dirs -- Directory stack manipulation functions for sh, ksh, et al # Brian Katzung 19 January 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 Ksh only - push cwd and cd oldpath newpat #DIRS= case $HOME in /) # Home is /; no ~ substitution DIRSMAP="-e ;" ;; *) # Replace $HOME with ~ by default DIRSMAP="-e s^$HOME~" ;; esac export DIRSMAP _d_fields=`echo +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 \ +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 | tr ' ' '\012'` # Utility functions to avoid having to exec cut, expr, sed, etc. # Return the first n ($1) items from the remaining arguments _d_first () { _which="$1" shift for _field in $_d_fields do # Stop if out of arguments case $1 in "") return 1 ;; esac echo "$1" # Stop after printing first n arguments case $_field in $_which) return 0 ;; esac shift done return 2 } # Return arguments starting after the nth ($1) one _d_skip () { _which="$1" shift for _field in $_d_fields do # Stop if out of arguments case $1 in "") return 1 ;; esac shift # Print arguments after the nth one case $_field in $_which) for _dir do echo "$_dir" done return 0 ;; esac done return 2 } # Return only the nth ($1) item from the remaining arguments _d_item () { _which="$1" shift for _field in $_d_fields do # Stop if out of arguments case $1 in "") return 1 ;; esac # Print and return when we get to the nth argument case $_field in $_which) echo "$1" return 0 ;; esac shift done return 2 } # Return all but the nth ($1) item from the remaining arguments _d_not () { _which="$1" shift for _field in $_d_fields do # Return if out of arguments case $1 in "") return 0 ;; esac # Print if not at the nth argument case $_field in $_which) ;; *) echo "$1" esac shift done return 0 } # Return arguments not matching the first argument _d_except () { _which="$1" shift for _dir do # Print if it doesn't match case $_dir in $_which) ;; *) echo "$_dir" ;; esac done return 0 } # Returns arguments except the first match _d_notfirst () { _which="$1" shift for _dir do case $_dir in # At the first match, print everything after the match $_which) shift for _dir do echo "$_dir" done return 0 ;; esac # Print each item before the first match echo "$_dir" shift done return 0 } # Display the directory stack dirs () { # Accumulate flags _d_flags= for _dir do case $_dir in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done _ifs="$IFS" IFS=" " # Implement -m (move mode) and -u (unique mode) case $_d_flags in *u*) DIRS=`_d_except $PWD $DIRS` ;; *m*) DIRS=`_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 set -- `echo "$PWD $DIRS" | sed ${DIRSMAP}` ;; 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 IFS="$_ifs" return 0 } # Push directory or rearrange the directory stack pushd () { # Accumulate flags _d_flags= for _dir do case $_dir in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done case ${3-} in ?*) echo "Too many arguments to pushd." return 1 ;; esac _pwd="$PWD" _ifs="$IFS" IFS=" " case ${1-} in "") # Swap top two directories shift $# set -- $DIRS case $# in 0) echo "No other directory." IFS="$_ifs" return 1 ;; esac if cd "${1-}" then # New location off stack and old one on shift DIRS="$_pwd $@" fi ;; +[0-9]*)# Rearrange nth or nth..last item(s) on the stack if _dir=`_d_item $1 $DIRS` then : else echo "Directory stack not that deep." IFS="$_ifs" return 1 fi if cd "$_dir" then case $_d_flags in *n*) # New behavior - just push nth item DIRS="$_pwd $DIRS" ;; *) # Move cwd and 1..n-1 to end DIRS="`_d_skip $1 $DIRS` `_d_first $1 $_pwd $DIRS'" ;; 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 IFS="$_ifs" dirs $_d_flags return 0 } # Remove the top directory or a middle directory from the directory stack popd () { _d_flags= for _dir do case $_dir in -*) _d_flags="$_d_flags$_dir" shift ;; *) break ;; esac done case ${2-} in ?*) echo "Too many arguments to popd." return 1 ;; esac _ifs="$IFS" IFS=" " case ${1-} in "") # Pop the top directory set -- $DIRS case ${1-} in "") echo "Directory stack empty." IFS="$_ifs" return 1 ;; esac if cd "$1" then shift DIRS="$@" fi ;; +[0-9]*)# Discard the nth item from the stack _dir=`_d_item "$1" $DIRS` case ${_dir-} in "") echo "Directory stack not that deep." IFS="$_ifs" return 1 ;; esac DIRS=`_d_not "$1" $DIRS` ;; esac IFS="$_ifs" dirs _d_flags return 0 } # Shorthand functions sd () { pushd -mn ${1+"$@"} return $? } pd () { pushd -n ${1+"$@"} return $? }