diff options
-rw-r--r-- | libbb/human_readable.c | 117 |
1 files changed, 70 insertions, 47 deletions
diff --git a/libbb/human_readable.c b/libbb/human_readable.c index 2cb887563..548712c75 100644 --- a/libbb/human_readable.c +++ b/libbb/human_readable.c | |||
@@ -1,66 +1,89 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | 1 | /* |
3 | * make_human_readable_str | 2 | * June 30, 2001 Manuel Novoa III |
4 | * | 3 | * |
5 | * Copyright (C) 1999-2001 Erik Andersen <andersee@debian.org> | 4 | * All-integer version (hey, not everyone has floating point) of |
5 | * make_human_readable_str, modified from similar code I had written | ||
6 | * for busybox several months ago. | ||
6 | * | 7 | * |
7 | * This program is free software; you can redistribute it and/or modify | 8 | * Notes: |
8 | * it under the terms of the GNU General Public License as published by | 9 | * 1) I'm using an unsigned long long to hold the product size * block_size, |
9 | * the Free Software Foundation; either version 2 of the License, or | 10 | * as df (which calls this routine) could request a representation of a |
10 | * (at your option) any later version. | 11 | * partition size in bytes > max of unsigned long. If long longs aren't |
12 | * available, it would be possible to do what's needed using polynomial | ||
13 | * representations (say, powers of 1024) and manipulating coefficients. | ||
14 | * The base ten "bytes" output could be handled similarly. | ||
11 | * | 15 | * |
12 | * This program is distributed in the hope that it will be useful, | 16 | * 2) The output of "ls -sh" can be misaligned because this routine always |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | * outputs a decimal point and a tenths digit when display_unit != 0. |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 18 | * Hence, it isn't uncommon for the returned string to have a length |
15 | * General Public License for more details. | 19 | * of 5 or 6 instead of <= 4 (as assumed). |
16 | * | 20 | * |
17 | * You should have received a copy of the GNU General Public License | 21 | * It might be nice to add a flag to indicate no decimal digits in |
18 | * along with this program; if not, write to the Free Software | 22 | * that case. This could be either an additional parameter, or a |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 23 | * special value of display_unit. Such a flag would also be nice for du. |
24 | * | ||
25 | * Some code to omit the decimal point and tenths digit is sketched out | ||
26 | * and "#if 0"'d below. | ||
20 | */ | 27 | */ |
21 | 28 | ||
22 | #include <stdio.h> | 29 | #include <stdio.h> |
23 | #include "libbb.h" | 30 | #include "libbb.h" |
24 | 31 | ||
25 | |||
26 | |||
27 | const char *make_human_readable_str(unsigned long size, | 32 | const char *make_human_readable_str(unsigned long size, |
28 | unsigned long block_size, unsigned long display_unit) | 33 | unsigned long block_size, |
34 | unsigned long display_unit) | ||
29 | { | 35 | { |
30 | static char str[10] = "\0"; | 36 | /* The code will adjust for additional (appended) units. */ |
31 | static const char strings[] = { 0, 'k', 'M', 'G', 'T', 0 }; | 37 | static const char zero_and_units[] = { '0', 0, 'k', 'M', 'G', 'T' }; |
38 | static const char fmt[] = "%Lu"; | ||
39 | static const char fmt_tenths[] = "%Lu.%d%c"; | ||
32 | 40 | ||
33 | if(size == 0 || block_size == 0) | 41 | static char str[21]; /* Sufficient for 64 bit unsigned integers. */ |
34 | return("0"); | ||
35 | 42 | ||
36 | if(display_unit) { | 43 | unsigned long long val; |
37 | snprintf(str, 9, "%ld", (size/display_unit)*block_size); | 44 | int frac; |
45 | const char *u; | ||
46 | const char *f; | ||
47 | |||
48 | u = zero_and_units; | ||
49 | f = fmt; | ||
50 | frac = 0; | ||
51 | |||
52 | val = ((unsigned long long) size) * block_size; | ||
53 | if (val == 0) { | ||
54 | return u; | ||
55 | } | ||
56 | |||
57 | if (display_unit) { | ||
58 | val += display_unit/2; /* Deal with rounding. */ | ||
59 | val /= display_unit; /* Don't combine with the line above!!! */ | ||
38 | } else { | 60 | } else { |
39 | /* Ok, looks like they want us to autoscale */ | 61 | ++u; |
40 | int i=0; | 62 | while ((val >= KILOBYTE) |
41 | unsigned long divisor = 1; | 63 | && (u < zero_and_units + sizeof(zero_and_units) - 1)) { |
42 | long double result = size*block_size; | 64 | f = fmt_tenths; |
43 | for(i=0; i <= 4; i++) { | 65 | ++u; |
44 | divisor<<=10; | 66 | frac = ((((int)(val % KILOBYTE)) * 10) + (KILOBYTE/2)) / KILOBYTE; |
45 | if (result <= divisor) { | 67 | val /= KILOBYTE; |
46 | divisor>>=10; | 68 | } |
47 | break; | 69 | if (frac >= 10) { /* We need to round up here. */ |
70 | ++val; | ||
71 | frac = 0; | ||
72 | } | ||
73 | #if 0 | ||
74 | /* Sample code to omit decimal point and tenths digit. */ | ||
75 | if ( /* no_tenths */ 1 ) { | ||
76 | if ( frac >= 5 ) { | ||
77 | ++val; | ||
48 | } | 78 | } |
79 | f = "%Lu%*c" /* fmt_no_tenths */ ; | ||
80 | frac = 1; | ||
49 | } | 81 | } |
50 | result/=divisor; | 82 | #endif |
51 | if (result > 10) | ||
52 | snprintf(str, 9, "%.0Lf%c", result, strings[i]); | ||
53 | else | ||
54 | snprintf(str, 9, "%.1Lf%c", result, strings[i]); | ||
55 | } | 83 | } |
56 | return(str); | ||
57 | } | ||
58 | 84 | ||
59 | /* END CODE */ | 85 | /* If f==fmt then 'frac' and 'u' are ignored. */ |
60 | /* | 86 | snprintf(str, sizeof(str), f, val, frac, *u); |
61 | Local Variables: | 87 | |
62 | c-file-style: "linux" | 88 | return str; |
63 | c-basic-offset: 4 | 89 | } |
64 | tab-width: 4 | ||
65 | End: | ||
66 | */ | ||