Skip to content

Instantly share code, notes, and snippets.

@ryancdotorg
Created September 11, 2024 19:22
Show Gist options
  • Save ryancdotorg/dc368d1712ca067c28c2e8afad8922ff to your computer and use it in GitHub Desktop.
Save ryancdotorg/dc368d1712ca067c28c2e8afad8922ff to your computer and use it in GitHub Desktop.
#!/bin/sh /dev/null
# vim: ft=bash:
__pat_safe() { expr "${1-}" : '[^[*!?:]*$' > /dev/null; }
if [ -z "${BASH_VERSION:-}" ]; then
# posix shells can't do variable indirection or pattern substitution
# directly, so we need to use eval and assorted commands
# https://stackoverflow.com/a/36235741/370695
__var_safe() { expr "${1-}" : '[[:alpha:]_][[:alnum:]_]*$' > /dev/null; }
pathlist_append() {
if __var_safe "${1-}" && [ -d "${2-}" ]; then
eval "export ${1}=\"\${${1}:+\$${1}:}\$2\""
fi
}
pathlist_prepend() {
if __var_safe "${1-}" && [ -d "${2-}" ]; then
eval "export ${1}=\"\$2\${${1}:+:\$${1}}\""
fi
}
pathlist_has() {
if __var_safe "${1-}" && __pat_safe "${2-}"; then
local SAFE_DIR="$(printf '%s' "${2}" | sed 's/[$.*[\^]/\\&/g')"
eval "printf '%s\\n' \"\${${1}}\"" | grep "\\(:\\|^\\)${SAFE_DIR}\\(:\\|$\\)" > /dev/null
return $?
fi
return 1
}
pathlist_remove() {
if __var_safe "${1-}" && __pat_safe "${2-}"; then
#local NEW_VALUE="$(eval printf '%s' \"\${${1}}\" | awk -v REM="${2}" -v RS=: -v ORS=: '$0 != REM' | sed 's/:$//')"
# adapted from https://www.linuxjournal.com/content/removing-duplicate-path-entries
local NEW_VALUE="$(eval printf '%s' \"\${${1}}\" | awk -v REM="${2}" -v RS=: '($0 != REM) {a[$0]; printf("%s%s", length(a) > 1 ? ":" : "", $0)}')"
eval "export ${1}=\"${NEW_VALUE}\""
fi
}
pathlist_dedupe() {
if __var_safe "${1-}"; then
local NEW_VALUE="$(eval printf '%s' \"\${${1}}\" | awk -v RS=: '!($0 in a) {a[$0]; printf("%s%s", length(a) > 1 ? ":" : "", $0)}')"
eval "export ${1}=\"${NEW_VALUE}\""
fi
}
else
# bash has enough functionality that everything can be done with builtins
pathlist_append() { [ -n "$1" -a -d "$2" ] && export "$1"="${!1:+${!1}:}$2"; }
pathlist_prepend() { [ -n "$1" -a -d "$2" ] && export "$1"="$2${!1:+:${!1}}"; }
pathlist_has() {
if [ -n "$1" ] && __pat_safe "${2-}"; then
if [ "${!1}" = "$2" ]; then # exact match
return 0
else # try to remove the path component...
local __VALUE="${!1}"
local __LENGTH="${#__VALUE}"
__VALUE="${__VALUE//":$2:"/":"}"
__VALUE="${__VALUE/#"$2:"/}"
__VALUE="${__VALUE/%":$2"/}"
# ...and check whether the length changed
[ "${#__VALUE}" -ne ${__LENGTH} ] && return 0
fi
#expr "${!1}" : "$(printf '%s' "$2" | sed 's/[$.*[\^]/\\&/g;s/\(.*\)/\\(\1:.\\+\\|.\\+:\1:.\\+\\|\1$\\|.\\+:\1$\\)/')" > /dev/null
#return $?
fi
return 1
}
pathlist_remove() {
# https://stackoverflow.com/a/19587970/370695
if [ -n "$1" ] && __pat_safe "${2-}"; then
local __VALUE="${!1}"
local __LENGTH="${#__VALUE}"
while :; do # remove from the middle until nothing happens
__VALUE="${__VALUE//":$2:"/":"}"
[ ${#__VALUE} -eq ${__LENGTH} ] && break
__LENGTH="${#__VALUE}"
done
__VALUE="${__VALUE/#"$2:"/}"
__VALUE="${__VALUE/%":$2"/}"
if [ "${__VALUE}" = "$2" ]; then
export "$1"=""
else
export "$1"="${__VALUE}"
fi
fi
}
pathlist_dedupe() {
local -A __KEYS
local -a __LIST
local __VALUE
readarray -t __LIST <<< "${!1//":"/$'\n'}"
for ENTRY in "${__LIST[@]}"; do
if [ "${__KEYS["${ENTRY}"]-0}" -eq 0 ]; then
if [ "${#__KEYS[@]}" -gt 0 ]; then
__VALUE="${__VALUE}:${ENTRY}"
else
__VALUE="${ENTRY}"
fi
__KEYS["${ENTRY}"]=1
fi
done
export "$1"="${__VALUE}"
}
fi
pathlist_cond_append() {
pathlist_has "$1" "$2" || pathlist_append "$1" "$2"
}
pathlist_cond_prepend() {
pathlist_has "$1" "$2" || pathlist_prepend "$1" "$2"
}
pathlist_head() {
pathlist_remove "$1" "$2"
pathlist_prepend "$1" "$2"
}
pathlist_cond_head() {
pathlist_has "$1" "$2" && pathlist_head "$1" "$2"
}
pathlist_tail() {
pathlist_remove "$1" "$2"
pathlist_append "$1" "$2"
}
pathlist_cond_tail() {
pathlist_has "$1" "$2" && pathlist_tail "$1" "$2"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment