xref: /freebsd/contrib/xz/src/xz/util.c (revision aa1a8ff2)
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.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12 
13 #include "private.h"
14 #include <stdarg.h>
15 
16 
17 /// Buffers for uint64_to_str() and uint64_to_nicestr()
18 static char bufs[4][128];
19 
20 
21 // Thousand separator support in uint64_to_str() and uint64_to_nicestr():
22 //
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.
27 //
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)
36 
37 static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN;
38 
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)
48 {
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;
53 	}
54 
55 	return;
56 }
57 #endif
58 
59 
60 extern void *
61 xrealloc(void *ptr, size_t size)
62 {
63 	assert(size > 0);
64 
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);
72 
73 	if (ptr == NULL) {
74 		const int saved_errno = errno;
75 		free(p);
76 		message_fatal("%s", strerror(saved_errno));
77 	}
78 
79 	return ptr;
80 }
81 
82 
83 extern char *
84 xstrdup(const char *src)
85 {
86 	assert(src != NULL);
87 	const size_t size = strlen(src) + 1;
88 	char *dest = xmalloc(size);
89 	return memcpy(dest, src, size);
90 }
91 
92 
93 extern uint64_t
94 str_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max)
95 {
96 	uint64_t result = 0;
97 
98 	// Skip blanks.
99 	while (*value == ' ' || *value == '\t')
100 		++value;
101 
102 	// Accept special value "max". Supporting "min" doesn't seem useful.
103 	if (strcmp(value, "max") == 0)
104 		return max;
105 
106 	if (*value < '0' || *value > '9')
107 		message_fatal(_("%s: Value is not a non-negative "
108 				"decimal integer"), value);
109 
110 	do {
111 		// Don't overflow.
112 		if (result > UINT64_MAX / 10)
113 			goto error;
114 
115 		result *= 10;
116 
117 		// Another overflow check
118 		const uint32_t add = (uint32_t)(*value - '0');
119 		if (UINT64_MAX - add < result)
120 			goto error;
121 
122 		result += add;
123 		++value;
124 	} while (*value >= '0' && *value <= '9');
125 
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;
139 
140 		++value;
141 
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;
147 
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)."));
153 		}
154 
155 		// Don't overflow here either.
156 		if (result > UINT64_MAX / multiplier)
157 			goto error;
158 
159 		result *= multiplier;
160 	}
161 
162 	if (result < min || result > max)
163 		goto error;
164 
165 	return result;
166 
167 error:
168 	message_fatal(_("Value of the option `%s' must be in the range "
169 				"[%" PRIu64 ", %" PRIu64 "]"),
170 				name, min, max);
171 }
172 
173 
174 extern uint64_t
175 round_up_to_mib(uint64_t n)
176 {
177 	return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0);
178 }
179 
180 
181 extern const char *
182 uint64_to_str(uint64_t value, uint32_t slot)
183 {
184 	assert(slot < ARRAY_SIZE(bufs));
185 
186 	check_thousand_sep(slot);
187 
188 	snprintf(bufs[slot], sizeof(bufs[slot]),
189 			FORMAT_THOUSAND_SEP("%", PRIu64), value);
190 
191 	return bufs[slot];
192 }
193 
194 
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)
199 {
200 	assert(unit_min <= unit_max);
201 	assert(unit_max <= NICESTR_TIB);
202 	assert(slot < ARRAY_SIZE(bufs));
203 
204 	check_thousand_sep(slot);
205 
206 	enum nicestr_unit unit = NICESTR_B;
207 	char *pos = bufs[slot];
208 	size_t left = sizeof(bufs[slot]);
209 
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));
224 
225 		my_snprintf(&pos, &left, FORMAT_THOUSAND_SEP("%", ".1f"), d);
226 	}
227 
228 	static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" };
229 	my_snprintf(&pos, &left, " %s", suffix[unit]);
230 
231 	if (always_also_bytes && value >= 10000)
232 		snprintf(pos, left, FORMAT_THOUSAND_SEP(" (%", PRIu64 " B)"),
233 				value);
234 
235 	return bufs[slot];
236 }
237 
238 
239 extern void
240 my_snprintf(char **pos, size_t *left, const char *fmt, ...)
241 {
242 	va_list ap;
243 	va_start(ap, fmt);
244 	const int len = vsnprintf(*pos, *left, fmt, ap);
245 	va_end(ap);
246 
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);
258 	}
259 
260 	return;
261 }
262 
263 
264 extern bool
265 is_tty_stdin(void)
266 {
267 	const bool ret = isatty(STDIN_FILENO);
268 
269 	if (ret)
270 		message_error(_("Compressed data cannot be read from "
271 				"a terminal"));
272 
273 	return ret;
274 }
275 
276 
277 extern bool
278 is_tty_stdout(void)
279 {
280 	const bool ret = isatty(STDOUT_FILENO);
281 
282 	if (ret)
283 		message_error(_("Compressed data cannot be written to "
284 				"a terminal"));
285 
286 	return ret;
287 }
288