.
[gnulib.git] / lib / human.c
index 3757723..a9ccf38 100644 (file)
@@ -1,5 +1,7 @@
 /* human.c -- print human readable file size
 /* human.c -- print human readable file size
-   Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
+
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 Free Software
+   Foundation, Inc.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 # include <config.h>
 #endif
 
 # include <config.h>
 #endif
 
-#if HAVE_INTTYPES_H
-# include <inttypes.h>
-#endif
-
 #include <sys/types.h>
 #include <stdio.h>
 
 #include <sys/types.h>
 #include <stdio.h>
 
 # include <limits.h>
 #endif
 
 # include <limits.h>
 #endif
 
+#if HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
 #ifndef CHAR_BIT
 # define CHAR_BIT 8
 #endif
 #ifndef CHAR_BIT
 # define CHAR_BIT 8
 #endif
@@ -42,6 +46,9 @@
 #endif
 
 #ifndef HAVE_DECL_GETENV
 #endif
 
 #ifndef HAVE_DECL_GETENV
+"this configure-time declaration test was not run"
+#endif
+#if !HAVE_DECL_GETENV
 char *getenv ();
 #endif
 
 char *getenv ();
 #endif
 
@@ -54,33 +61,79 @@ char *getenv ();
 
 #include <argmatch.h>
 #include <error.h>
 
 #include <argmatch.h>
 #include <error.h>
-#include <xstrtoul.h>
+#include <xstrtol.h>
 
 #include "human.h"
 
 static const char suffixes[] =
 {
   0,   /* not used */
 
 #include "human.h"
 
 static const char suffixes[] =
 {
   0,   /* not used */
-  'k', /* kilo */
-  'M', /* Mega */
-  'G', /* Giga */
-  'T', /* Tera */
-  'P', /* Peta */
-  'E', /* Exa */
-  'Z', /* Zetta */
-  'Y'  /* Yotta */
+  'K', /* kibi ('k' for kilo is a special case) */
+  'M', /* mega or mebi */
+  'G', /* giga or gibi */
+  'T', /* tera or tebi */
+  'P', /* peta or pebi */
+  'E', /* exa or exbi */
+  'Z', /* zetta or 2**70 */
+  'Y'  /* yotta or 2**80 */
 };
 
 };
 
+/* Generate into P[-1] (and possibly P[-2]) the proper suffix for
+   POWER and BASE.  Return the address of the generated suffix.  */
+static char *
+generate_suffix_backwards (char *p, int power, int base)
+{
+  char letter = suffixes[power];
+
+  if (base == 1000)
+    {
+      *--p = 'B';
+      if (power == 1)
+       letter = 'k';
+    }
+
+  *--p = letter;
+  return p;
+}
+
+/* If INEXACT_STYLE is not human_round_to_even, and if easily
+   possible, adjust VALUE according to the style.  */
+static double
+adjust_value (enum human_inexact_style inexact_style, double value)
+{
+  /* Do not use the floor or ceil functions, as that would mean
+     linking with the standard math library, which is a porting pain.
+     So leave the value alone if it is too large to easily round.  */
+  if (inexact_style != human_round_to_even && value < (uintmax_t) -1)
+    {
+      uintmax_t u = value;
+      value = u + (inexact_style == human_ceiling && u != value);
+    }
+
+  return value;
+}
+
+/* Like human_readable_inexact, except always round to even.  */
+char *
+human_readable (uintmax_t n, char *buf,
+               int from_block_size, int output_block_size)
+{
+  return human_readable_inexact (n, buf, from_block_size, output_block_size,
+                                human_round_to_even);
+}
+
 /* Convert N to a human readable format in BUF.
 
    N is expressed in units of FROM_BLOCK_SIZE.  FROM_BLOCK_SIZE must
 /* Convert N to a human readable format in BUF.
 
    N is expressed in units of FROM_BLOCK_SIZE.  FROM_BLOCK_SIZE must
-   be positive.
+   be nonnegative.
+
+   OUTPUT_BLOCK_SIZE must be nonzero.  If it is positive, use units of
+   OUTPUT_BLOCK_SIZE in the output number.
 
 
-   If OUTPUT_BLOCK_SIZE is positive, use units of OUTPUT_BLOCK_SIZE in
-   the output number.  OUTPUT_BLOCK_SIZE must be a multiple of
-   FROM_BLOCK_SIZE or vice versa.
+   Use INEXACT_STYLE to determine whether to take the ceiling or floor
+   of any result that cannot be expressed exactly.
 
 
-   If OUTPUT_BLOCK_SIZE is negative, use a format like "127k" if
+   If OUTPUT_BLOCK_SIZE is negative, use a format like "127K" if
    possible, using powers of -OUTPUT_BLOCK_SIZE; otherwise, use
    ordinary decimal format.  Normally -OUTPUT_BLOCK_SIZE is either
    1000 or 1024; it must be at least 2.  Most people visually process
    possible, using powers of -OUTPUT_BLOCK_SIZE; otherwise, use
    ordinary decimal format.  Normally -OUTPUT_BLOCK_SIZE is either
    1000 or 1024; it must be at least 2.  Most people visually process
@@ -88,18 +141,20 @@ static const char suffixes[] =
    more prone to misinterpretation.  Hence, converting to an
    abbreviated form usually improves readability.  Use a suffix
    indicating which power is being used.  For example, assuming
    more prone to misinterpretation.  Hence, converting to an
    abbreviated form usually improves readability.  Use a suffix
    indicating which power is being used.  For example, assuming
-   -OUTPUT_BLOCK_SIZE is 1024, 8500 would be converted to 8.3k,
+   -OUTPUT_BLOCK_SIZE is 1024, 8500 would be converted to 8.3K,
    133456345 to 127M, 56990456345 to 53G, and so on.  Numbers smaller
    133456345 to 127M, 56990456345 to 53G, and so on.  Numbers smaller
-   than -OUTPUT_BLOCK_SIZE aren't modified.  */
+   than -OUTPUT_BLOCK_SIZE aren't modified.  If -OUTPUT_BLOCK_SIZE is
+   1024, append a "B" after any size letter.  */
 
 char *
 
 char *
-human_readable (uintmax_t n, char *buf,
-               int from_block_size, int output_block_size)
+human_readable_inexact (uintmax_t n, char *buf,
+                       int from_block_size, int output_block_size,
+                       enum human_inexact_style inexact_style)
 {
   uintmax_t amt;
   int base;
   int to_block_size;
 {
   uintmax_t amt;
   int base;
   int to_block_size;
-  int tenths;
+  int tenths = 0;
   int power;
   char *p;
 
   int power;
   char *p;
 
@@ -107,7 +162,7 @@ human_readable (uintmax_t n, char *buf,
      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
      2 means adjusted N == AMT.TENTHS + 0.05;
      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
      2 means adjusted N == AMT.TENTHS + 0.05;
      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
-  int rounding;
+  int rounding = 0;
 
   if (output_block_size < 0)
     {
 
   if (output_block_size < 0)
     {
@@ -130,56 +185,61 @@ human_readable (uintmax_t n, char *buf,
 
   /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units.  */
 
 
   /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units.  */
 
-  if (to_block_size <= from_block_size)
-    {
-      int multiplier = from_block_size / to_block_size;
-      amt = n * multiplier;
-      tenths = rounding = 0;
-
-      if (amt / multiplier != n)
-       {
-         /* Overflow occurred during multiplication.  We should use
-            multiple precision arithmetic here, but we'll be lazy and
-            resort to floating point.  This can yield answers that
-            are slightly off.  In practice it is quite rare to
-            overflow uintmax_t, so this is good enough for now.  */
-
-         double damt = n * (double) multiplier;
-
-         if (! base)
-           sprintf (buf, "%.0f", damt);
-         else
-           {
-             double e = 1;
-             power = 0;
-
-             do
-               {
-                 e *= base;
-                 power++;
-               }
-             while (e * base <= amt && power < sizeof suffixes - 1);
-
-             damt /= e;
-
-             sprintf (buf, "%.1f%c", damt, suffixes[power]);
-             if (4 < strlen (buf))
-               sprintf (buf, "%.0f%c", damt, suffixes[power]);
-           }
-
-         return buf;
-       }
-    }
-  else
-    {
-      int divisor = to_block_size / from_block_size;
-      int r10 = (n % divisor) * 10;
-      int r2 = (r10 % divisor) * 2;
-      amt = n / divisor;
-      tenths = r10 / divisor;
-      rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
-    }
-
+  {
+    int multiplier;
+    int divisor;
+    int r2;
+    int r10;
+    if (to_block_size <= from_block_size
+       ? (from_block_size % to_block_size != 0
+          || (multiplier = from_block_size / to_block_size,
+              (amt = n * multiplier) / multiplier != n))
+       : (from_block_size == 0
+          || to_block_size % from_block_size != 0
+          || (divisor = to_block_size / from_block_size,
+              r10 = (n % divisor) * 10,
+              r2 = (r10 % divisor) * 2,
+              amt = n / divisor,
+              tenths = r10 / divisor,
+              rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2),
+              0)))
+      {
+       /* Either the result cannot be computed easily using uintmax_t,
+          or from_block_size is zero.  Fall back on floating point.
+          FIXME: This can yield answers that are slightly off.  */
+
+       double damt = n * (from_block_size / (double) to_block_size);
+
+       if (! base)
+         sprintf (buf, "%.0f", adjust_value (inexact_style, damt));
+       else
+         {
+           char suffix[3];
+           char const *psuffix;
+           double e = 1;
+           power = 0;
+
+           do
+             {
+               e *= base;
+               power++;
+             }
+           while (e * base <= damt && power < sizeof suffixes - 1);
+
+           damt /= e;
+
+           suffix[2] = '\0';
+           psuffix = generate_suffix_backwards (suffix + 2, power, base);
+           sprintf (buf, "%.1f%s",
+                    adjust_value (inexact_style, damt), psuffix);
+           if (4 + (base == 1000) < strlen (buf))
+             sprintf (buf, "%.0f%s",
+                      adjust_value (inexact_style, damt * 10) / 10, psuffix);
+         }
+
+       return buf;
+      }
+  }
 
   /* Use power of BASE notation if adjusted AMT is large enough.  */
 
 
   /* Use power of BASE notation if adjusted AMT is large enough.  */
 
@@ -200,28 +260,37 @@ human_readable (uintmax_t n, char *buf,
        }
       while (base <= amt && power < sizeof suffixes - 1);
 
        }
       while (base <= amt && power < sizeof suffixes - 1);
 
-      *--p = suffixes[power];
+      p = generate_suffix_backwards (p, power, base);
 
       if (amt < 10)
        {
 
       if (amt < 10)
        {
-         tenths += 2 < rounding + (tenths & 1);
-
-         if (tenths == 10)
+         if (2 * (1 - (int) inexact_style)
+             < rounding + (tenths & (inexact_style == human_round_to_even)))
            {
            {
-             amt++;
-             tenths = 0;
+             tenths++;
+             rounding = 0;
+
+             if (tenths == 10)
+               {
+                 amt++;
+                 tenths = 0;
+               }
            }
 
          if (amt < 10)
            {
              *--p = '0' + tenths;
              *--p = '.';
            }
 
          if (amt < 10)
            {
              *--p = '0' + tenths;
              *--p = '.';
-             tenths = 0;
+             tenths = rounding = 0;
            }
        }
     }
 
            }
        }
     }
 
-  if (5 < tenths + (2 < rounding + (amt & 1)))
+  if (inexact_style == human_ceiling
+      ? 0 < tenths + rounding
+      : inexact_style == human_round_to_even
+      ? 5 < tenths + (2 < rounding + (amt & 1))
+      : /* inexact_style == human_floor */ 0)
     {
       amt++;
 
     {
       amt++;
 
@@ -251,14 +320,20 @@ human_readable (uintmax_t n, char *buf,
 static char const *const block_size_args[] = { "human-readable", "si", 0 };
 static int const block_size_types[] = { -1024, -1000 };
 
 static char const *const block_size_args[] = { "human-readable", "si", 0 };
 static int const block_size_types[] = { -1024, -1000 };
 
+static int
+default_block_size (void)
+{
+  return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
+}
+
 static strtol_error
 humblock (char const *spec, int *block_size)
 {
   int i;
 
   if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
 static strtol_error
 humblock (char const *spec, int *block_size)
 {
   int i;
 
   if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
-    *block_size = getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
-  else if (0 <= (i = argmatch (spec, block_size_args)))
+    *block_size = default_block_size ();
+  else if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_types)))
     *block_size = block_size_types[i];
   else
     {
     *block_size = block_size_types[i];
   else
     {
@@ -281,6 +356,11 @@ void
 human_block_size (char const *spec, int report_errors, int *block_size)
 {
   strtol_error e = humblock (spec, block_size);
 human_block_size (char const *spec, int report_errors, int *block_size)
 {
   strtol_error e = humblock (spec, block_size);
+  if (*block_size == 0)
+    {
+      *block_size = default_block_size ();
+      e = LONGINT_INVALID;
+    }
   if (e != LONGINT_OK && report_errors)
     STRTOL_FATAL_ERROR (spec, _("block size"), e);
 }
   if (e != LONGINT_OK && report_errors)
     STRTOL_FATAL_ERROR (spec, _("block size"), e);
 }