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 | */ | ||
