aboutsummaryrefslogtreecommitdiff
path: root/libbb/human_readable.c
diff options
context:
space:
mode:
Diffstat (limited to 'libbb/human_readable.c')
-rw-r--r--libbb/human_readable.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/libbb/human_readable.c b/libbb/human_readable.c
new file mode 100644
index 0000000..61c8567
--- /dev/null
+++ b/libbb/human_readable.c
@@ -0,0 +1,97 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * June 30, 2001 Manuel Novoa III
+ *
+ * All-integer version (hey, not everyone has floating point) of
+ * make_human_readable_str, modified from similar code I had written
+ * for busybox several months ago.
+ *
+ * Notes:
+ * 1) I'm using an unsigned long long to hold the product size * block_size,
+ * as df (which calls this routine) could request a representation of a
+ * partition size in bytes > max of unsigned long. If long longs aren't
+ * available, it would be possible to do what's needed using polynomial
+ * representations (say, powers of 1024) and manipulating coefficients.
+ * The base ten "bytes" output could be handled similarly.
+ *
+ * 2) This routine always outputs a decimal point and a tenths digit when
+ * display_unit != 0. Hence, it isn't uncommon for the returned string
+ * to have a length of 5 or 6.
+ *
+ * It might be nice to add a flag to indicate no decimal digits in
+ * that case. This could be either an additional parameter, or a
+ * special value of display_unit. Such a flag would also be nice for du.
+ *
+ * Some code to omit the decimal point and tenths digit is sketched out
+ * and "#if 0"'d below.
+ */
+
+#include "libbb.h"
+
+const char* FAST_FUNC make_human_readable_str(unsigned long long size,
+ unsigned long block_size, unsigned long display_unit)
+{
+ /* The code will adjust for additional (appended) units */
+ static const char unit_chars[] ALIGN1 = {
+ '\0', 'K', 'M', 'G', 'T', 'P', 'E'
+ };
+ static const char fmt[] ALIGN1 = "%llu";
+ static const char fmt_tenths[] ALIGN1 = "%llu.%d%c";
+
+ static char str[21] ALIGN1; /* Sufficient for 64 bit unsigned integers */
+
+ unsigned long long val;
+ int frac;
+ const char *u;
+ const char *f;
+ smallint no_tenths;
+
+ if (size == 0)
+ return "0";
+
+ /* If block_size is 0 then do not print tenths */
+ no_tenths = 0;
+ if (block_size == 0) {
+ no_tenths = 1;
+ block_size = 1;
+ }
+
+ u = unit_chars;
+ val = size * block_size;
+ f = fmt;
+ frac = 0;
+
+ if (display_unit) {
+ val += display_unit/2; /* Deal with rounding */
+ val /= display_unit; /* Don't combine with the line above!!! */
+ /* will just print it as ulonglong (below) */
+ } else {
+ while ((val >= 1024)
+ && (u < unit_chars + sizeof(unit_chars) - 1)
+ ) {
+ f = fmt_tenths;
+ u++;
+ frac = (((int)(val % 1024)) * 10 + 1024/2) / 1024;
+ val /= 1024;
+ }
+ if (frac >= 10) { /* We need to round up here. */
+ ++val;
+ frac = 0;
+ }
+#if 1
+ /* Sample code to omit decimal point and tenths digit. */
+ if (no_tenths) {
+ if (frac >= 5) {
+ ++val;
+ }
+ f = "%llu%*c" /* fmt_no_tenths */;
+ frac = 1;
+ }
+#endif
+ }
+
+ /* If f==fmt then 'frac' and 'u' are ignored. */
+ snprintf(str, sizeof(str), f, val, frac, *u);
+
+ return str;
+}