This source file includes following definitions.
- adjust_value
- group_number
- human_readable
- default_block_size
- humblock
- human_options
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 #include <config.h>
21
22 #include "human.h"
23
24 #include <locale.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <argmatch.h>
30 #include <error.h>
31 #include <intprops.h>
32
33
34 #define HUMAN_READABLE_SUFFIX_LENGTH_MAX 3
35
36 static const char power_letter[] =
37 {
38 0,
39 'K',
40 'M',
41 'G',
42 'T',
43 'P',
44 'E',
45 'Z',
46 'Y'
47 };
48
49
50
51
52
53 static long double
54 adjust_value (int inexact_style, long double value)
55 {
56
57
58
59
60 if (inexact_style != human_round_to_nearest && value < UINTMAX_MAX)
61 {
62 uintmax_t u = value;
63 value = u + (inexact_style == human_ceiling && u != value);
64 }
65
66 return value;
67 }
68
69
70
71
72
73
74
75
76
77
78 static char *
79 group_number (char *number, size_t numberlen,
80 char const *grouping, char const *thousands_sep)
81 {
82 register char *d;
83 size_t grouplen = SIZE_MAX;
84 size_t thousands_seplen = strlen (thousands_sep);
85 size_t i = numberlen;
86
87
88
89 char buf[2 * INT_STRLEN_BOUND (uintmax_t) + 1];
90
91 memcpy (buf, number, numberlen);
92 d = number + numberlen;
93
94 for (;;)
95 {
96 unsigned char g = *grouping;
97
98 if (g)
99 {
100 grouplen = g < CHAR_MAX ? g : i;
101 grouping++;
102 }
103
104 if (i < grouplen)
105 grouplen = i;
106
107 d -= grouplen;
108 i -= grouplen;
109 memcpy (d, buf + i, grouplen);
110 if (i == 0)
111 return d;
112
113 d -= thousands_seplen;
114 memcpy (d, thousands_sep, thousands_seplen);
115 }
116 }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152 char *
153 human_readable (uintmax_t n, char *buf, int opts,
154 uintmax_t from_block_size, uintmax_t to_block_size)
155 {
156 int inexact_style =
157 opts & (human_round_to_nearest | human_floor | human_ceiling);
158 unsigned int base = opts & human_base_1024 ? 1024 : 1000;
159 uintmax_t amt;
160 int tenths;
161 int exponent = -1;
162 int exponent_max = sizeof power_letter - 1;
163 char *p;
164 char *psuffix;
165 char const *integerlim;
166
167
168
169
170
171 int rounding;
172
173 char const *decimal_point = ".";
174 size_t decimal_pointlen = 1;
175 char const *grouping = "";
176 char const *thousands_sep = "";
177 struct lconv const *l = localeconv ();
178 size_t pointlen = strlen (l->decimal_point);
179 if (0 < pointlen && pointlen <= MB_LEN_MAX)
180 {
181 decimal_point = l->decimal_point;
182 decimal_pointlen = pointlen;
183 }
184 grouping = l->grouping;
185 if (strlen (l->thousands_sep) <= MB_LEN_MAX)
186 thousands_sep = l->thousands_sep;
187
188
189 psuffix = buf + LONGEST_HUMAN_READABLE - 1 - HUMAN_READABLE_SUFFIX_LENGTH_MAX;
190 p = psuffix;
191
192
193
194
195 if (to_block_size <= from_block_size)
196 {
197 if (from_block_size % to_block_size == 0)
198 {
199 uintmax_t multiplier = from_block_size / to_block_size;
200 amt = n * multiplier;
201 if (amt / multiplier == n)
202 {
203 tenths = 0;
204 rounding = 0;
205 goto use_integer_arithmetic;
206 }
207 }
208 }
209 else if (from_block_size != 0 && to_block_size % from_block_size == 0)
210 {
211 uintmax_t divisor = to_block_size / from_block_size;
212 uintmax_t r10 = (n % divisor) * 10;
213 uintmax_t r2 = (r10 % divisor) * 2;
214 amt = n / divisor;
215 tenths = r10 / divisor;
216 rounding = r2 < divisor ? 0 < r2 : 2 + (divisor < r2);
217 goto use_integer_arithmetic;
218 }
219
220 {
221
222
223
224
225 long double dto_block_size = to_block_size;
226 long double damt = n * (from_block_size / dto_block_size);
227 size_t buflen;
228 size_t nonintegerlen;
229
230 if (! (opts & human_autoscale))
231 {
232 sprintf (buf, "%.0Lf", adjust_value (inexact_style, damt));
233 buflen = strlen (buf);
234 nonintegerlen = 0;
235 }
236 else
237 {
238 long double e = 1;
239 exponent = 0;
240
241 do
242 {
243 e *= base;
244 exponent++;
245 }
246 while (e * base <= damt && exponent < exponent_max);
247
248 damt /= e;
249
250 sprintf (buf, "%.1Lf", adjust_value (inexact_style, damt));
251 buflen = strlen (buf);
252 nonintegerlen = decimal_pointlen + 1;
253
254 if (1 + nonintegerlen + ! (opts & human_base_1024) < buflen
255 || ((opts & human_suppress_point_zero)
256 && buf[buflen - 1] == '0'))
257 {
258 sprintf (buf, "%.0Lf",
259 adjust_value (inexact_style, damt * 10) / 10);
260 buflen = strlen (buf);
261 nonintegerlen = 0;
262 }
263 }
264
265 p = psuffix - buflen;
266 memmove (p, buf, buflen);
267 integerlim = p + buflen - nonintegerlen;
268 }
269 goto do_grouping;
270
271 use_integer_arithmetic:
272 {
273
274
275
276
277
278 if (opts & human_autoscale)
279 {
280 exponent = 0;
281
282 if (base <= amt)
283 {
284 do
285 {
286 unsigned int r10 = (amt % base) * 10 + tenths;
287 unsigned int r2 = (r10 % base) * 2 + (rounding >> 1);
288 amt /= base;
289 tenths = r10 / base;
290 rounding = (r2 < base
291 ? (r2 + rounding) != 0
292 : 2 + (base < r2 + rounding));
293 exponent++;
294 }
295 while (base <= amt && exponent < exponent_max);
296
297 if (amt < 10)
298 {
299 if (inexact_style == human_round_to_nearest
300 ? 2 < rounding + (tenths & 1)
301 : inexact_style == human_ceiling && 0 < rounding)
302 {
303 tenths++;
304 rounding = 0;
305
306 if (tenths == 10)
307 {
308 amt++;
309 tenths = 0;
310 }
311 }
312
313 if (amt < 10
314 && (tenths || ! (opts & human_suppress_point_zero)))
315 {
316 *--p = '0' + tenths;
317 p -= decimal_pointlen;
318 memcpy (p, decimal_point, decimal_pointlen);
319 tenths = rounding = 0;
320 }
321 }
322 }
323 }
324
325 if (inexact_style == human_round_to_nearest
326 ? 5 < tenths + (0 < rounding + (amt & 1))
327 : inexact_style == human_ceiling && 0 < tenths + rounding)
328 {
329 amt++;
330
331 if ((opts & human_autoscale)
332 && amt == base && exponent < exponent_max)
333 {
334 exponent++;
335 if (! (opts & human_suppress_point_zero))
336 {
337 *--p = '0';
338 p -= decimal_pointlen;
339 memcpy (p, decimal_point, decimal_pointlen);
340 }
341 amt = 1;
342 }
343 }
344
345 integerlim = p;
346
347 do
348 {
349 int digit = amt % 10;
350 *--p = digit + '0';
351 }
352 while ((amt /= 10) != 0);
353 }
354
355 do_grouping:
356 if (opts & human_group_digits)
357 p = group_number (p, integerlim - p, grouping, thousands_sep);
358
359 if (opts & human_SI)
360 {
361 if (exponent < 0)
362 {
363 uintmax_t power;
364 exponent = 0;
365 for (power = 1; power < to_block_size; power *= base)
366 if (++exponent == exponent_max)
367 break;
368 }
369
370 if ((exponent | (opts & human_B)) && (opts & human_space_before_unit))
371 *psuffix++ = ' ';
372
373 if (exponent)
374 *psuffix++ = (! (opts & human_base_1024) && exponent == 1
375 ? 'k'
376 : power_letter[exponent]);
377
378 if (opts & human_B)
379 {
380 if ((opts & human_base_1024) && exponent)
381 *psuffix++ = 'i';
382 *psuffix++ = 'B';
383 }
384 }
385
386 *psuffix = '\0';
387
388 return p;
389 }
390
391
392
393
394 #ifndef DEFAULT_BLOCK_SIZE
395 # define DEFAULT_BLOCK_SIZE 1024
396 #endif
397
398 static char const *const block_size_args[] = { "human-readable", "si", 0 };
399 static int const block_size_opts[] =
400 {
401 human_autoscale + human_SI + human_base_1024,
402 human_autoscale + human_SI
403 };
404
405 static uintmax_t
406 default_block_size (void)
407 {
408 return getenv ("POSIXLY_CORRECT") ? 512 : DEFAULT_BLOCK_SIZE;
409 }
410
411 static strtol_error
412 humblock (char const *spec, uintmax_t *block_size, int *options)
413 {
414 int i;
415 int opts = 0;
416
417 if (! spec
418 && ! (spec = getenv ("BLOCK_SIZE"))
419 && ! (spec = getenv ("BLOCKSIZE")))
420 *block_size = default_block_size ();
421 else
422 {
423 if (*spec == '\'')
424 {
425 opts |= human_group_digits;
426 spec++;
427 }
428
429 if (0 <= (i = ARGMATCH (spec, block_size_args, block_size_opts)))
430 {
431 opts |= block_size_opts[i];
432 *block_size = 1;
433 }
434 else
435 {
436 char *ptr;
437 strtol_error e = xstrtoumax (spec, &ptr, 0, block_size,
438 "eEgGkKmMpPtTyYzZ0");
439 if (e != LONGINT_OK)
440 {
441 *options = 0;
442 return e;
443 }
444 for (; ! ('0' <= *spec && *spec <= '9'); spec++)
445 if (spec == ptr)
446 {
447 opts |= human_SI;
448 if (ptr[-1] == 'B')
449 opts |= human_B;
450 if (ptr[-1] != 'B' || ptr[-2] == 'i')
451 opts |= human_base_1024;
452 break;
453 }
454 }
455 }
456
457 *options = opts;
458 return LONGINT_OK;
459 }
460
461 enum strtol_error
462 human_options (char const *spec, int *opts, uintmax_t *block_size)
463 {
464 strtol_error e = humblock (spec, block_size, opts);
465 if (*block_size == 0)
466 {
467 *block_size = default_block_size ();
468 e = LONGINT_INVALID;
469 }
470 return e;
471 }