aboutsummaryrefslogtreecommitdiff
path: root/sh/shrc.d/sd.sh
diff options
context:
space:
mode:
Diffstat (limited to 'sh/shrc.d/sd.sh')
-rw-r--r--sh/shrc.d/sd.sh110
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"
}