* lib/human.c, lib/human.h (human_readable): Coalesce last two args
[gnulib.git] / lib / human.c
1 /* human.c -- print human readable file size
2    Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Originally contributed by lm@sgi.com;
19    --si, output block size selection, and large file support
20    added by eggert@twinsun.com.  */
21
22 #if HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25
26 #if HAVE_INTTYPES_H
27 # include <inttypes.h>
28 #endif
29
30 #include <sys/types.h>
31 #include <stdio.h>
32
33 #if HAVE_LIMITS_H
34 # include <limits.h>
35 #endif
36
37 #ifndef CHAR_BIT
38 # define CHAR_BIT 8
39 #endif
40 #if HAVE_STDLIB_H
41 # include <stdlib.h>
42 #endif
43
44 #ifndef HAVE_DECL_GETENV
45 char *getenv ();
46 #endif
47
48 #if ENABLE_NLS
49 # include <libintl.h>
50 # define _(Text) gettext (Text)
51 #else
52 # define _(Text) Text
53 #endif
54
55 #include <argmatch.h>
56 #include <error.h>
57 #include <xstrtoul.h>
58
59 #include "human.h"
60
61 static const char suffixes[] =
62 {
63   0,    /* not used */
64   'k',  /* kilo */
65   'M',  /* Mega */
66   'G',  /* Giga */
67   'T',  /* Tera */
68   'P',  /* Peta */
69   'E',  /* Exa */
70   'Z',  /* Zetta */
71   'Y'   /* Yotta */
72 };
73
74 /* Convert N to a human readable format in BUF.
75
76    N is expressed in units of FROM_BLOCK_SIZE.  FROM_BLOCK_SIZE must
77    be positive.
78
79    If OUTPUT_BLOCK_SIZE is positive, use units of OUTPUT_BLOCK_SIZE in
80    the output number.  OUTPUT_BLOCK_SIZE must be a multiple of
81    FROM_BLOCK_SIZE or vice versa.
82
83    If OUTPUT_BLOCK_SIZE is negative, use a format like "127k" if
84    possible, using powers of -OUTPUT_BLOCK_SIZE; otherwise, use
85    ordinary decimal format.  Normally -OUTPUT_BLOCK_SIZE is either
86    1000 or 1024; it must be at least 2.  Most people visually process
87    strings of 3-4 digits effectively, but longer strings of digits are
88    more prone to misinterpretation.  Hence, converting to an
89    abbreviated form usually improves readability.  Use a suffix
90    indicating which power is being used.  For example, assuming
91    -OUTPUT_BLOCK_SIZE is 1024, 8500 would be converted to 8.3k,
92    133456345 to 127M, 56990456345 to 53G, and so on.  Numbers smaller
93    than -OUTPUT_BLOCK_SIZE aren't modified.  */
94
95 char *
96 human_readable (uintmax_t n, char *buf,
97                 int from_block_size, int output_block_size)
98 {
99   uintmax_t amt;
100   int base;
101   int to_block_size;
102   int tenths;
103   int power;
104   char *p;
105
106   /* 0 means adjusted N == AMT.TENTHS;
107      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
108      2 means adjusted N == AMT.TENTHS + 0.05;
109      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
110   int rounding;
111
112   if (output_block_size < 0)
113     {
114       base = -output_block_size;
115       to_block_size = 1;
116     }
117   else
118     {
119       base = 0;
120       to_block_size = output_block_size;
121     }
122
123   p = buf + LONGEST_HUMAN_READABLE;
124   *p = '\0';
125
126 #ifdef lint
127   /* Suppress `used before initialized' warning.  */
128   power = 0;
129 #endif
130
131   /* Adjust AMT out of FROM_BLOCK_SIZE units and into TO_BLOCK_SIZE units.  */
132
133   if (to_block_size <= from_block_size)
134     {
135       int multiplier = from_block_size / to_block_size;
136       amt = n * multiplier;
137       tenths = rounding = 0;
138
139       if (amt / multiplier != n)
140         {
141           /* Overflow occurred during multiplication.  We should use
142              multiple precision arithmetic here, but we'll be lazy and
143              resort to floating point.  This can yield answers that
144              are slightly off.  In practice it is quite rare to
145              overflow uintmax_t, so this is good enough for now.  */
146
147           double damt = n * (double) multiplier;
148
149           if (! base)
150             sprintf (buf, "%.0f", damt);
151           else
152             {
153               double e = 1;
154               power = 0;
155
156               do
157                 {
158                   e *= base;
159                   power++;
160                 }
161               while (e * base <= amt && power < sizeof suffixes - 1);
162
163               damt /= e;
164
165               sprintf (buf, "%.1f%c", damt, suffixes[power]);
166               if (4 < strlen (buf))
167                 sprintf (buf, "%.0f%c", damt, suffixes[power]);
168             }
169
170           return buf;
171         }
172     }
173   else
174     {
175       int divisor = to_block_size / from_block_size;
176       int r10 = (n % divisor) * 10;
177       int r2 = (r10 % divisor) * 2;
178       amt = n / divisor;
179       tenths = r10 / divisor;
180       rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
181     }
182
183
184   /* Use power of BASE notation if adjusted AMT is large enough.  */
185
186   if (base && base <= amt)
187     {
188       power = 0;
189
190       do
191         {
192           int r10 = (amt % base) * 10 + tenths;
193           int r2 = (r10 % base) * 2 + (rounding >> 1);
194           amt /= base;
195           tenths = r10 / base;
196           rounding = (r2 < base
197                       ? 0 < r2 + rounding
198                       : 2 + (base < r2 + rounding));
199           power++;
200         }
201       while (base <= amt && power < sizeof suffixes - 1);
202
203       *--p = suffixes[power];
204
205       if (amt < 10)
206         {
207           tenths += 2 < rounding + (tenths & 1);
208
209           if (tenths == 10)
210             {
211               amt++;
212               tenths = 0;
213             }
214
215           if (amt < 10)
216             {
217               *--p = '0' + tenths;
218               *--p = '.';
219               tenths = 0;
220             }
221         }
222     }
223
224   if (5 < tenths + (2 < rounding + (amt & 1)))
225     {
226       amt++;
227
228       if (amt == base && power < sizeof suffixes - 1)
229         {
230           *p = suffixes[power + 1];
231           *--p = '0';
232           *--p = '.';
233           amt = 1;
234         }
235     }
236
237   do
238     *--p = '0' + (int) (amt % 10);
239   while ((amt /= 10) != 0);
240
241   return p;
242 }
243
244
245 /* The default block size used for output.  This number may change in
246    the future as disks get larger.  */
247 #ifndef DEFAULT_BLOCK_SIZE
248 # define DEFAULT_BLOCK_SIZE 1024
249 #endif
250
251 static char const *const block_size_args[] = { "human-readable", "si", 0 };
252 static int const block_size_types[] = { -1024, -1000 };
253
254 static strtol_error
255 humblock (char const *spec, int *block_size)
256 {
257   int i;
258
259   if (! spec && ! (spec = getenv ("BLOCK_SIZE")))
260     *block_size = getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
261   else if (0 <= (i = argmatch (spec, block_size_args)))
262     *block_size = block_size_types[i];
263   else
264     {
265       char *ptr;
266       unsigned long val;
267       strtol_error e = xstrtoul (spec, &ptr, 0, &val, "eEgGkKmMpPtTyYzZ0");
268       if (e != LONGINT_OK)
269         return e;
270       if (*ptr)
271         return LONGINT_INVALID_SUFFIX_CHAR;
272       if ((int) val < 0 || val != (int) val)
273         return LONGINT_OVERFLOW;
274       *block_size = (int) val;
275     }
276
277   return LONGINT_OK;
278 }
279
280 void
281 human_block_size (char const *spec, int report_errors, int *block_size)
282 {
283   strtol_error e = humblock (spec, block_size);
284   if (e != LONGINT_OK && report_errors)
285     STRTOL_FATAL_ERROR (spec, _("block size"), e);
286 }