From b58489d29a1898a4bff1b5da6ca4bfb14003dbfe Mon Sep 17 00:00:00 2001 From: Dave Reisner Date: Wed, 25 Apr 2012 22:27:19 -0400 Subject: [PATCH] scripts/library: add human_to_size This is a bash wrapper around an awk function that parses human readable sizes and returns their representative values in bytes, as a string. A small test harness is added to validate the functionality. Signed-off-by: Dave Reisner --- Makefile.am | 2 + scripts/Makefile.am | 3 +- scripts/library/README | 8 ++++ scripts/library/human_to_size.sh | 51 ++++++++++++++++++++ test/scripts/Makefile.am | 3 +- test/scripts/human_to_size_test.sh | 75 ++++++++++++++++++++++++++++++ 6 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 scripts/library/human_to_size.sh create mode 100644 test/scripts/human_to_size_test.sh diff --git a/Makefile.am b/Makefile.am index 7d6f3065..0010a3eb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,6 +41,8 @@ test-vercmp: test/util src/util test-parseopts: test/scripts scripts $(BASH_SHELL) $(top_srcdir)/test/scripts/parseopts_test.sh \ $(top_srcdir)/scripts/library/parseopts.sh + $(BASH_SHELL) $(top_srcdir)/test/scripts/human_to_size_test.sh \ + $(top_srcdir)/scripts/library/human_to_size.sh # create the pacman DB and cache directories upon install install-data-local: diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 6bb72198..75699b2e 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -27,7 +27,8 @@ EXTRA_DIST = \ LIBRARY = \ library/output_format.sh \ - library/parseopts.sh + library/parseopts.sh \ + library/human_to_size.sh # Files that should be removed, but which Automake does not know. MOSTLYCLEANFILES = $(bin_SCRIPTS) diff --git a/scripts/library/README b/scripts/library/README index c71c0714..44748ee2 100644 --- a/scripts/library/README +++ b/scripts/library/README @@ -27,3 +27,11 @@ Reccommended Usage: 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. 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/test/scripts/Makefile.am b/test/scripts/Makefile.am index b949e880..d525d894 100644 --- a/test/scripts/Makefile.am +++ b/test/scripts/Makefile.am @@ -1,5 +1,6 @@ check_SCRIPTS = \ - parseopts_test.sh + parseopts_test.sh \ + human_to_size_test.sh noinst_SCRIPTS = $(check_SCRIPTS) diff --git a/test/scripts/human_to_size_test.sh b/test/scripts/human_to_size_test.sh new file mode 100644 index 00000000..1d747946 --- /dev/null +++ b/test/scripts/human_to_size_test.sh @@ -0,0 +1,75 @@ +#!/bin/bash + +# source the library function +if [[ -z $1 || ! -f $1 ]]; then + printf "error: path to human_to_size library not provided or does not exist\n" + exit 1 +fi +. "$1" + +if ! type -t human_to_size >/dev/null; then + printf 'human_to_size function not found\n' + exit 1 +fi + +parse_hts() { + local input=$1 expected=$2 result + + (( ++testcount )) + + result=$(human_to_size "$1") + if [[ $result = "$expected" ]]; then + (( ++pass )) + else + (( ++fail )) + printf '[TEST %3s]: FAIL\n' "$testcount" + printf ' input: %s\n' "$input" + printf ' output: %s\n' "$result" + printf ' expected: %s\n' "$expected" + fi +} + +summarize() { + if (( !fail )); then + printf 'All %s tests successful\n' "$testcount" + exit 0 + else + printf '%s of %s tests failed\n' "$fail" "$testcount" + exit 1 + fi +} +trap 'summarize' EXIT + +printf 'Beginning human_to_size tests\n' + +# parse_hts + +parse_hts '1MiB' 1048576 + +parse_hts '10XiB' '' + +parse_hts '10 MiB' 10485760 + +parse_hts '10 XiB' '' + +parse_hts '.1 TiB' 109951162778 + +parse_hts ' -3 KiB ' -3072 + +parse_hts 'foo3KiB' '' + +parse_hts '3KiBfoo' '' + +parse_hts '3kib' '' + +parse_hts '+1KiB' 1024 + +parse_hts '+1.0 KiB' 1024 + +parse_hts '1MB' 1000000 + +parse_hts '1M' 1048576 + +parse_hts ' 1 G ' 1073741824 + +parse_hts '1Q' ''