1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Miscellaneous utility functions
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
17 /// Buffers for uint64_to_str() and uint64_to_nicestr()
18 static char bufs
[4][128];
21 // Thousand separator support in uint64_to_str() and uint64_to_nicestr():
23 // DJGPP 2.05 added support for thousands separators but it's broken
24 // at least under WinXP with Finnish locale that uses a non-breaking space
25 // as the thousands separator. Workaround by disabling thousands separators
28 // MSVC doesn't support thousand separators.
29 #if defined(__DJGPP__) || defined(_MSC_VER)
30 # define FORMAT_THOUSAND_SEP(prefix, suffix) prefix suffix
31 # define check_thousand_sep(slot) do { } while (0)
33 # define FORMAT_THOUSAND_SEP(prefix, suffix) ((thousand == WORKS) \
37 static enum { UNKNOWN
, WORKS
, BROKEN
} thousand
= UNKNOWN
;
39 /// Check if thousands separator is supported. Run-time checking is easiest
40 /// because it seems to be sometimes lacking even on a POSIXish system.
41 /// Note that trying to use thousands separators when snprintf() doesn't
42 /// support them results in undefined behavior. This just has happened to
43 /// work well enough in practice.
45 /// This must be called before using the FORMAT_THOUSAND_SEP macro.
47 check_thousand_sep(uint32_t slot
)
49 if (thousand
== UNKNOWN
) {
51 snprintf(bufs
[slot
], sizeof(bufs
[slot
]), "%'u", 1U);
52 thousand
= bufs
[slot
][0] == '1' ? WORKS
: BROKEN
;
61 xrealloc(void *ptr
, size_t size
)
65 // Save ptr so that we can free it if realloc fails.
66 // The point is that message_fatal ends up calling stdio functions
67 // which in some libc implementations might allocate memory from
68 // the heap. Freeing ptr improves the chances that there's free
69 // memory for stdio functions if they need it.
71 ptr
= realloc(ptr
, size
);
74 const int saved_errno
= errno
;
76 message_fatal("%s", strerror(saved_errno
));
84 xstrdup(const char *src
)
87 const size_t size
= strlen(src
) + 1;
88 char *dest
= xmalloc(size
);
89 return memcpy(dest
, src
, size
);
94 str_to_uint64(const char *name
, const char *value
, uint64_t min
, uint64_t max
)
99 while (*value
== ' ' || *value
== '\t')
102 // Accept special value "max". Supporting "min" doesn't seem useful.
103 if (strcmp(value
, "max") == 0)
106 if (*value
< '0' || *value
> '9')
107 message_fatal(_("%s: Value is not a non-negative "
108 "decimal integer"), value
);
112 if (result
> UINT64_MAX
/ 10)
117 // Another overflow check
118 const uint32_t add
= (uint32_t)(*value
- '0');
119 if (UINT64_MAX
- add
< result
)
124 } while (*value
>= '0' && *value
<= '9');
126 if (*value
!= '\0') {
127 // Look for suffix. Originally this supported both base-2
128 // and base-10, but since there seems to be little need
129 // for base-10 in this program, treat everything as base-2
130 // and also be more relaxed about the case of the first
131 // letter of the suffix.
132 uint64_t multiplier
= 0;
133 if (*value
== 'k' || *value
== 'K')
134 multiplier
= UINT64_C(1) << 10;
135 else if (*value
== 'm' || *value
== 'M')
136 multiplier
= UINT64_C(1) << 20;
137 else if (*value
== 'g' || *value
== 'G')
138 multiplier
= UINT64_C(1) << 30;
142 // Allow also e.g. Ki, KiB, and KB.
143 if (*value
!= '\0' && strcmp(value
, "i") != 0
144 && strcmp(value
, "iB") != 0
145 && strcmp(value
, "B") != 0)
148 if (multiplier
== 0) {
149 message(V_ERROR
, _("%s: Invalid multiplier suffix"),
151 message_fatal(_("Valid suffixes are `KiB' (2^10), "
152 "`MiB' (2^20), and `GiB' (2^30)."));
155 // Don't overflow here either.
156 if (result
> UINT64_MAX
/ multiplier
)
159 result
*= multiplier
;
162 if (result
< min
|| result
> max
)
168 message_fatal(_("Value of the option `%s' must be in the range "
169 "[%" PRIu64
", %" PRIu64
"]"),
175 round_up_to_mib(uint64_t n
)
177 return (n
>> 20) + ((n
& ((UINT32_C(1) << 20) - 1)) != 0);
182 uint64_to_str(uint64_t value
, uint32_t slot
)
184 assert(slot
< ARRAY_SIZE(bufs
));
186 check_thousand_sep(slot
);
188 snprintf(bufs
[slot
], sizeof(bufs
[slot
]),
189 FORMAT_THOUSAND_SEP("%", PRIu64
), value
);
196 uint64_to_nicestr(uint64_t value
, enum nicestr_unit unit_min
,
197 enum nicestr_unit unit_max
, bool always_also_bytes
,
200 assert(unit_min
<= unit_max
);
201 assert(unit_max
<= NICESTR_TIB
);
202 assert(slot
< ARRAY_SIZE(bufs
));
204 check_thousand_sep(slot
);
206 enum nicestr_unit unit
= NICESTR_B
;
207 char *pos
= bufs
[slot
];
208 size_t left
= sizeof(bufs
[slot
]);
210 if ((unit_min
== NICESTR_B
&& value
< 10000)
211 || unit_max
== NICESTR_B
) {
212 // The value is shown as bytes.
213 my_snprintf(&pos
, &left
, FORMAT_THOUSAND_SEP("%", "u"),
214 (unsigned int)value
);
216 // Scale the value to a nicer unit. Unless unit_min and
217 // unit_max limit us, we will show at most five significant
218 // digits with one decimal place.
219 double d
= (double)(value
);
223 } while (unit
< unit_min
|| (d
> 9999.9 && unit
< unit_max
));
225 my_snprintf(&pos
, &left
, FORMAT_THOUSAND_SEP("%", ".1f"), d
);
228 static const char suffix
[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" };
229 my_snprintf(&pos
, &left
, " %s", suffix
[unit
]);
231 if (always_also_bytes
&& value
>= 10000)
232 snprintf(pos
, left
, FORMAT_THOUSAND_SEP(" (%", PRIu64
" B)"),
240 my_snprintf(char **pos
, size_t *left
, const char *fmt
, ...)
244 const int len
= vsnprintf(*pos
, *left
, fmt
, ap
);
247 // If an error occurred, we want the caller to think that the whole
248 // buffer was used. This way no more data will be written to the
249 // buffer. We don't need better error handling here, although it
250 // is possible that the result looks garbage on the terminal if
251 // e.g. an UTF-8 character gets split. That shouldn't (easily)
252 // happen though, because the buffers used have some extra room.
253 if (len
< 0 || (size_t)(len
) >= *left
) {
257 *left
-= (size_t)(len
);
267 const bool ret
= isatty(STDIN_FILENO
);
270 message_error(_("Compressed data cannot be read from "
280 const bool ret
= isatty(STDOUT_FILENO
);
283 message_error(_("Compressed data cannot be written to "