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]) +	}' +} | 
