1207753Smm///////////////////////////////////////////////////////////////////////////////
2207753Smm//
3207753Smm/// \file       util.c
4207753Smm/// \brief      Miscellaneous utility functions
5207753Smm//
6207753Smm//  Author:     Lasse Collin
7207753Smm//
8207753Smm//  This file has been put into the public domain.
9207753Smm//  You can do whatever you want with this file.
10207753Smm//
11207753Smm///////////////////////////////////////////////////////////////////////////////
12207753Smm
13207753Smm#include "private.h"
14207753Smm#include <stdarg.h>
15207753Smm
16207753Smm
17213700Smm/// Buffers for uint64_to_str() and uint64_to_nicestr()
18213700Smmstatic char bufs[4][128];
19213700Smm
20213700Smm/// Thousand separator support in uint64_to_str() and uint64_to_nicestr()
21213700Smmstatic enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN;
22213700Smm
23213700Smm
24207753Smmextern void *
25207753Smmxrealloc(void *ptr, size_t size)
26207753Smm{
27207753Smm	assert(size > 0);
28207753Smm
29262754Sdelphij	// Save ptr so that we can free it if realloc fails.
30262754Sdelphij	// The point is that message_fatal ends up calling stdio functions
31262754Sdelphij	// which in some libc implementations might allocate memory from
32262754Sdelphij	// the heap. Freeing ptr improves the chances that there's free
33262754Sdelphij	// memory for stdio functions if they need it.
34262754Sdelphij	void *p = ptr;
35207753Smm	ptr = realloc(ptr, size);
36207753Smm
37262754Sdelphij	if (ptr == NULL) {
38262754Sdelphij		const int saved_errno = errno;
39262754Sdelphij		free(p);
40262754Sdelphij		message_fatal("%s", strerror(saved_errno));
41262754Sdelphij	}
42262754Sdelphij
43207753Smm	return ptr;
44207753Smm}
45207753Smm
46207753Smm
47207753Smmextern char *
48207753Smmxstrdup(const char *src)
49207753Smm{
50207753Smm	assert(src != NULL);
51207753Smm	const size_t size = strlen(src) + 1;
52207753Smm	char *dest = xmalloc(size);
53207753Smm	return memcpy(dest, src, size);
54207753Smm}
55207753Smm
56207753Smm
57207753Smmextern uint64_t
58207753Smmstr_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max)
59207753Smm{
60207753Smm	uint64_t result = 0;
61207753Smm
62207753Smm	// Skip blanks.
63207753Smm	while (*value == ' ' || *value == '\t')
64207753Smm		++value;
65207753Smm
66207753Smm	// Accept special value "max". Supporting "min" doesn't seem useful.
67207753Smm	if (strcmp(value, "max") == 0)
68207753Smm		return max;
69207753Smm
70207753Smm	if (*value < '0' || *value > '9')
71207753Smm		message_fatal(_("%s: Value is not a non-negative "
72207753Smm				"decimal integer"), value);
73207753Smm
74207753Smm	do {
75207753Smm		// Don't overflow.
76213700Smm		if (result > UINT64_MAX / 10)
77207753Smm			goto error;
78207753Smm
79207753Smm		result *= 10;
80213700Smm
81213700Smm		// Another overflow check
82360523Sdelphij		const uint32_t add = (uint32_t)(*value - '0');
83213700Smm		if (UINT64_MAX - add < result)
84213700Smm			goto error;
85213700Smm
86213700Smm		result += add;
87207753Smm		++value;
88207753Smm	} while (*value >= '0' && *value <= '9');
89207753Smm
90207753Smm	if (*value != '\0') {
91207753Smm		// Look for suffix. Originally this supported both base-2
92207753Smm		// and base-10, but since there seems to be little need
93207753Smm		// for base-10 in this program, treat everything as base-2
94207753Smm		// and also be more relaxed about the case of the first
95207753Smm		// letter of the suffix.
96207753Smm		uint64_t multiplier = 0;
97207753Smm		if (*value == 'k' || *value == 'K')
98207753Smm			multiplier = UINT64_C(1) << 10;
99207753Smm		else if (*value == 'm' || *value == 'M')
100207753Smm			multiplier = UINT64_C(1) << 20;
101207753Smm		else if (*value == 'g' || *value == 'G')
102207753Smm			multiplier = UINT64_C(1) << 30;
103207753Smm
104207753Smm		++value;
105207753Smm
106207753Smm		// Allow also e.g. Ki, KiB, and KB.
107207753Smm		if (*value != '\0' && strcmp(value, "i") != 0
108207753Smm				&& strcmp(value, "iB") != 0
109207753Smm				&& strcmp(value, "B") != 0)
110207753Smm			multiplier = 0;
111207753Smm
112207753Smm		if (multiplier == 0) {
113207753Smm			message(V_ERROR, _("%s: Invalid multiplier suffix"),
114207753Smm					value - 1);
115207753Smm			message_fatal(_("Valid suffixes are `KiB' (2^10), "
116207753Smm					"`MiB' (2^20), and `GiB' (2^30)."));
117207753Smm		}
118207753Smm
119207753Smm		// Don't overflow here either.
120207753Smm		if (result > UINT64_MAX / multiplier)
121207753Smm			goto error;
122207753Smm
123207753Smm		result *= multiplier;
124207753Smm	}
125207753Smm
126207753Smm	if (result < min || result > max)
127207753Smm		goto error;
128207753Smm
129207753Smm	return result;
130207753Smm
131207753Smmerror:
132207753Smm	message_fatal(_("Value of the option `%s' must be in the range "
133207753Smm				"[%" PRIu64 ", %" PRIu64 "]"),
134207753Smm				name, min, max);
135207753Smm}
136207753Smm
137207753Smm
138207753Smmextern uint64_t
139207753Smmround_up_to_mib(uint64_t n)
140207753Smm{
141207753Smm	return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0);
142207753Smm}
143207753Smm
144207753Smm
145360523Sdelphij/// Check if thousands separator is supported. Run-time checking is easiest
146360523Sdelphij/// because it seems to be sometimes lacking even on a POSIXish system.
147360523Sdelphij/// Note that trying to use thousands separators when snprintf() doesn't
148360523Sdelphij/// support them results in undefined behavior. This just has happened to
149360523Sdelphij/// work well enough in practice.
150360523Sdelphij///
151360523Sdelphij/// DJGPP 2.05 added support for thousands separators but it's broken
152360523Sdelphij/// at least under WinXP with Finnish locale that uses a non-breaking space
153360523Sdelphij/// as the thousands separator. Workaround by disabling thousands separators
154360523Sdelphij/// for DJGPP builds.
155213700Smmstatic void
156213700Smmcheck_thousand_sep(uint32_t slot)
157207753Smm{
158207753Smm	if (thousand == UNKNOWN) {
159207753Smm		bufs[slot][0] = '\0';
160360523Sdelphij#ifndef __DJGPP__
161213700Smm		snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U);
162360523Sdelphij#endif
163207753Smm		thousand = bufs[slot][0] == '1' ? WORKS : BROKEN;
164207753Smm	}
165207753Smm
166213700Smm	return;
167213700Smm}
168213700Smm
169213700Smm
170213700Smmextern const char *
171213700Smmuint64_to_str(uint64_t value, uint32_t slot)
172213700Smm{
173213700Smm	assert(slot < ARRAY_SIZE(bufs));
174213700Smm
175213700Smm	check_thousand_sep(slot);
176213700Smm
177207753Smm	if (thousand == WORKS)
178207753Smm		snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, value);
179207753Smm	else
180207753Smm		snprintf(bufs[slot], sizeof(bufs[slot]), "%" PRIu64, value);
181207753Smm
182207753Smm	return bufs[slot];
183207753Smm}
184207753Smm
185207753Smm
186207753Smmextern const char *
187207753Smmuint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min,
188207753Smm		enum nicestr_unit unit_max, bool always_also_bytes,
189207753Smm		uint32_t slot)
190207753Smm{
191207753Smm	assert(unit_min <= unit_max);
192207753Smm	assert(unit_max <= NICESTR_TIB);
193213700Smm	assert(slot < ARRAY_SIZE(bufs));
194207753Smm
195213700Smm	check_thousand_sep(slot);
196213700Smm
197207753Smm	enum nicestr_unit unit = NICESTR_B;
198213700Smm	char *pos = bufs[slot];
199213700Smm	size_t left = sizeof(bufs[slot]);
200207753Smm
201207753Smm	if ((unit_min == NICESTR_B && value < 10000)
202207753Smm			|| unit_max == NICESTR_B) {
203207753Smm		// The value is shown as bytes.
204213700Smm		if (thousand == WORKS)
205213700Smm			my_snprintf(&pos, &left, "%'u", (unsigned int)value);
206213700Smm		else
207213700Smm			my_snprintf(&pos, &left, "%u", (unsigned int)value);
208207753Smm	} else {
209207753Smm		// Scale the value to a nicer unit. Unless unit_min and
210207753Smm		// unit_max limit us, we will show at most five significant
211207753Smm		// digits with one decimal place.
212207753Smm		double d = (double)(value);
213207753Smm		do {
214207753Smm			d /= 1024.0;
215207753Smm			++unit;
216207753Smm		} while (unit < unit_min || (d > 9999.9 && unit < unit_max));
217207753Smm
218213700Smm		if (thousand == WORKS)
219213700Smm			my_snprintf(&pos, &left, "%'.1f", d);
220213700Smm		else
221213700Smm			my_snprintf(&pos, &left, "%.1f", d);
222207753Smm	}
223207753Smm
224207753Smm	static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" };
225213700Smm	my_snprintf(&pos, &left, " %s", suffix[unit]);
226207753Smm
227213700Smm	if (always_also_bytes && value >= 10000) {
228213700Smm		if (thousand == WORKS)
229213700Smm			snprintf(pos, left, " (%'" PRIu64 " B)", value);
230213700Smm		else
231213700Smm			snprintf(pos, left, " (%" PRIu64 " B)", value);
232207753Smm	}
233207753Smm
234213700Smm	return bufs[slot];
235207753Smm}
236207753Smm
237207753Smm
238207753Smmextern void
239207753Smmmy_snprintf(char **pos, size_t *left, const char *fmt, ...)
240207753Smm{
241207753Smm	va_list ap;
242207753Smm	va_start(ap, fmt);
243207753Smm	const int len = vsnprintf(*pos, *left, fmt, ap);
244207753Smm	va_end(ap);
245207753Smm
246207753Smm	// If an error occurred, we want the caller to think that the whole
247207753Smm	// buffer was used. This way no more data will be written to the
248213700Smm	// buffer. We don't need better error handling here, although it
249213700Smm	// is possible that the result looks garbage on the terminal if
250213700Smm	// e.g. an UTF-8 character gets split. That shouldn't (easily)
251213700Smm	// happen though, because the buffers used have some extra room.
252207753Smm	if (len < 0 || (size_t)(len) >= *left) {
253207753Smm		*left = 0;
254207753Smm	} else {
255207753Smm		*pos += len;
256360523Sdelphij		*left -= (size_t)(len);
257207753Smm	}
258207753Smm
259207753Smm	return;
260207753Smm}
261207753Smm
262207753Smm
263207753Smmextern bool
264207753Smmis_empty_filename(const char *filename)
265207753Smm{
266207753Smm	if (filename[0] == '\0') {
267207753Smm		message_error(_("Empty filename, skipping"));
268207753Smm		return true;
269207753Smm	}
270207753Smm
271207753Smm	return false;
272207753Smm}
273207753Smm
274207753Smm
275207753Smmextern bool
276207753Smmis_tty_stdin(void)
277207753Smm{
278207753Smm	const bool ret = isatty(STDIN_FILENO);
279207753Smm
280207753Smm	if (ret)
281207753Smm		message_error(_("Compressed data cannot be read from "
282207753Smm				"a terminal"));
283207753Smm
284207753Smm	return ret;
285207753Smm}
286207753Smm
287207753Smm
288207753Smmextern bool
289207753Smmis_tty_stdout(void)
290207753Smm{
291207753Smm	const bool ret = isatty(STDOUT_FILENO);
292207753Smm
293207753Smm	if (ret)
294207753Smm		message_error(_("Compressed data cannot be written to "
295207753Smm				"a terminal"));
296207753Smm
297207753Smm	return ret;
298207753Smm}
299