1 // SPDX-License-Identifier: 0BSD
2
3 ///////////////////////////////////////////////////////////////////////////////
4 //
5 /// \file util.c
6 /// \brief Miscellaneous utility functions
7 //
8 // Author: Lasse Collin
9 //
10 ///////////////////////////////////////////////////////////////////////////////
11
12 #include "private.h"
13 #include <stdarg.h>
14
15
16 /// Buffers for uint64_to_str() and uint64_to_nicestr()
17 static char bufs[4][128];
18
19
20 // Thousand separator support in uint64_to_str() and uint64_to_nicestr():
21 //
22 // DJGPP 2.05 added support for thousands separators but it's broken
23 // at least under WinXP with Finnish locale that uses a non-breaking space
24 // as the thousands separator. Workaround by disabling thousands separators
25 // for DJGPP builds.
26 //
27 // MSVC doesn't support thousand separators.
28 //
29 // MinGW-w64 supports thousand separators only with its own stdio functions
30 // which our sysdefs.h disables when _UCRT && HAVE_SMALL.
31 #if defined(__DJGPP__) || defined(_MSC_VER) \
32 || (defined(__MINGW32__) && __USE_MINGW_ANSI_STDIO == 0)
33 # define FORMAT_THOUSAND_SEP(prefix, suffix) prefix suffix
34 # define check_thousand_sep(slot) do { } while (0)
35 #else
36 # define FORMAT_THOUSAND_SEP(prefix, suffix) ((thousand == WORKS) \
37 ? prefix "'" suffix \
38 : prefix suffix)
39
40 static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN;
41
42 /// Check if thousands separator is supported. Run-time checking is easiest
43 /// because it seems to be sometimes lacking even on a POSIXish system.
44 /// Note that trying to use thousands separators when snprintf() doesn't
45 /// support them results in undefined behavior. This just has happened to
46 /// work well enough in practice.
47 ///
48 /// This must be called before using the FORMAT_THOUSAND_SEP macro.
49 static void
check_thousand_sep(uint32_t slot)50 check_thousand_sep(uint32_t slot)
51 {
52 if (thousand == UNKNOWN) {
53 bufs[slot][0] = '\0';
54 snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U);
55 thousand = bufs[slot][0] == '1' ? WORKS : BROKEN;
56 }
57
58 return;
59 }
60 #endif
61
62
63 extern void *
xrealloc(void * ptr,size_t size)64 xrealloc(void *ptr, size_t size)
65 {
66 assert(size > 0);
67
68 // Save ptr so that we can free it if realloc fails.
69 // The point is that message_fatal ends up calling stdio functions
70 // which in some libc implementations might allocate memory from
71 // the heap. Freeing ptr improves the chances that there's free
72 // memory for stdio functions if they need it.
73 void *p = ptr;
74 ptr = realloc(ptr, size);
75
76 if (ptr == NULL) {
77 const int saved_errno = errno;
78 free(p);
79 message_fatal("%s", strerror(saved_errno));
80 }
81
82 return ptr;
83 }
84
85
86 extern char *
xstrdup(const char * src)87 xstrdup(const char *src)
88 {
89 assert(src != NULL);
90 const size_t size = strlen(src) + 1;
91 char *dest = xmalloc(size);
92 return memcpy(dest, src, size);
93 }
94
95
96 extern uint64_t
str_to_uint64(const char * name,const char * value,uint64_t min,uint64_t max)97 str_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max)
98 {
99 uint64_t result = 0;
100
101 // Skip blanks.
102 while (*value == ' ' || *value == '\t')
103 ++value;
104
105 // Accept special value "max". Supporting "min" doesn't seem useful.
106 if (strcmp(value, "max") == 0)
107 return max;
108
109 if (*value < '0' || *value > '9')
110 message_fatal(_("%s: %s"), value,
111 _("Value is not a non-negative decimal integer"));
112
113 do {
114 // Don't overflow.
115 if (result > UINT64_MAX / 10)
116 goto error;
117
118 result *= 10;
119
120 // Another overflow check
121 const uint32_t add = (uint32_t)(*value - '0');
122 if (UINT64_MAX - add < result)
123 goto error;
124
125 result += add;
126 ++value;
127 } while (*value >= '0' && *value <= '9');
128
129 if (*value != '\0') {
130 // Look for suffix. Originally this supported both base-2
131 // and base-10, but since there seems to be little need
132 // for base-10 in this program, treat everything as base-2
133 // and also be more relaxed about the case of the first
134 // letter of the suffix.
135 uint64_t multiplier = 0;
136 if (*value == 'k' || *value == 'K')
137 multiplier = UINT64_C(1) << 10;
138 else if (*value == 'm' || *value == 'M')
139 multiplier = UINT64_C(1) << 20;
140 else if (*value == 'g' || *value == 'G')
141 multiplier = UINT64_C(1) << 30;
142
143 ++value;
144
145 // Allow also e.g. Ki, KiB, and KB.
146 if (*value != '\0' && strcmp(value, "i") != 0
147 && strcmp(value, "iB") != 0
148 && strcmp(value, "B") != 0)
149 multiplier = 0;
150
151 if (multiplier == 0) {
152 message(V_ERROR, _("%s: Invalid multiplier suffix"),
153 value - 1);
154 message_fatal(_("Valid suffixes are 'KiB' (2^10), "
155 "'MiB' (2^20), and 'GiB' (2^30)."));
156 }
157
158 // Don't overflow here either.
159 if (result > UINT64_MAX / multiplier)
160 goto error;
161
162 result *= multiplier;
163 }
164
165 if (result < min || result > max)
166 goto error;
167
168 return result;
169
170 error:
171 message_fatal(_("Value of the option '%s' must be in the range "
172 "[%" PRIu64 ", %" PRIu64 "]"),
173 name, min, max);
174 }
175
176
177 extern uint64_t
round_up_to_mib(uint64_t n)178 round_up_to_mib(uint64_t n)
179 {
180 return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0);
181 }
182
183
184 extern const char *
uint64_to_str(uint64_t value,uint32_t slot)185 uint64_to_str(uint64_t value, uint32_t slot)
186 {
187 assert(slot < ARRAY_SIZE(bufs));
188
189 check_thousand_sep(slot);
190
191 snprintf(bufs[slot], sizeof(bufs[slot]),
192 FORMAT_THOUSAND_SEP("%", PRIu64), value);
193
194 return bufs[slot];
195 }
196
197
198 extern const char *
uint64_to_nicestr(uint64_t value,enum nicestr_unit unit_min,enum nicestr_unit unit_max,bool always_also_bytes,uint32_t slot)199 uint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min,
200 enum nicestr_unit unit_max, bool always_also_bytes,
201 uint32_t slot)
202 {
203 assert(unit_min <= unit_max);
204 assert(unit_max <= NICESTR_TIB);
205 assert(slot < ARRAY_SIZE(bufs));
206
207 check_thousand_sep(slot);
208
209 enum nicestr_unit unit = NICESTR_B;
210 char *pos = bufs[slot];
211 size_t left = sizeof(bufs[slot]);
212
213 if ((unit_min == NICESTR_B && value < 10000)
214 || unit_max == NICESTR_B) {
215 // The value is shown as bytes.
216 my_snprintf(&pos, &left, FORMAT_THOUSAND_SEP("%", "u"),
217 (unsigned int)value);
218 } else {
219 // Scale the value to a nicer unit. Unless unit_min and
220 // unit_max limit us, we will show at most five significant
221 // digits with one decimal place.
222 double d = (double)(value);
223 do {
224 d /= 1024.0;
225 ++unit;
226 } while (unit < unit_min || (d > 9999.9 && unit < unit_max));
227
228 my_snprintf(&pos, &left, FORMAT_THOUSAND_SEP("%", ".1f"), d);
229 }
230
231 static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" };
232 my_snprintf(&pos, &left, " %s", suffix[unit]);
233
234 if (always_also_bytes && value >= 10000)
235 snprintf(pos, left, FORMAT_THOUSAND_SEP(" (%", PRIu64 " B)"),
236 value);
237
238 return bufs[slot];
239 }
240
241
242 extern void
my_snprintf(char ** pos,size_t * left,const char * fmt,...)243 my_snprintf(char **pos, size_t *left, const char *fmt, ...)
244 {
245 va_list ap;
246 va_start(ap, fmt);
247 const int len = vsnprintf(*pos, *left, fmt, ap);
248 va_end(ap);
249
250 // If an error occurred, we want the caller to think that the whole
251 // buffer was used. This way no more data will be written to the
252 // buffer. We don't need better error handling here, although it
253 // is possible that the result looks garbage on the terminal if
254 // e.g. an UTF-8 character gets split. That shouldn't (easily)
255 // happen though, because the buffers used have some extra room.
256 if (len < 0 || (size_t)(len) >= *left) {
257 *left = 0;
258 } else {
259 *pos += len;
260 *left -= (size_t)(len);
261 }
262
263 return;
264 }
265
266
267 extern bool
is_tty(int fd)268 is_tty(int fd)
269 {
270 #if defined(_WIN32) && !defined(__CYGWIN__)
271 // There is no need to check if handle == INVALID_HANDLE_VALUE
272 // because it will return false anyway when used in GetConsoleMode().
273 // The resulting HANDLE is owned by the file descriptor.
274 // The HANDLE must not be closed here.
275 intptr_t handle = _get_osfhandle(fd);
276 DWORD mode;
277
278 // GetConsoleMode() is an easy way to tell if the HANDLE is a
279 // console or not. We do not care about the value of mode since we
280 // do not plan to use any further Windows console functions.
281 return GetConsoleMode((HANDLE)handle, &mode);
282 #else
283 return isatty(fd);
284 #endif
285 }
286
287
288 extern bool
is_tty_stdin(void)289 is_tty_stdin(void)
290 {
291 const bool ret = is_tty(STDIN_FILENO);
292
293 if (ret)
294 message_error(_("Compressed data cannot be read from "
295 "a terminal"));
296
297 return ret;
298 }
299
300
301 extern bool
is_tty_stdout(void)302 is_tty_stdout(void)
303 {
304 const bool ret = is_tty(STDOUT_FILENO);
305
306 if (ret)
307 message_error(_("Compressed data cannot be written to "
308 "a terminal"));
309
310 return ret;
311 }
312