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