(human_readable): Convert to ANSI-style definition.
[gnulib.git] / lib / human.c
1 /* human.c -- print human readable file size
2    Copyright (C) 1996, 1997 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 and large file support added by eggert@twinsun.com.  */
20
21 #include <config.h>
22
23 #if HAVE_INTTYPES_H
24 # include <inttypes.h>
25 #endif
26
27 #include <sys/types.h>
28 #include <stdio.h>
29
30 #if HAVE_LIMITS_H
31 # include <limits.h>
32 #endif
33
34 #ifndef CHAR_BIT
35 # define CHAR_BIT 8
36 #endif
37
38 #include "human.h"
39
40 static const char suffixes[] =
41 {
42   0,    /* not used */
43   'k',  /* kilo */
44   'M',  /* Mega */
45   'G',  /* Giga */
46   'T',  /* Tera */
47   'P',  /* Peta */
48   'E',  /* Exa */
49   'Z',  /* Zetta */
50   'Y'   /* Yotta */
51 };
52
53 /* Convert N to a human readable format in BUF.
54
55    N is expressed in units of FROM_UNITS; use units of TO_UNITS in the
56    output number.  FROM_UNITS and TO_UNITS must be positive, and one must
57    be a multiple of the other.
58
59    If BASE is nonzero, use a format like "127k" if possible,
60    using powers of BASE; otherwise, use ordinary decimal format.
61    Normally BASE is either 1000 or 1024; it must be at least 2.
62    Most people visually process strings of 3-4 digits effectively,
63    but longer strings of digits are more prone to misinterpretation.
64    Hence, converting to an abbreviated form usually improves readability.
65    Use a suffix indicating which power is being used.
66    For example, assuming BASE is 1024, 8500 would be converted to 8.3k,
67    133456345 to 127M, 56990456345 to 53G, and so on.  Numbers smaller
68    than BASE aren't modified.  */
69
70 char *
71 human_readable (uintmax_t n, char *buf, int from_units, int to_units, int base)
72 {
73   uintmax_t amt;
74   int tenths;
75   int power;
76   char *p;
77
78   /* 0 means adjusted N == AMT.TENTHS;
79      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
80      2 means adjusted N == AMT.TENTHS + 0.05;
81      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
82   int rounding;
83
84   p = buf + LONGEST_HUMAN_READABLE;
85   *p = '\0';
86
87 #ifdef lint
88   /* Suppress `used before initialized' warning.  */
89   power = 0;
90 #endif
91
92   /* Adjust AMT out of FROM_UNITS units and into TO_UNITS units.  */
93
94   if (to_units <= from_units)
95     {
96       int multiplier = from_units / to_units;
97       amt = n * multiplier;
98       tenths = rounding = 0;
99
100       if (amt / multiplier != n)
101         {
102           /* Overflow occurred during multiplication.  We should use
103              multiple precision arithmetic here, but we'll be lazy and
104              resort to floating point.  This can yield answers that
105              are slightly off.  In practice it is quite rare to
106              overflow uintmax_t, so this is good enough for now.  */
107
108           double damt = n * (double) multiplier;
109
110           if (! base)
111             sprintf (buf, "%.0f", damt);
112           else
113             {
114               double e = 1;
115               power = 0;
116
117               do
118                 {
119                   e *= base;
120                   power++;
121                 }
122               while (e * base <= amt && power < sizeof suffixes - 1);
123
124               damt /= e;
125
126               sprintf (buf, "%.1f%c", damt, suffixes[power]);
127               if (4 < strlen (buf))
128                 sprintf (buf, "%.0f%c", damt, suffixes[power]);
129             }
130
131           return buf;
132         }
133     }
134   else
135     {
136       int divisor = to_units / from_units;
137       int r10 = (n % divisor) * 10;
138       int r2 = (r10 % divisor) * 2;
139       amt = n / divisor;
140       tenths = r10 / divisor;
141       rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
142     }
143
144
145   /* Use power of BASE notation if adjusted AMT is large enough.  */
146
147   if (base && base <= amt)
148     {
149       power = 0;
150
151       do
152         {
153           int r10 = (amt % base) * 10 + tenths;
154           int r2 = (r10 % base) * 2 + (rounding >> 1);
155           amt /= base;
156           tenths = r10 / base;
157           rounding = (r2 < base
158                       ? 0 < r2 + rounding
159                       : 2 + (base < r2 + rounding));
160           power++;
161         }
162       while (base <= amt && power < sizeof suffixes - 1);
163
164       *--p = suffixes[power];
165
166       if (amt < 10)
167         {
168           tenths += 2 < rounding + (tenths & 1);
169
170           if (tenths == 10)
171             {
172               amt++;
173               tenths = 0;
174             }
175
176           if (amt < 10)
177             {
178               *--p = '0' + tenths;
179               *--p = '.';
180               tenths = 0;
181             }
182         }
183     }
184
185   if (5 < tenths + (2 < rounding + (amt & 1)))
186     {
187       amt++;
188
189       if (amt == base && power < sizeof suffixes - 1)
190         {
191           *p = suffixes[power + 1];
192           *--p = '0';
193           *--p = '.';
194           amt = 1;
195         }
196     }
197
198   do
199     *--p = '0' + (int) (amt % 10);
200   while ((amt /= 10) != 0);
201
202   return p;
203 }