xz: Refactor thousand separator detection and disable it on MSVC.
[xz.git] / src / xz / util.c
blob6ab4c2d776ce408ac0791ea59aa09a1b8b7106aa
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file util.c
4 /// \brief Miscellaneous utility functions
5 //
6 // Author: Lasse Collin
7 //
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
13 #include "private.h"
14 #include <stdarg.h>
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
26 // for DJGPP builds.
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)
32 #else
33 # define FORMAT_THOUSAND_SEP(prefix, suffix) ((thousand == WORKS) \
34 ? prefix "'" suffix \
35 : prefix suffix)
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.
44 ///
45 /// This must be called before using the FORMAT_THOUSAND_SEP macro.
46 static void
47 check_thousand_sep(uint32_t slot)
49 if (thousand == UNKNOWN) {
50 bufs[slot][0] = '\0';
51 snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U);
52 thousand = bufs[slot][0] == '1' ? WORKS : BROKEN;
55 return;
57 #endif
60 extern void *
61 xrealloc(void *ptr, size_t size)
63 assert(size > 0);
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.
70 void *p = ptr;
71 ptr = realloc(ptr, size);
73 if (ptr == NULL) {
74 const int saved_errno = errno;
75 free(p);
76 message_fatal("%s", strerror(saved_errno));
79 return ptr;
83 extern char *
84 xstrdup(const char *src)
86 assert(src != NULL);
87 const size_t size = strlen(src) + 1;
88 char *dest = xmalloc(size);
89 return memcpy(dest, src, size);
93 extern uint64_t
94 str_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max)
96 uint64_t result = 0;
98 // Skip blanks.
99 while (*value == ' ' || *value == '\t')
100 ++value;
102 // Accept special value "max". Supporting "min" doesn't seem useful.
103 if (strcmp(value, "max") == 0)
104 return max;
106 if (*value < '0' || *value > '9')
107 message_fatal(_("%s: Value is not a non-negative "
108 "decimal integer"), value);
110 do {
111 // Don't overflow.
112 if (result > UINT64_MAX / 10)
113 goto error;
115 result *= 10;
117 // Another overflow check
118 const uint32_t add = (uint32_t)(*value - '0');
119 if (UINT64_MAX - add < result)
120 goto error;
122 result += add;
123 ++value;
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;
140 ++value;
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)
146 multiplier = 0;
148 if (multiplier == 0) {
149 message(V_ERROR, _("%s: Invalid multiplier suffix"),
150 value - 1);
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)
157 goto error;
159 result *= multiplier;
162 if (result < min || result > max)
163 goto error;
165 return result;
167 error:
168 message_fatal(_("Value of the option `%s' must be in the range "
169 "[%" PRIu64 ", %" PRIu64 "]"),
170 name, min, max);
174 extern uint64_t
175 round_up_to_mib(uint64_t n)
177 return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0);
181 extern const char *
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);
191 return bufs[slot];
195 extern const char *
196 uint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min,
197 enum nicestr_unit unit_max, bool always_also_bytes,
198 uint32_t slot)
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);
215 } else {
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);
220 do {
221 d /= 1024.0;
222 ++unit;
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)"),
233 value);
235 return bufs[slot];
239 extern void
240 my_snprintf(char **pos, size_t *left, const char *fmt, ...)
242 va_list ap;
243 va_start(ap, fmt);
244 const int len = vsnprintf(*pos, *left, fmt, ap);
245 va_end(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) {
254 *left = 0;
255 } else {
256 *pos += len;
257 *left -= (size_t)(len);
260 return;
264 extern bool
265 is_tty_stdin(void)
267 const bool ret = isatty(STDIN_FILENO);
269 if (ret)
270 message_error(_("Compressed data cannot be read from "
271 "a terminal"));
273 return ret;
277 extern bool
278 is_tty_stdout(void)
280 const bool ret = isatty(STDOUT_FILENO);
282 if (ret)
283 message_error(_("Compressed data cannot be written to "
284 "a terminal"));
286 return ret;