diff options
Diffstat (limited to 'scripts/library')
-rw-r--r-- | scripts/library/README | 36 | ||||
-rw-r--r-- | scripts/library/human_to_size.sh | 51 | ||||
-rw-r--r-- | scripts/library/parse_options.sh | 105 | ||||
-rw-r--r-- | scripts/library/parseopts.sh | 141 | ||||
-rw-r--r-- | scripts/library/size_to_human.sh | 22 |
5 files changed, 245 insertions, 110 deletions
diff --git a/scripts/library/README b/scripts/library/README index 1e9c962b..0fa0f847 100644 --- a/scripts/library/README +++ b/scripts/library/README @@ -8,8 +8,34 @@ and can be silenced by defining 'QUIET'. The 'warning' and 'error' functions print to stderr with the appropriate prefix added to the message. -parse_options.sh: -A getopt replacement to avoids portability issues, in particular the -lack of long option name support in the default getopt provided by some -platforms. -Usage: parse_option $SHORT_OPTS $LONG_OPTS "$@" +parseopts.sh: +A getopt_long-like parser which portably supports longopts and shortopts +with some GNU extensions. It does not allow for options with optional +arguments. For both short and long opts, options requiring an argument +should be suffixed with a colon. After the first argument containing +the short opts, any number of valid long opts may be be passed. The end +of the options delimiter must then be added, followed by the user arguments +to the calling program. + +Reccommended Usage: + OPT_SHORT='fb:z' + OPT_LONG=('foo' 'bar:' 'baz') + if ! parseopts "$OPT_SHORT" "${OPT_LONG[@]}" -- "$@"; then + exit 1 + fi + set -- "${OPTRET[@]}" +Returns: + 0: parse success + 1: parse failure (error message supplied) + +human_to_size.sh: +A function to convert human readable sizes (such as "5.3 GiB") to raw byte +equivalents. base10 and base2 suffixes are supported, case sensitively. If +successful, the converted byte value is written to stdout and the function +returns 0. If an error occurs, nothing in written and the function returns 1. +Results may be inaccurate when using a broken implementation of awk, such +as mawk or busybox awk. + +size_to_human.sh: +The reverse of human_to_size, this function takes an integer byte size and +prints its in human readable format, with SI prefixes (e.g. MiB, TiB). diff --git a/scripts/library/human_to_size.sh b/scripts/library/human_to_size.sh new file mode 100644 index 00000000..11613207 --- /dev/null +++ b/scripts/library/human_to_size.sh @@ -0,0 +1,51 @@ +human_to_size() { + awk -v human="$1" ' + function trim(s) { + gsub(/^[[:space:]]+|[[:space:]]+$/, "", s) + return s + } + + function parse_units(units) { + if (!units || units == "B") + return 1 + if (match(units, /^.iB$/)) + return 1024 + if (match(units, /^.B$/)) + return 1000 + if (length(units) == 1) + return 1024 + + # parse failure: invalid base + return -1 + } + + function parse_scale(s) { + return index("BKMGTPE", s) - 1 + } + + function isnumeric(string) { + return match(string, /^[-+]?[[:digit:]]*(\.[[:digit:]]*)?/) + } + + BEGIN { + # peel off the leading number as the size, fail on invalid number + human = trim(human) + if (isnumeric(human)) + size = substr(human, RSTART, RLENGTH) + else + exit 1 + + # the trimmed remainder is assumed to be the units + units = trim(substr(human, RLENGTH + 1)) + + base = parse_units(units) + if (base < 0) + exit 1 + + scale = parse_scale(substr(units, 1, 1)) + if (scale < 0) + exit 1 + + printf "%d\n", size * base^scale + (size + 0 > 0 ? 0.5 : -0.5) + }' +} diff --git a/scripts/library/parse_options.sh b/scripts/library/parse_options.sh deleted file mode 100644 index 039eef92..00000000 --- a/scripts/library/parse_options.sh +++ /dev/null @@ -1,105 +0,0 @@ -# getopt like parser -parse_options() { - local short_options=$1; shift; - local long_options=$1; shift; - local ret=0; - local unused_options="" - local i - - while [[ -n $1 ]]; do - if [[ ${1:0:2} = '--' ]]; then - if [[ -n ${1:2} ]]; then - local match="" - for i in ${long_options//,/ }; do - if [[ ${1:2} = ${i//:} ]]; then - match=$i - break - fi - done - if [[ -n $match ]]; then - local needsargument=0 - - [[ ${match} = ${1:2}: ]] && needsargument=1 - [[ ${match} = ${1:2}:: && -n $2 && ${2:0:1} != "-" ]] && needsargument=1 - - if (( ! needsargument )); then - printf ' %s' "$1" - else - if [[ -n $2 ]]; then - printf ' %s ' "$1" - shift - printf "'%q" "$1" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - else - printf "@SCRIPTNAME@: $(gettext "option %s requires an argument\n")" "'$1'" >&2 - ret=1 - fi - fi - else - echo "@SCRIPTNAME@: $(gettext "unrecognized option") '$1'" >&2 - ret=1 - fi - else - shift - break - fi - elif [[ ${1:0:1} = '-' ]]; then - for ((i=1; i<${#1}; i++)); do - if [[ $short_options =~ ${1:i:1} ]]; then - local needsargument=0 - - [[ $short_options =~ ${1:i:1}: && ! $short_options =~ ${1:i:1}:: ]] && needsargument=1 - [[ $short_options =~ ${1:i:1}:: && \ - ( -n ${1:$i+1} || ( -n $2 && ${2:0:1} != "-" ) ) ]] && needsargument=1 - - if (( ! needsargument )); then - printf ' -%s' "${1:i:1}" - else - if [[ -n ${1:$i+1} ]]; then - printf ' -%s ' "${1:i:1}" - printf "'%q" "${1:$i+1}" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - else - if [[ -n $2 ]]; then - printf ' -%s ' "${1:i:1}" - shift - printf "'%q" "$1" - while [[ -n $2 && ${2:0:1} != "-" ]]; do - shift - printf " %q" "$1" - done - printf "'" - - else - printf "@SCRIPTNAME@: $(gettext "option %s requires an argument\n")" "'-${1:i:1}'" >&2 - ret=1 - fi - fi - break - fi - else - echo "@SCRIPTNAME@: $(gettext "unrecognized option") '-${1:i:1}'" >&2 - ret=1 - fi - done - else - unused_options="${unused_options} '$1'" - fi - shift - done - - printf " --" - [[ $unused_options ]] && printf ' %s' "${unused_options[@]}" - [[ $1 ]] && printf " '%s'" "$@" - printf "\n" - - return $ret -} diff --git a/scripts/library/parseopts.sh b/scripts/library/parseopts.sh new file mode 100644 index 00000000..11589ce3 --- /dev/null +++ b/scripts/library/parseopts.sh @@ -0,0 +1,141 @@ +# getopt-like parser +parseopts() { + local opt= optarg= i= shortopts=$1 + local -a longopts=() unused_argv=() + + shift + while [[ $1 && $1 != '--' ]]; do + longopts+=("$1") + shift + done + shift + + longoptmatch() { + local o longmatch=() + for o in "${longopts[@]}"; do + if [[ ${o%:} = "$1" ]]; then + longmatch=("$o") + break + fi + [[ ${o%:} = "$1"* ]] && longmatch+=("$o") + done + + case ${#longmatch[*]} in + 1) + # success, override with opt and return arg req (0 == none, 1 == required) + opt=${longmatch%:} + if [[ $longmatch = *: ]]; then + return 1 + else + return 0 + fi ;; + 0) + # fail, no match found + return 255 ;; + *) + # fail, ambiguous match + printf "@SCRIPTNAME@: $(gettext "option '%s' is ambiguous; possibilities:")" "--$1" + printf " '%s'" "${longmatch[@]%:}" + printf '\n' + return 254 ;; + esac >&2 + } + + while (( $# )); do + case $1 in + --) # explicit end of options + shift + break + ;; + -[!-]*) # short option + for (( i = 1; i < ${#1}; i++ )); do + opt=${1:i:1} + + # option doesn't exist + if [[ $shortopts != *$opt* ]]; then + printf "@SCRIPTNAME@: $(gettext "invalid option") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + + OPTRET+=("-$opt") + # option requires optarg + if [[ $shortopts = *$opt:* ]]; then + # if we're not at the end of the option chunk, the rest is the optarg + if (( i < ${#1} - 1 )); then + OPTRET+=("${1:i+1}") + break + # if we're at the end, grab the the next positional, if it exists + elif (( i == ${#1} - 1 )) && [[ $2 ]]; then + OPTRET+=("$2") + shift + break + # parse failure + else + printf "@SCRIPTNAME@: $(gettext "option requires an argument") -- '%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + fi + fi + done + ;; + --?*=*|--?*) # long option + IFS='=' read -r opt optarg <<< "${1#--}" + longoptmatch "$opt" + case $? in + 0) + # parse failure + if [[ $optarg ]]; then + printf "@SCRIPTNAME@: $(gettext "option '%s' does not allow an argument")\n" "--$opt" >&2 + OPTRET=(--) + return 1 + # --longopt + else + OPTRET+=("--$opt") + shift + continue 2 + fi + ;; + 1) + # --longopt=optarg + if [[ $optarg ]]; then + OPTRET+=("--$opt" "$optarg") + shift + # --longopt optarg + elif [[ $2 ]]; then + OPTRET+=("--$opt" "$2" ) + shift 2 + # parse failure + else + printf "@SCRIPTNAME@: $(gettext "option '%s' requires an argument")\n" "--$opt" >&2 + OPTRET=(--) + return 1 + fi + continue 2 + ;; + 254) + # ambiguous option -- error was reported for us by longoptmatch() + OPTRET=(--) + return 1 + ;; + 255) + # parse failure + printf "@SCRIPTNAME@: $(gettext "invalid option") '--%s'\n" "$opt" >&2 + OPTRET=(--) + return 1 + ;; + esac + ;; + *) # non-option arg encountered, add it as a parameter + unused_argv+=("$1") + ;; + esac + shift + done + + # add end-of-opt terminator and any leftover positional parameters + OPTRET+=('--' "${unused_argv[@]}" "$@") + unset longoptmatch + + return 0 +} diff --git a/scripts/library/size_to_human.sh b/scripts/library/size_to_human.sh new file mode 100644 index 00000000..1d13eeb4 --- /dev/null +++ b/scripts/library/size_to_human.sh @@ -0,0 +1,22 @@ +size_to_human() { + awk -v size="$1" ' + BEGIN { + suffix[1] = "B" + suffix[2] = "KiB" + suffix[3] = "MiB" + suffix[4] = "GiB" + suffix[5] = "TiB" + suffix[6] = "PiB" + suffix[7] = "EiB" + count = 1 + + while (size > 1024) { + size /= 1024 + count++ + } + + sizestr = sprintf("%.2f", size) + sub(/\.?0+$/, "", sizestr) + printf("%s %s", sizestr, suffix[count]) + }' +} |