xref: /freebsd/contrib/xz/src/xz/util.c (revision 128836d304d93f2d00eb14069c27089ab46c38d4)
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