diff options
author | Rob Landley <rob@landley.net> | 2015-09-03 20:36:44 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2015-09-03 20:36:44 -0500 |
commit | d06ea3708d12e3e87fa26441715ac47f6dc63032 (patch) | |
tree | 18a517dd614a27ec99b36f51822a90d3a1155736 | |
parent | 5640847b0328c5319f353eed985781e166547ef7 (diff) | |
download | toybox-d06ea3708d12e3e87fa26441715ac47f6dc63032.tar.gz |
Make human_readable() handle base 1024 units without floating point.
Rounds correctly via brute force, displayed digits are decimal even when
working with powers of 2, shows at most 3 significant (decimal) digits.
(So no "1023M" nonsense, that's 1.0G.)
-rw-r--r-- | lib/lib.c | 42 | ||||
-rw-r--r-- | lib/lib.h | 8 | ||||
-rwxr-xr-x | tests/test_human_readable.test | 10 |
3 files changed, 41 insertions, 19 deletions
@@ -866,27 +866,37 @@ void names_to_pid(char **names, int (*callback)(pid_t pid, char *name)) closedir(dp); } -// display first few digits of number with power of two units, except we're -// actually just counting decimal digits and showing mil/bil/trillions. +// display first few digits of number with power of two units int human_readable(char *buf, unsigned long long num, int style) { - int end, len; - - len = sprintf(buf, "%lld", num)-1; - end = (len%3)+1; - len /= 3; + unsigned long long snap = 0; + int len, unit, divisor = (style&HR_SI) ? 1024 : 1000; + + // Divide rounding up until we have 3 or fewer digits. Since the part we + // print is decimal, the test is 999 even when we divide by 1024. + // We can't run out of units because 2<<64 is 18 exabytes. + // test 5675 is 5.5k not 5.6k. + for (unit = 0; num > 999; unit++) num = ((snap = num)+(divisor/2))/divisor; + len = sprintf(buf, "%llu", num); + if (unit && len == 1) { + // Redo rounding for 1.2M case, this works with HR_SI and not. + num = snap/divisor; + snap -= num*divisor; + snap = ((snap*100)+50)/divisor; + snap /= 10; + len = sprintf(buf, "%llu.%llu", num, snap); + } + if (style & HR_SPACE) buf[len++] = ' '; + if (unit) { + unit = " kMGTPE"[unit]; - if (len && end == 1) { - buf[2] = buf[1]; - buf[1] = '.'; - end = 3; + if (!(style&HR_SI)) unit = toupper(unit); + buf[len++] = unit; } - if (style & HR_SPACE) buf[end++] = ' '; - if (len) buf[end++] = " KMGTPE"[len]; - if (style & HR_B) buf[end++] = 'B'; - buf[end++] = 0; + if (style & HR_B) buf[len++] = 'B'; + buf[len] = 0; - return end; + return len; } // The qsort man page says you can use alphasort, the posix committee @@ -177,12 +177,14 @@ void replace_tempfile(int fdin, int fdout, char **tempname); void crc_init(unsigned int *crc_table, int little_endian); void base64_init(char *p); int yesno(char *prompt, int def); -#define HR_SPACE 1 -#define HR_B 2 -int human_readable(char *buf, unsigned long long num, int style); int qstrcmp(const void *a, const void *b); int xpoll(struct pollfd *fds, int nfds, int timeout); +#define HR_SPACE 1 +#define HR_B 2 +#define HR_SI 4 +int human_readable(char *buf, unsigned long long num, int style); + // interestingtimes.c int xgettty(void); int terminal_size(unsigned *xx, unsigned *yy); diff --git a/tests/test_human_readable.test b/tests/test_human_readable.test new file mode 100755 index 00000000..a66b4fda --- /dev/null +++ b/tests/test_human_readable.test @@ -0,0 +1,10 @@ +#!/bin/bash + +[ -f testing.sh ] && . testing.sh + +#testing "name" "command" "result" "infile" "stdin" + +testing "human_readable" "test_human_readable 123456789" "123M\n" "" "" +testing "human_readable -b" "test_human_readable -b 123456789" "123MB\n" "" "" +testing "human_readable -s" "test_human_readable -s 123456789" "123 M\n" "" "" +testing "human_readable -i" "test_human_readable -i 5675" "5.5k\n" "" "" |