blob: 9fe9481bcf9194f03a9cdedd4c41a42a277859a3 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
2 * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
3 * Copyright (C) 2010 Davidlohr Bueso <dave@gnu.org>
4 */
5
6#include <stdio.h>
7#include <stdlib.h>
8#include <inttypes.h>
9#include <ctype.h>
10#include <errno.h>
11#include <sys/stat.h>
12#include <string.h>
bigbiff7b4c7a62015-01-01 19:44:14 -050013#include <assert.h>
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050014
15#include "c.h"
16#include "nls.h"
17#include "strutils.h"
18#include "bitops.h"
19
20static int do_scale_by_power (uintmax_t *x, int base, int power)
21{
22 while (power--) {
23 if (UINTMAX_MAX / base < *x)
bigbiff7b4c7a62015-01-01 19:44:14 -050024 return -ERANGE;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050025 *x *= base;
26 }
27 return 0;
28}
29
30/*
31 * strtosize() - convert string to size (uintmax_t).
32 *
33 * Supported suffixes:
34 *
35 * XiB or X for 2^N
bigbiff7b4c7a62015-01-01 19:44:14 -050036 * where X = {K,M,G,T,P,E,Z,Y}
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050037 * or X = {k,m,g,t,p,e} (undocumented for backward compatibility only)
38 * for example:
39 * 10KiB = 10240
40 * 10K = 10240
41 *
42 * XB for 10^N
bigbiff7b4c7a62015-01-01 19:44:14 -050043 * where X = {K,M,G,T,P,E,Z,Y}
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050044 * for example:
45 * 10KB = 10000
46 *
bigbiff7b4c7a62015-01-01 19:44:14 -050047 * The optinal 'power' variable returns number associated with used suffix
48 * {K,M,G,T,P,E,Z,Y} = {1,2,3,4,5,6,7,8}.
49 *
50 * The function also supports decimal point, for example:
51 * 0.5MB = 500000
52 * 0.5MiB = 512000
53 *
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050054 * Note that the function does not accept numbers with '-' (negative sign)
55 * prefix.
56 */
bigbiff7b4c7a62015-01-01 19:44:14 -050057int parse_size(const char *str, uintmax_t *res, int *power)
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050058{
59 char *p;
bigbiff7b4c7a62015-01-01 19:44:14 -050060 uintmax_t x, frac = 0;
61 int base = 1024, rc = 0, pwr = 0, frac_zeros = 0;
62
63 static const char *suf = "KMGTPEYZ";
64 static const char *suf2 = "kmgtpeyz";
65 const char *sp;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050066
67 *res = 0;
68
bigbiff7b4c7a62015-01-01 19:44:14 -050069 if (!str || !*str) {
70 rc = -EINVAL;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050071 goto err;
bigbiff7b4c7a62015-01-01 19:44:14 -050072 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050073
74 /* Only positive numbers are acceptable
75 *
76 * Note that this check is not perfect, it would be better to
77 * use lconv->negative_sign. But coreutils use the same solution,
78 * so it's probably good enough...
79 */
80 p = (char *) str;
81 while (isspace((unsigned char) *p))
82 p++;
bigbiff7b4c7a62015-01-01 19:44:14 -050083 if (*p == '-') {
84 rc = -EINVAL;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050085 goto err;
bigbiff7b4c7a62015-01-01 19:44:14 -050086 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050087 p = NULL;
88
89 errno = 0;
90 x = strtoumax(str, &p, 0);
91
92 if (p == str ||
bigbiff7b4c7a62015-01-01 19:44:14 -050093 (errno != 0 && (x == UINTMAX_MAX || x == 0))) {
94 rc = errno ? -errno : -1;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050095 goto err;
bigbiff7b4c7a62015-01-01 19:44:14 -050096 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -050097 if (!p || !*p)
98 goto done; /* without suffix */
99
100 /*
101 * Check size suffixes
102 */
bigbiff7b4c7a62015-01-01 19:44:14 -0500103check_suffix:
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500104 if (*(p + 1) == 'i' && *(p + 2) == 'B' && !*(p + 3))
105 base = 1024; /* XiB, 2^N */
106 else if (*(p + 1) == 'B' && !*(p + 2))
107 base = 1000; /* XB, 10^N */
bigbiff7b4c7a62015-01-01 19:44:14 -0500108 else if (*(p + 1)) {
109 struct lconv const *l = localeconv();
110 char *dp = l ? l->decimal_point : NULL;
111 size_t dpsz = dp ? strlen(dp) : 0;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500112
bigbiff7b4c7a62015-01-01 19:44:14 -0500113 if (frac == 0 && *p && dp && strncmp(dp, p, dpsz) == 0) {
114 char *fstr = p + dpsz;
115
116 for (p = fstr; *p && *p == '0'; p++)
117 frac_zeros++;
118 errno = 0, p = NULL;
119 frac = strtoumax(fstr, &p, 0);
120 if (p == fstr ||
121 (errno != 0 && (frac == UINTMAX_MAX || frac == 0))) {
122 rc = errno ? -errno : -1;
123 goto err;
124 }
125 if (frac && (!p || !*p)) {
126 rc = -EINVAL;
127 goto err; /* without suffix, but with frac */
128 }
129 goto check_suffix;
130 }
131 rc = -EINVAL;
132 goto err; /* unexpected suffix */
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500133 }
134
bigbiff7b4c7a62015-01-01 19:44:14 -0500135 sp = strchr(suf, *p);
136 if (sp)
137 pwr = (sp - suf) + 1;
138 else {
139 sp = strchr(suf2, *p);
140 if (sp)
141 pwr = (sp - suf2) + 1;
142 else {
143 rc = -EINVAL;
144 goto err;
145 }
146 }
147
148 rc = do_scale_by_power(&x, base, pwr);
149 if (power)
150 *power = pwr;
151 if (frac && pwr) {
152 int zeros_in_pwr = frac_zeros % 3;
153 int frac_pwr = pwr - (frac_zeros / 3) - 1;
154 uintmax_t y = frac * (zeros_in_pwr == 0 ? 100 :
155 zeros_in_pwr == 1 ? 10 : 1);
156
157 if (frac_pwr < 0) {
158 rc = -EINVAL;
159 goto err;
160 }
161 do_scale_by_power(&y, base, frac_pwr);
162 x += y;
163 }
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500164done:
165 *res = x;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500166err:
bigbiff7b4c7a62015-01-01 19:44:14 -0500167 return rc;
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500168}
169
bigbiff7b4c7a62015-01-01 19:44:14 -0500170int strtosize(const char *str, uintmax_t *res)
171{
172 return parse_size(str, res, NULL);
173}
174
175int isdigit_string(const char *str)
176{
177 const char *p;
178
179 for (p = str; p && *p && isdigit((unsigned char) *p); p++);
180
181 return p && p > str && !*p;
182}
183
184
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500185#ifndef HAVE_MEMPCPY
186void *mempcpy(void *restrict dest, const void *restrict src, size_t n)
187{
188 return ((char *)memcpy(dest, src, n)) + n;
189}
190#endif
191
192#ifndef HAVE_STRNLEN
193size_t strnlen(const char *s, size_t maxlen)
194{
195 int i;
196
197 for (i = 0; i < maxlen; i++) {
198 if (s[i] == '\0')
199 return i + 1;
200 }
201 return maxlen;
202}
203#endif
204
205#ifndef HAVE_STRNCHR
206char *strnchr(const char *s, size_t maxlen, int c)
207{
208 for (; maxlen-- && *s != '\0'; ++s)
209 if (*s == (char)c)
210 return (char *)s;
211 return NULL;
212}
213#endif
214
215#ifndef HAVE_STRNDUP
216char *strndup(const char *s, size_t n)
217{
218 size_t len = strnlen(s, n);
219 char *new = (char *) malloc((len + 1) * sizeof(char));
220 if (!new)
221 return NULL;
222 new[len] = '\0';
223 return (char *) memcpy(new, s, len);
224}
225#endif
226
227int16_t strtos16_or_err(const char *str, const char *errmesg)
228{
229 int32_t num = strtos32_or_err(str, errmesg);
230
231 if (num < INT16_MIN || num > INT16_MAX)
232 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
233
234 return num;
235}
236
237uint16_t strtou16_or_err(const char *str, const char *errmesg)
238{
239 uint32_t num = strtou32_or_err(str, errmesg);
240
241 if (num > UINT16_MAX)
242 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
243
244 return num;
245}
246
247int32_t strtos32_or_err(const char *str, const char *errmesg)
248{
249 int64_t num = strtos64_or_err(str, errmesg);
250
251 if (num < INT32_MIN || num > INT32_MAX)
252 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
253
254 return num;
255}
256
257uint32_t strtou32_or_err(const char *str, const char *errmesg)
258{
259 uint64_t num = strtou64_or_err(str, errmesg);
260
261 if (num > UINT32_MAX)
262 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
263
264 return num;
265}
266
267int64_t strtos64_or_err(const char *str, const char *errmesg)
268{
269 int64_t num;
270 char *end = NULL;
271
272 if (str == NULL || *str == '\0')
273 goto err;
274 errno = 0;
275 num = strtoimax(str, &end, 10);
276
277 if (errno || str == end || (end && *end))
278 goto err;
279
280 return num;
281err:
282 if (errno)
283 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
284
285 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
286}
287
288uint64_t strtou64_or_err(const char *str, const char *errmesg)
289{
290 uintmax_t num;
291 char *end = NULL;
292
293 if (str == NULL || *str == '\0')
294 goto err;
295 errno = 0;
296 num = strtoumax(str, &end, 10);
297
298 if (errno || str == end || (end && *end))
299 goto err;
300
301 return num;
302err:
303 if (errno)
304 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
305
306 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
307}
308
309
310double strtod_or_err(const char *str, const char *errmesg)
311{
312 double num;
313 char *end = NULL;
314
315 if (str == NULL || *str == '\0')
316 goto err;
317 errno = 0;
318 num = strtod(str, &end);
319
320 if (errno || str == end || (end && *end))
321 goto err;
322
323 return num;
324err:
325 if (errno)
326 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
327
328 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
329}
330
331long strtol_or_err(const char *str, const char *errmesg)
332{
333 long num;
334 char *end = NULL;
335
336 if (str == NULL || *str == '\0')
337 goto err;
338 errno = 0;
339 num = strtol(str, &end, 10);
340
341 if (errno || str == end || (end && *end))
342 goto err;
343
344 return num;
345err:
346 if (errno)
347 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
348 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
349}
350
351unsigned long strtoul_or_err(const char *str, const char *errmesg)
352{
353 unsigned long num;
354 char *end = NULL;
355
356 if (str == NULL || *str == '\0')
357 goto err;
358 errno = 0;
359 num = strtoul(str, &end, 10);
360
361 if (errno || str == end || (end && *end))
362 goto err;
363
364 return num;
365err:
366 if (errno)
367 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
368
369 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
370}
371
372uintmax_t strtosize_or_err(const char *str, const char *errmesg)
373{
374 uintmax_t num;
375
376 if (strtosize(str, &num) == 0)
377 return num;
378
379 if (errno)
380 err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
381
382 errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
383}
384
bigbiff7b4c7a62015-01-01 19:44:14 -0500385
386void strtotimeval_or_err(const char *str, struct timeval *tv, const char *errmesg)
387{
388 double user_input;
389
390 user_input = strtod_or_err(str, errmesg);
391 tv->tv_sec = (time_t) user_input;
392 tv->tv_usec = (long)((user_input - tv->tv_sec) * 1000000);
393}
394
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500395/*
396 * Converts stat->st_mode to ls(1)-like mode string. The size of "str" must
bigbiff7b4c7a62015-01-01 19:44:14 -0500397 * be 11 bytes.
bigbiff bigbiffe60683a2013-02-22 20:55:50 -0500398 */
399void strmode(mode_t mode, char *str)
400{
401 if (S_ISDIR(mode))
402 str[0] = 'd';
403 else if (S_ISLNK(mode))
404 str[0] = 'l';
405 else if (S_ISCHR(mode))
406 str[0] = 'c';
407 else if (S_ISBLK(mode))
408 str[0] = 'b';
409 else if (S_ISSOCK(mode))
410 str[0] = 's';
411 else if (S_ISFIFO(mode))
412 str[0] = 'p';
413 else if (S_ISREG(mode))
414 str[0] = '-';
415
416 str[1] = mode & S_IRUSR ? 'r' : '-';
417 str[2] = mode & S_IWUSR ? 'w' : '-';
418 str[3] = (mode & S_ISUID
419 ? (mode & S_IXUSR ? 's' : 'S')
420 : (mode & S_IXUSR ? 'x' : '-'));
421 str[4] = mode & S_IRGRP ? 'r' : '-';
422 str[5] = mode & S_IWGRP ? 'w' : '-';
423 str[6] = (mode & S_ISGID
424 ? (mode & S_IXGRP ? 's' : 'S')
425 : (mode & S_IXGRP ? 'x' : '-'));
426 str[7] = mode & S_IROTH ? 'r' : '-';
427 str[8] = mode & S_IWOTH ? 'w' : '-';
428 str[9] = (mode & S_ISVTX
429 ? (mode & S_IXOTH ? 't' : 'T')
430 : (mode & S_IXOTH ? 'x' : '-'));
431 str[10] = '\0';
432}
433
434/*
435 * returns exponent (2^x=n) in range KiB..PiB
436 */
437static int get_exp(uint64_t n)
438{
439 int shft;
440
441 for (shft = 10; shft <= 60; shft += 10) {
442 if (n < (1ULL << shft))
443 break;
444 }
445 return shft - 10;
446}
447
448char *size_to_human_string(int options, uint64_t bytes)
449{
450 char buf[32];
451 int dec, exp;
452 uint64_t frac;
453 const char *letters = "BKMGTPE";
454 char suffix[sizeof(" KiB")], *psuf = suffix;
455 char c;
456
457 if (options & SIZE_SUFFIX_SPACE)
458 *psuf++ = ' ';
459
460 exp = get_exp(bytes);
461 c = *(letters + (exp ? exp / 10 : 0));
462 dec = exp ? bytes / (1ULL << exp) : bytes;
463 frac = exp ? bytes % (1ULL << exp) : 0;
464
465 *psuf++ = c;
466
467 if ((options & SIZE_SUFFIX_3LETTER) && (c != 'B')) {
468 *psuf++ = 'i';
469 *psuf++ = 'B';
470 }
471
472 *psuf = '\0';
473
474 /* fprintf(stderr, "exp: %d, unit: %c, dec: %d, frac: %jd\n",
475 * exp, suffix[0], dec, frac);
476 */
477
478 if (frac) {
479 /* round */
480 frac = (frac / (1ULL << (exp - 10)) + 50) / 100;
481 if (frac == 10)
482 dec++, frac = 0;
483 }
484
485 if (frac) {
486 struct lconv const *l = localeconv();
487 char *dp = l ? l->decimal_point : NULL;
488
489 if (!dp || !*dp)
490 dp = ".";
491 snprintf(buf, sizeof(buf), "%d%s%jd%s", dec, dp, frac, suffix);
492 } else
493 snprintf(buf, sizeof(buf), "%d%s", dec, suffix);
494
495 return strdup(buf);
496}
497
498/*
499 * Parses comma delimited list to array with IDs, for example:
500 *
501 * "aaa,bbb,ccc" --> ary[0] = FOO_AAA;
502 * ary[1] = FOO_BBB;
503 * ary[3] = FOO_CCC;
504 *
505 * The function name2id() provides conversion from string to ID.
506 *
507 * Returns: >= 0 : number of items added to ary[]
508 * -1 : parse error or unknown item
509 * -2 : arysz reached
510 */
511int string_to_idarray(const char *list, int ary[], size_t arysz,
512 int (name2id)(const char *, size_t))
513{
514 const char *begin = NULL, *p;
515 size_t n = 0;
516
517 if (!list || !*list || !ary || !arysz || !name2id)
518 return -1;
519
520 for (p = list; p && *p; p++) {
521 const char *end = NULL;
522 int id;
523
524 if (n >= arysz)
525 return -2;
526 if (!begin)
527 begin = p; /* begin of the column name */
528 if (*p == ',')
529 end = p; /* terminate the name */
530 if (*(p + 1) == '\0')
531 end = p + 1; /* end of string */
532 if (!begin || !end)
533 continue;
534 if (end <= begin)
535 return -1;
536
537 id = name2id(begin, end - begin);
538 if (id == -1)
539 return -1;
540 ary[ n++ ] = id;
541 begin = NULL;
542 if (end && !*end)
543 break;
544 }
545 return n;
546}
547
548/*
549 * Parses the array like string_to_idarray but if format is "+aaa,bbb"
550 * it adds fields to array instead of replacing them.
551 */
552int string_add_to_idarray(const char *list, int ary[], size_t arysz,
553 int *ary_pos, int (name2id)(const char *, size_t))
554{
555 const char *list_add;
556 int r;
557
558 if (!list || !*list || !ary_pos ||
559 *ary_pos < 0 || (size_t) *ary_pos > arysz)
560 return -1;
561
562 if (list[0] == '+')
563 list_add = &list[1];
564 else {
565 list_add = list;
566 *ary_pos = 0;
567 }
568
569 r = string_to_idarray(list_add, &ary[*ary_pos], arysz - *ary_pos, name2id);
570 if (r > 0)
571 *ary_pos += r;
572 return r;
573}
574
575/*
576 * LIST ::= <item> [, <item>]
577 *
578 * The <item> is translated to 'id' by name2id() function and the 'id' is used
579 * as a position in the 'ary' bit array. It means that the 'id' has to be in
580 * range <0..N> where N < sizeof(ary) * NBBY.
581 *
582 * Returns: 0 on success, <0 on error.
583 */
584int string_to_bitarray(const char *list,
585 char *ary,
586 int (*name2bit)(const char *, size_t))
587{
588 const char *begin = NULL, *p;
589
590 if (!list || !name2bit || !ary)
591 return -EINVAL;
592
593 for (p = list; p && *p; p++) {
594 const char *end = NULL;
595 int bit;
596
597 if (!begin)
598 begin = p; /* begin of the level name */
599 if (*p == ',')
600 end = p; /* terminate the name */
601 if (*(p + 1) == '\0')
602 end = p + 1; /* end of string */
603 if (!begin || !end)
604 continue;
605 if (end <= begin)
606 return -1;
607
608 bit = name2bit(begin, end - begin);
609 if (bit < 0)
610 return bit;
611 setbit(ary, bit);
612 begin = NULL;
613 if (end && !*end)
614 break;
615 }
616 return 0;
617}
618
619/*
620 * LIST ::= <item> [, <item>]
621 *
622 * The <item> is translated to 'id' by name2flag() function and the flags is
623 * set to the 'mask'
624*
625 * Returns: 0 on success, <0 on error.
626 */
627int string_to_bitmask(const char *list,
628 unsigned long *mask,
629 long (*name2flag)(const char *, size_t))
630{
631 const char *begin = NULL, *p;
632
633 if (!list || !name2flag || !mask)
634 return -EINVAL;
635
636 for (p = list; p && *p; p++) {
637 const char *end = NULL;
638 long flag;
639
640 if (!begin)
641 begin = p; /* begin of the level name */
642 if (*p == ',')
643 end = p; /* terminate the name */
644 if (*(p + 1) == '\0')
645 end = p + 1; /* end of string */
646 if (!begin || !end)
647 continue;
648 if (end <= begin)
649 return -1;
650
651 flag = name2flag(begin, end - begin);
652 if (flag < 0)
653 return flag; /* error */
654 *mask |= flag;
655 begin = NULL;
656 if (end && !*end)
657 break;
658 }
659 return 0;
660}
661
662/*
663 * Parse the lower and higher values in a string containing
664 * "lower:higher" or "lower-higher" format. Note that either
665 * the lower or the higher values may be missing, and the def
666 * value will be assigned to it by default.
667 *
668 * Returns: 0 on success, <0 on error.
669 */
670int parse_range(const char *str, int *lower, int *upper, int def)
671{
672 char *end = NULL;
673
674 if (!str)
675 return 0;
676
677 *upper = *lower = def;
678 errno = 0;
679
680 if (*str == ':') { /* <:N> */
681 str++;
682 *upper = strtol(str, &end, 10);
683 if (errno || !end || *end || end == str)
684 return -1;
685 } else {
686 *upper = *lower = strtol(str, &end, 10);
687 if (errno || !end || end == str)
688 return -1;
689
690 if (*end == ':' && !*(end + 1)) /* <M:> */
691 *upper = 0;
692 else if (*end == '-' || *end == ':') { /* <M:N> <M-N> */
693 str = end + 1;
694 end = NULL;
695 errno = 0;
696 *upper = strtol(str, &end, 10);
697
698 if (errno || !end || *end || end == str)
699 return -1;
700 }
701 }
702 return 0;
703}
704
705/*
706 * Compare two strings for equality, ignoring at most one trailing
707 * slash.
708 */
709int streq_except_trailing_slash(const char *s1, const char *s2)
710{
711 int equal;
712
713 if (!s1 && !s2)
714 return 1;
715 if (!s1 || !s2)
716 return 0;
717
718 equal = !strcmp(s1, s2);
719
720 if (!equal) {
721 size_t len1 = strlen(s1);
722 size_t len2 = strlen(s2);
723
724 if (len1 && *(s1 + len1 - 1) == '/')
725 len1--;
726 if (len2 && *(s2 + len2 - 1) == '/')
727 len2--;
728 if (len1 != len2)
729 return 0;
730
731 equal = !strncmp(s1, s2, len1);
732 }
733
734 return equal;
735}
736
737
738#ifdef TEST_PROGRAM
739
740int main(int argc, char *argv[])
741{
742 uintmax_t size = 0;
743 char *hum, *hum2;
744
745 if (argc < 2) {
746 fprintf(stderr, "usage: %s <number>[suffix]\n", argv[0]);
747 exit(EXIT_FAILURE);
748 }
749
750 if (strtosize(argv[1], &size))
751 errx(EXIT_FAILURE, "invalid size '%s' value", argv[1]);
752
753 hum = size_to_human_string(SIZE_SUFFIX_1LETTER, size);
754 hum2 = size_to_human_string(SIZE_SUFFIX_3LETTER |
755 SIZE_SUFFIX_SPACE, size);
756
757 printf("%25s : %20ju : %8s : %12s\n", argv[1], size, hum, hum2);
758 free(hum);
759 free(hum2);
760
761 return EXIT_SUCCESS;
762}
763#endif /* TEST_PROGRAM */