123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- #!/bin/bash
-
- util__gate_printable() {
- #
- # True if stdin had non-whitespace values
- #
- local value
- value=$(cat)
- grep -q '[[:print:]]' <<<"$value" || return 1
- echo "$value"
- }
-
- util__isa_bool() {
- #
- # True if $1 is a boolean value
- #
- test "$1" == false && return 0
- test "$1" == true && return 0
- return 1
- }
-
- util__isa_enum() {
- #
- # True if $1 is one of $2..
- #
- local value=$1; shift
- local alloweds=("$@")
- local allowed
- for allowed in "${alloweds[@]}"; do
- test "$value" == "$allowed" && return 0
- done
- return 1
- }
-
- util__isa_int() {
- #
- # True if $1 is an integer
- #
- # Note that $1 does not have to be decimal; in POSIX shells,
- # eg. 0xf1 is a valid hexadecimal integer and 0755 is a valid
- # octal integer.
- #
- {
- test -z "$1" && return 1
- test "$1" -ge 0 && return 0
- return 1
- } 2>/dev/null
- }
-
- util__isa_name() {
- #
- # True if $1 is a name in most languages
- #
- echo "$1" | grep -qx '[[:alpha:]_][[:alnum:]_]*'
- }
-
- util__isa_posint() {
- #
- # True if $1 is a positive integer
- #
- {
- test -z "$1" && return 1
- test "$1" -ge 0 && return 0
- return 1
- } 2>/dev/null
- }
-
- util__join() {
- #
- # Use $1 to join items $2..
- #
- local dlm=$1; shift
- local items=("$@")
- local item
- local head=true
- for item in "${items[@]}"; do
- $head || echo -n "$dlm"
- $head && head=false
- echo -n "$item"
- done
- }
-
- util__loadarr() {
- #
- # Save output lines from command $2.. to array named $1
- #
- # Usage:
- # util__loadarr ARRAY CMD [ARG]..
- #
- # Unlike `mapfile -t <<<"$(command)", this produces zero
- # items if command output was empty.
- #
- local __util__arr_fromcmd__oarr=$1; shift
- local __util__arr_fromcmd__cmd=("$@")
- local __util__arr_fromcmd__tmp
- local __util__arr_fromcmd__es
- __util__arr_val_oarr "$__util__arr_fromcmd__oarr" || return 2
- __util__arr_fromcmd__tmp=$(mktemp /tmp/util__loadarr.__util__arr_fromcmd__tmp.XXXXX)
- "${__util__arr_fromcmd__cmd[@]}" > "$__util__arr_fromcmd__tmp"; __util__arr_fromcmd__es=$?
- mapfile -t "$__util__arr_fromcmd__oarr" <"$__util__arr_fromcmd__tmp"
- rm "$__util__arr_fromcmd__tmp"
- return $__util__arr_fromcmd__es
- }
-
- util__loadenv() {
- #
- # Safely load value from envvar $1
- #
- local name=$1
- local value
- value=${!name}
- test -n "$value" || return 1
- echo "$value"
- }
-
- util__loadenv_bool() {
- #
- # Safely load integer from envvar $1
- #
- local name=$1
- __util__safe_loadenv \
- "$name" \
- util__isa_bool \
- "not a boolean (true|false): $name=$value"
- }
-
- util__loadenv_enum() {
- #
- # Safely load enum from envvar $1, allowing values $2..
- #
- local name=$1; shift
- local allowed_values=("$@")
- local value
- local desc_allowed
- value=${!name}
- test -n "$value" || return 1
- util__isa_enum "$value" "${allowed_values[@]}" || {
- desc_allowed=$(util__join "|" "${allowed_values[@]}")
- warn "not one of supported values ($desc_allowed): $name = $value"
- return 3
- }
- echo "$value"
- }
-
- util__loadenv_int() {
- #
- # Safely load integer from envvar $1
- #
- local name=$1
- __util__safe_loadenv \
- "$name" \
- util__isa_int \
- "not an integer"
- }
-
- util__loadenv_name() {
- #
- # Safely load positive integer from envvar $1
- #
- local name=$1
- __util__safe_loadenv \
- "$name" \
- util__isa_name \
- "not a valid name (alphanumeric, starting with letter)"
- }
-
- util__loadenv_posint() {
- #
- # Safely load positive integer from envvar $1
- #
- local name=$1
- __util__safe_loadenv \
- "$name" \
- util__isa_posint \
- "not a positive integer"
- }
-
- util__sort_by_length() {
- #
- # Sort items on stdin by length
- #
- while IFS= read -r item; do
- echo "${#item} $item"
- done \
- | sort -n \
- | cut -d' ' -f2-
- }
-
- util__warnbox() {
- #
- # Emit warning $2 box-decorated with char $1, follow by lines $3..
- #
- local char=$1; shift
- local subj=$1; shift
- local body=("$@")
- case "${#body[*]}" in
- 0) __util__warnbox1 "$char" "$subj" ;;
- *) __util__warnbox_sub "$char" "$subj" "${body[@]}" ;;
- esac
- }
-
- __util__warnbox1() {
- #
- # Emit warning $2 box-decorated with char $1
- #
- local char=$1
- local msg=$2
- local line
- local midline="$char$char $msg $char$char"
- line=$(__util__make_hr "$char" "$midline")
- warn "$line" \
- "$midline" \
- "$line"
- }
-
- __util__warnbox_sub() {
- #
- # Emit warning $2 box-decorated with char $1, follow by lines $3..
- #
- local char=$1; shift # decoration character
- local subj=$1; shift # local of the message subject
- local body=("$@") # lines of the message body
- local longest # longest line from subject and body
- local width # length of ^^
- local hr # horizontal ruler
- local vr="$char$char" # vertical ruler point
- local padded_body # body lines; padded
- local padded_subj # body line; padded
- local line # each of ^^^
- local out_lines=() # final lines
- longest=$(__util__longest "$subj" "${body[@]}")
- width=${#longest}
- padded_subj=$(__util__rpad "${width}" "$subj")
- util__loadarr padded_body __util__rpad "$width" "${body[@]}"
- debug_var longest width padded_subj padded_body
- hr=$(__util__make_hr "$char" "$vr $longest $vr")
- out_lines+=(
- "$hr"
- "$vr $padded_subj $vr"
- "$hr"
- )
- for line in "${padded_body[@]}"; do
- out_lines+=("$vr $line $vr")
- done
- out_lines+=("$hr")
- warn "${out_lines[@]}"
- }
-
- __util__longest() {
- #
- # Choose longest of $@
- #
- printf '%s\n' "$@" \
- | util__sort_by_length \
- | tail -n 1
- }
-
- __util__make_hr() {
- #
- # Repeat char $1 to the length of $2
- #
- local char=$1
- local stuff=$2
- local length=${#stuff}
- __util__repchar "$char" "$length"
- }
-
- __util__repchar() {
- #
- # Repeat char $1 $2 times
- #
- local char=$1
- local times=$2
- local togo=$times
- while test "$togo" -gt 0; do
- echo -n "$char"
- ((togo--))
- done
- echo
- }
-
- __util__rpad() {
- #
- # Right-pad all of $2.. to length $1 with space
- #
- local wid=$1; shift
- local rest=("$@")
- printf "%-${wid}s"'\n' "${rest[@]}"
- }
-
- __util__safe_loadenv() {
- #
- # Safely load envvar $1 with validator $2 and error message $3
- #
- local name=$1
- local validator=$2
- local msg=$3
- local value
- value=${!name}
- debug_var name value
- test -n "$value" || return 1
- "$validator" "$value" || {
- warn "$msg: $name=$value"
- return 3
- }
- echo "$value"
- }
-
- __util__isa_name() {
- #
- # True if $1 is a name in most languages
- #
- echo "$1" | grep -qx '[[:alpha:]_][[:alnum:]_]*'
- }
-
- __util__arr_val_oarr() {
- #
- # Validate output array named $1
- #
- local name=$1
- __util__isa_name "$name" || { warn "invalid array name: $name"; return 2; }
- }
|