LCOV - code coverage report
Current view: top level - lib - human.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 166 183 90.7 %
Date: 2018-01-30 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* human.c -- print human readable file size
       2             : 
       3             :    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
       4             :    2005, 2006, 2007 Free Software Foundation, Inc.
       5             : 
       6             :    This program is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      18             : 
      19             : /* Written by Paul Eggert and Larry McVoy.  */
      20             : 
      21             : #include <config.h>
      22             : 
      23             : #include "human.h"
      24             : 
      25             : #include <locale.h>
      26             : #include <stdio.h>
      27             : #include <stdlib.h>
      28             : #include <string.h>
      29             : 
      30             : #include <argmatch.h>
      31             : #include <error.h>
      32             : #include <intprops.h>
      33             : 
      34             : /* The maximum length of a suffix like "KiB".  */
      35             : #define HUMAN_READABLE_SUFFIX_LENGTH_MAX 3
      36             : 
      37             : static const char power_letter[] =
      38             : {
      39             :   0,    /* not used */
      40             :   'K',  /* kibi ('k' for kilo is a special case) */
      41             :   'M',  /* mega or mebi */
      42             :   'G',  /* giga or gibi */
      43             :   'T',  /* tera or tebi */
      44             :   'P',  /* peta or pebi */
      45             :   'E',  /* exa or exbi */
      46             :   'Z',  /* zetta or 2**70 */
      47             :   'Y'   /* yotta or 2**80 */
      48             : };
      49             : 
      50             : 
      51             : /* If INEXACT_STYLE is not human_round_to_nearest, and if easily
      52             :    possible, adjust VALUE according to the style.  */
      53             : 
      54             : static long double
      55         200 : adjust_value (int inexact_style, long double value)
      56             : {
      57             :   /* Do not use the floorl or ceill functions, as that would mean
      58             :      checking for their presence and possibly linking with the
      59             :      standard math library, which is a porting pain.  So leave the
      60             :      value alone if it is too large to easily round.  */
      61         200 :   if (inexact_style != human_round_to_nearest && value < UINTMAX_MAX)
      62             :     {
      63         180 :       uintmax_t u = value;
      64         180 :       value = u + (inexact_style == human_ceiling && u != value);
      65             :     }
      66             : 
      67         200 :   return value;
      68             : }
      69             : 
      70             : /* Group the digits of NUMBER according to the grouping rules of the
      71             :    current locale.  NUMBER contains NUMBERLEN digits.  Modify the
      72             :    bytes pointed to by NUMBER in place, subtracting 1 from NUMBER for
      73             :    each byte inserted.  Return the starting address of the modified
      74             :    number.
      75             : 
      76             :    To group the digits, use GROUPING and THOUSANDS_SEP as in `struct
      77             :    lconv' from <locale.h>.  */
      78             : 
      79             : static char *
      80          75 : group_number (char *number, size_t numberlen,
      81             :               char const *grouping, char const *thousands_sep)
      82             : {
      83             :   register char *d;
      84          75 :   size_t grouplen = SIZE_MAX;
      85          75 :   size_t thousands_seplen = strlen (thousands_sep);
      86          75 :   size_t i = numberlen;
      87             : 
      88             :   /* The maximum possible value for NUMBERLEN is the number of digits
      89             :      in the square of the largest uintmax_t, so double the size needed.  */
      90             :   char buf[2 * INT_STRLEN_BOUND (uintmax_t) + 1];
      91             : 
      92          75 :   memcpy (buf, number, numberlen);
      93          75 :   d = number + numberlen;
      94             : 
      95             :   for (;;)
      96           0 :     {
      97          75 :       unsigned char g = *grouping;
      98             : 
      99          75 :       if (g)
     100             :         {
     101           0 :           grouplen = g < CHAR_MAX ? g : i;
     102           0 :           grouping++;
     103             :         }
     104             : 
     105          75 :       if (i < grouplen)
     106          75 :         grouplen = i;
     107             : 
     108          75 :       d -= grouplen;
     109          75 :       i -= grouplen;
     110          75 :       memcpy (d, buf + i, grouplen);
     111          75 :       if (i == 0)
     112         150 :         return d;
     113             : 
     114           0 :       d -= thousands_seplen;
     115           0 :       memcpy (d, thousands_sep, thousands_seplen);
     116             :     }
     117             : }
     118             : 
     119             : /* Convert N to a human readable format in BUF, using the options OPTS.
     120             : 
     121             :    N is expressed in units of FROM_BLOCK_SIZE.  FROM_BLOCK_SIZE must
     122             :    be nonnegative.
     123             : 
     124             :    Use units of TO_BLOCK_SIZE in the output number.  TO_BLOCK_SIZE
     125             :    must be positive.
     126             : 
     127             :    Use (OPTS & (human_round_to_nearest | human_floor | human_ceiling))
     128             :    to determine whether to take the ceiling or floor of any result
     129             :    that cannot be expressed exactly.
     130             : 
     131             :    If (OPTS & human_group_digits), group the thousands digits
     132             :    according to the locale, e.g., `1,000,000' in an American English
     133             :    locale.
     134             : 
     135             :    If (OPTS & human_autoscale), deduce the output block size
     136             :    automatically; TO_BLOCK_SIZE must be 1 but it has no effect on the
     137             :    output.  Use powers of 1024 if (OPTS & human_base_1024), and powers
     138             :    of 1000 otherwise.  For example, assuming powers of 1024, 8500
     139             :    would be converted to 8.3, 133456345 to 127, 56990456345 to 53, and
     140             :    so on.  Numbers smaller than the power aren't modified.
     141             :    human_autoscale is normally used together with human_SI.
     142             : 
     143             :    If (OPTS & human_space_before_unit), use a space to separate the
     144             :    number from any suffix that is appended as described below.
     145             : 
     146             :    If (OPTS & human_SI), append an SI prefix indicating which power is
     147             :    being used.  If in addition (OPTS & human_B), append "B" (if base
     148             :    1000) or "iB" (if base 1024) to the SI prefix.  When ((OPTS &
     149             :    human_SI) && ! (OPTS & human_autoscale)), TO_BLOCK_SIZE must be a
     150             :    power of 1024 or of 1000, depending on (OPTS &
     151             :    human_base_1024).  */
     152             : 
     153             : char *
     154       42469 : human_readable (uintmax_t n, char *buf, int opts,
     155             :                 uintmax_t from_block_size, uintmax_t to_block_size)
     156             : {
     157       42469 :   int inexact_style =
     158             :     opts & (human_round_to_nearest | human_floor | human_ceiling);
     159       42469 :   unsigned int base = opts & human_base_1024 ? 1024 : 1000;
     160             :   uintmax_t amt;
     161             :   int tenths;
     162       42469 :   int exponent = -1;
     163       42469 :   int exponent_max = sizeof power_letter - 1;
     164             :   char *p;
     165             :   char *psuffix;
     166             :   char const *integerlim;
     167             : 
     168             :   /* 0 means adjusted N == AMT.TENTHS;
     169             :      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
     170             :      2 means adjusted N == AMT.TENTHS + 0.05;
     171             :      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
     172             :   int rounding;
     173             : 
     174       42469 :   char const *decimal_point = ".";
     175       42469 :   size_t decimal_pointlen = 1;
     176       42469 :   char const *grouping = "";
     177       42469 :   char const *thousands_sep = "";
     178       42469 :   struct lconv const *l = localeconv ();
     179       42469 :   size_t pointlen = strlen (l->decimal_point);
     180       42469 :   if (0 < pointlen && pointlen <= MB_LEN_MAX)
     181             :     {
     182       42469 :       decimal_point = l->decimal_point;
     183       42469 :       decimal_pointlen = pointlen;
     184             :     }
     185       42469 :   grouping = l->grouping;
     186       42469 :   if (strlen (l->thousands_sep) <= MB_LEN_MAX)
     187       42469 :     thousands_sep = l->thousands_sep;
     188             : 
     189       42469 :   psuffix = buf + LONGEST_HUMAN_READABLE - HUMAN_READABLE_SUFFIX_LENGTH_MAX;
     190       42469 :   p = psuffix;
     191             : 
     192             :   /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE
     193             :      units.  If this can be done exactly with integer arithmetic, do
     194             :      not use floating point operations.  */
     195       42469 :   if (to_block_size <= from_block_size)
     196             :     {
     197        1058 :       if (from_block_size % to_block_size == 0)
     198             :         {
     199         903 :           uintmax_t multiplier = from_block_size / to_block_size;
     200         903 :           amt = n * multiplier;
     201         903 :           if (amt / multiplier == n)
     202             :             {
     203         903 :               tenths = 0;
     204         903 :               rounding = 0;
     205         903 :               goto use_integer_arithmetic;
     206             :             }
     207             :         }
     208             :     }
     209       41411 :   else if (from_block_size != 0 && to_block_size % from_block_size == 0)
     210             :     {
     211       41375 :       uintmax_t divisor = to_block_size / from_block_size;
     212       41375 :       uintmax_t r10 = (n % divisor) * 10;
     213       41375 :       uintmax_t r2 = (r10 % divisor) * 2;
     214       41375 :       amt = n / divisor;
     215       41375 :       tenths = r10 / divisor;
     216       41375 :       rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
     217       41375 :       goto use_integer_arithmetic;
     218             :     }
     219             : 
     220             :   {
     221             :     /* Either the result cannot be computed easily using uintmax_t,
     222             :        or from_block_size is zero.  Fall back on floating point.
     223             :        FIXME: This can yield answers that are slightly off.  */
     224             : 
     225         191 :     long double dto_block_size = to_block_size;
     226         191 :     long double damt = n * (from_block_size / dto_block_size);
     227             :     size_t buflen;
     228             :     size_t nonintegerlen;
     229             : 
     230         191 :     if (! (opts & human_autoscale))
     231             :       {
     232         180 :         sprintf (buf, "%.0Lf", adjust_value (inexact_style, damt));
     233         180 :         buflen = strlen (buf);
     234         180 :         nonintegerlen = 0;
     235             :       }
     236             :     else
     237             :       {
     238          11 :         long double e = 1;
     239          11 :         exponent = 0;
     240             : 
     241             :         do
     242             :           {
     243          11 :             e *= base;
     244          11 :             exponent++;
     245             :           }
     246          11 :         while (e * base <= damt && exponent < exponent_max);
     247             : 
     248          11 :         damt /= e;
     249             : 
     250          11 :         sprintf (buf, "%.1Lf", adjust_value (inexact_style, damt));
     251          11 :         buflen = strlen (buf);
     252          11 :         nonintegerlen = decimal_pointlen + 1;
     253             : 
     254          11 :         if (1 + nonintegerlen + ! (opts & human_base_1024) < buflen
     255           2 :             || ((opts & human_suppress_point_zero)
     256           0 :                 && buf[buflen - 1] == '0'))
     257             :           {
     258           9 :             sprintf (buf, "%.0Lf",
     259           9 :                      adjust_value (inexact_style, damt * 10) / 10);
     260           9 :             buflen = strlen (buf);
     261           9 :             nonintegerlen = 0;
     262             :           }
     263             :       }
     264             : 
     265         191 :     p = psuffix - buflen;
     266         191 :     memmove (p, buf, buflen);
     267         191 :     integerlim = p + buflen - nonintegerlen;
     268             :   }
     269         191 :   goto do_grouping;
     270             : 
     271       42278 :  use_integer_arithmetic:
     272             :   {
     273             :     /* The computation can be done exactly, with integer arithmetic.
     274             : 
     275             :        Use power of BASE notation if requested and if adjusted AMT is
     276             :        large enough.  */
     277             : 
     278       42278 :     if (opts & human_autoscale)
     279             :       {
     280         238 :         exponent = 0;
     281             : 
     282         238 :         if (base <= amt)
     283             :           {
     284             :             do
     285             :               {
     286         478 :                 unsigned int r10 = (amt % base) * 10 + tenths;
     287         478 :                 unsigned int r2 = (r10 % base) * 2 + (rounding >> 1);
     288         478 :                 amt /= base;
     289         478 :                 tenths = r10 / base;
     290         478 :                 rounding = (r2 < base
     291         299 :                             ? (r2 + rounding) != 0
     292         777 :                             : 2 + (base < r2 + rounding));
     293         478 :                 exponent++;
     294             :               }
     295         478 :             while (base <= amt && exponent < exponent_max);
     296             : 
     297         183 :             if (amt < 10)
     298             :               {
     299          96 :                 if (inexact_style == human_round_to_nearest
     300           0 :                     ? 2 < rounding + (tenths & 1)
     301          48 :                     : inexact_style == human_ceiling && 0 < rounding)
     302             :                   {
     303          23 :                     tenths++;
     304          23 :                     rounding = 0;
     305             : 
     306          23 :                     if (tenths == 10)
     307             :                       {
     308           0 :                         amt++;
     309           0 :                         tenths = 0;
     310             :                       }
     311             :                   }
     312             : 
     313          48 :                 if (amt < 10
     314          48 :                     && (tenths || ! (opts & human_suppress_point_zero)))
     315             :                   {
     316          24 :                     *--p = '0' + tenths;
     317          24 :                     p -= decimal_pointlen;
     318          24 :                     memcpy (p, decimal_point, decimal_pointlen);
     319          24 :                     tenths = rounding = 0;
     320             :                   }
     321             :               }
     322             :           }
     323             :       }
     324             : 
     325       84556 :     if (inexact_style == human_round_to_nearest
     326          11 :         ? 5 < tenths + (0 < rounding + (amt & 1))
     327       42267 :         : inexact_style == human_ceiling && 0 < tenths + rounding)
     328             :       {
     329         252 :         amt++;
     330             : 
     331         252 :         if ((opts & human_autoscale)
     332         129 :             && amt == base && exponent < exponent_max)
     333             :           {
     334           0 :             exponent++;
     335           0 :             if (! (opts & human_suppress_point_zero))
     336             :               {
     337           0 :                 *--p = '0';
     338           0 :                 p -= decimal_pointlen;
     339           0 :                 memcpy (p, decimal_point, decimal_pointlen);
     340             :               }
     341           0 :             amt = 1;
     342             :           }
     343             :       }
     344             : 
     345       42278 :     integerlim = p;
     346             : 
     347             :     do
     348             :       {
     349       87180 :         int digit = amt % 10;
     350       87180 :         *--p = digit + '0';
     351             :       }
     352       87180 :     while ((amt /= 10) != 0);
     353             :   }
     354             : 
     355       42278 :  do_grouping:
     356       42469 :   if (opts & human_group_digits)
     357          75 :     p = group_number (p, integerlim - p, grouping, thousands_sep);
     358             : 
     359       42469 :   if (opts & human_SI)
     360             :     {
     361         360 :       if (exponent < 0)
     362             :         {
     363             :           uintmax_t power;
     364         111 :           exponent = 0;
     365         597 :           for (power = 1; power < to_block_size; power *= base)
     366         486 :             if (++exponent == exponent_max)
     367           0 :               break;
     368             :         }
     369             : 
     370         360 :       if ((exponent | (opts & human_B)) && (opts & human_space_before_unit))
     371          22 :         *psuffix++ = ' ';
     372             : 
     373         360 :       if (exponent)
     374         305 :         *psuffix++ = (! (opts & human_base_1024) && exponent == 1
     375             :                       ? 'k'
     376             :                       : power_letter[exponent]);
     377             : 
     378         360 :       if (opts & human_B)
     379             :         {
     380         104 :           if ((opts & human_base_1024) && exponent)
     381           0 :             *psuffix++ = 'i';
     382         104 :           *psuffix++ = 'B';
     383             :         }
     384             :     }
     385             : 
     386       42469 :   *psuffix = '\0';
     387             : 
     388       42469 :   return p;
     389             : }
     390             : 
     391             : 
     392             : /* The default block size used for output.  This number may change in
     393             :    the future as disks get larger.  */
     394             : #ifndef DEFAULT_BLOCK_SIZE
     395             : # define DEFAULT_BLOCK_SIZE 1024
     396             : #endif
     397             : 
     398             : static char const *const block_size_args[] = { "human-readable", "si", 0 };
     399             : static int const block_size_opts[] =
     400             :   {
     401             :     human_autoscale + human_SI + human_base_1024,
     402             :     human_autoscale + human_SI
     403             :   };
     404             : 
     405             : static uintmax_t
     406         113 : default_block_size (void)
     407             : {
     408         113 :   return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
     409             : }
     410             : 
     411             : static strtol_error
     412         135 : humblock (char const *spec, uintmax_t *block_size, int *options)
     413             : {
     414             :   int i;
     415         135 :   int opts = 0;
     416             : 
     417         135 :   if (! spec
     418          90 :       && ! (spec = getenv ("BLOCK_SIZE"))
     419          90 :       && ! (spec = getenv ("BLOCKSIZE")))
     420          90 :     *block_size = default_block_size ();
     421             :   else
     422             :     {
     423          45 :       if (*spec == '\'')
     424             :         {
     425           4 :           opts |= human_group_digits;
     426           4 :           spec++;
     427             :         }
     428             : 
     429          45 :       if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_opts)))
     430             :         {
     431           6 :           opts |= block_size_opts[i];
     432           6 :           *block_size = 1;
     433             :         }
     434             :       else
     435             :         {
     436             :           char *ptr;
     437          39 :           strtol_error e = xstrtoumax (spec, &ptr, 0, block_size,
     438             :                                        "eEgGkKmMpPtTyYzZ0");
     439          39 :           if (e != LONGINT_OK)
     440             :             {
     441          22 :               *options = 0;
     442          22 :               return e;
     443             :             }
     444          26 :           for (; ! ('0' <= *spec && *spec <= '9'); spec++)
     445          13 :             if (spec == ptr)
     446             :               {
     447           4 :                 opts |= human_SI;
     448           4 :                 if (ptr[-1] == 'B')
     449           3 :                   opts |= human_B;
     450           4 :                 if (ptr[-1] != 'B' || ptr[-2] == 'i')
     451           1 :                   opts |= human_base_1024;
     452           4 :                 break;
     453             :               }
     454             :         }
     455             :     }
     456             : 
     457         113 :   *options = opts;
     458         113 :   return LONGINT_OK;
     459             : }
     460             : 
     461             : enum strtol_error
     462         135 : human_options (char const *spec, int *opts, uintmax_t *block_size)
     463             : {
     464         135 :   strtol_error e = humblock (spec, block_size, opts);
     465         135 :   if (*block_size == 0)
     466             :     {
     467          23 :       *block_size = default_block_size ();
     468          23 :       e = LONGINT_INVALID;
     469             :     }
     470         135 :   return e;
     471             : }

Generated by: LCOV version 1.10