diff options
Diffstat (limited to 'sh/shrc.d/sd.sh')
-rw-r--r-- | sh/shrc.d/sd.sh | 110 |
1 files changed, 70 insertions, 40 deletions
diff --git a/sh/shrc.d/sd.sh b/sh/shrc.d/sd.sh index 4d63b7d6..8b12c170 100644 --- a/sh/shrc.d/sd.sh +++ b/sh/shrc.d/sd.sh @@ -1,4 +1,3 @@ -# # Shortcut to switch to another directory with the same parent, i.e. a sibling # of the current directory. # @@ -31,7 +30,6 @@ # /tmp/tmp.ZSunna5Eup/a # # Seems to work for symbolic links. -# sd() { # Check argument count @@ -40,48 +38,80 @@ sd() { return 2 fi - # Change positional parameters to what will hopefully be a completed - # substitution - set -- "$( + # Read sole optional argument + case $1 in + + # Slashes aren't allowed + */*) + printf >&2 'bd(): Illegal slash\n' + return 2 + ;; + + # If blank, we try to find if there's just one sibling, and change to + # that if so + '') + # First a special case: root dir + case $PWD in + *[!/]*) ;; + *) + printf >&2 'sd(): No siblings\n' + return 1 + ;; + esac + + # Get a full list of directories at this level; include dotfiles, + # but not the . and .. entries, using glob tricks to avoid Bash + # ruining things with `dotglob` + set -- ../[!.]*/ + [ -e "$1" ] || shift + set -- ../.[!.]*/ "$@" + [ -e "$1" ] || shift + set -- ../..?*/ "$@" + [ -e "$1" ] || shift + + # Check the number of matches + case $# in + + # One match? Must be $PWD, so no siblings--throw in 0 just in + # case, but that Shouldn't Happen (TM) + 0|1) + printf >&2 'sd(): No siblings\n' + return 1 + ;; - # Set the positional parameters to either the requested directory, or - # all siblings of the current directory if no request - spec=$1 - set -- - if [ -n "$spec" ] ; then - set -- "$@" ../"$spec" - else - for sib in ../.* ../* ; do - case ${sib#../} in - (.|..|"${PWD##*/}") continue ;; - esac - set -- "$@" "$sib" - done - fi + # Two matches; hopefully just one sibling, but which is it? + 2) - # We should have exactly one sibling - case $# in - (1) ;; - (0) - printf >&2 'sd(): No siblings\n' - exit 1 - ;; - (*) - printf >&2 'sd(): More than one sibling\n' - exit 1 - ;; - esac + # Push PWD onto the stack, strip trailing slashes + set -- "$1" "$2" "$PWD" + while : ; do + case $3 in + */) set -- "$1" "$2" "${3%/}" ;; + *) break ;; + esac + done - # Print the target with trailing slash to work around newline stripping - printf '%s/' "${1%/}" - )" + # Pick whichever of our two parameters doesn't look like + # PWD as our sole parameter + case $1 in + ../"${3##*/}"/) set -- "$2" ;; + *) set -- "$1" ;; + esac + ;; - # Remove trailing slash - set -- "${1%/}" + # Anything else? Multiple siblings--user will need to specify + *) + printf >&2 'sd(): Multiple siblings\n' + return 1 + ;; + esac + ;; - # If the subshell printed nothing, return with failure - [ -n "$1" ] || return + # If not, simply set our target to that directory, and let `cd` do the + # complaining if it doesn't exist + *) set -- ../"$1" ;; + esac - # Try to change into the determined directory - command cd -- "$@" + # Try and change into the first parameter + command cd -- "$1" } |