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