aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libbb/human_readable.c117
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
27const char *make_human_readable_str(unsigned long size, 32const 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);
61Local Variables: 87
62c-file-style: "linux" 88 return str;
63c-basic-offset: 4 89}
64tab-width: 4
65End:
66*/