New file. The interface is inspired
[pspp] / 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 (n, buf, from_units, to_units, base)
72      uintmax_t n;
73      char *buf;
74      int from_units;
75      int to_units;
76      int base;
77 {
78   uintmax_t amt;
79   int tenths;
80   int power;
81   char *p;
82
83   /* 0 means adjusted N == AMT.TENTHS;
84      1 means AMT.TENTHS < adjusted N < AMT.TENTHS + 0.05;
85      2 means adjusted N == AMT.TENTHS + 0.05;
86      3 means AMT.TENTHS + 0.05 < adjusted N < AMT.TENTHS + 0.1.  */
87   int rounding;
88
89   p = buf + LONGEST_HUMAN_READABLE;
90   *p = '\0';
91    
92
93   /* Adjust AMT out of FROM_UNITS units and into TO_UNITS units.  */
94    
95   if (to_units <= from_units)
96     {
97       int multiplier = from_units / to_units;
98       amt = n * multiplier;
99       tenths = rounding = 0;
100
101       if (amt / multiplier != n)
102         {
103           /* Overflow occurred during multiplication.  We should use
104              multiple precision arithmetic here, but we'll be lazy and
105              resort to floating point.  This can yield answers that
106              are slightly off.  In practice it is quite rare to
107              overflow uintmax_t, so this is good enough for now.  */
108
109           double damt = n * (double) multiplier;
110
111           if (! base)
112             sprintf (buf, "%.0f", damt);
113           else
114             {
115               double e = 1;
116               power = 0;
117
118               do
119                 {
120                   e *= base;
121                   power++;
122                 }
123               while (e * base <= amt && power < sizeof suffixes - 1);
124
125               damt /= e;
126
127               sprintf (buf, "%.1f%c", damt, suffixes[power]);
128               if (4 < strlen (buf))
129                 sprintf (buf, "%.0f%c", damt, suffixes[power]);
130             }
131
132           return buf;
133         }
134     }
135   else
136     {
137       int divisor = to_units / from_units;
138       int r10 = (n % divisor) * 10;
139       int r2 = (r10 % divisor) * 2;
140       amt = n / divisor;
141       tenths = r10 / divisor;
142       rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
143     }
144
145    
146   /* Use power of BASE notation if adjusted AMT is large enough.  */
147
148   if (base && base <= amt)
149     {
150       power = 0;
151
152       do
153         {
154           int r10 = (amt % base) * 10 + tenths;
155           int r2 = (r10 % base) * 2 + (rounding >> 1);
156           amt /= base;
157           tenths = r10 / base;
158           rounding = (r2 < base
159                       ? 0 < r2 + rounding
160                       : 2 + (base < r2 + rounding));
161           power++;
162         }
163       while (base <= amt && power < sizeof suffixes - 1);
164
165       *--p = suffixes[power];
166
167       if (amt < 10)
168         {
169           tenths += 2 < rounding + (tenths & 1);
170
171           if (tenths == 10)
172             {
173               amt++;
174               tenths = 0;
175             }
176
177           if (amt < 10)
178             {
179               *--p = '0' + tenths;
180               *--p = '.';
181               tenths = 0;
182             }
183         }
184     }
185    
186   if (5 < tenths + (2 < rounding + (amt & 1)))
187     {
188       amt++;
189
190       if (amt == base && power < sizeof suffixes - 1)
191         {
192           *p = suffixes[power + 1];
193           *--p = '0';
194           *--p = '.';
195           amt = 1;
196         }
197     }
198
199   do
200     *--p = '0' + (int) (amt % 10);
201   while ((amt /= 10) != 0);
202
203   return p;
204 }