1 ///////////////////////////////////////////////////////////////////////////////
4 /// \brief Miscellaneous utility functions
6 // Author: Lasse Collin
8 // This file has been put into the public domain.
9 // You can do whatever you want with this file.
11 ///////////////////////////////////////////////////////////////////////////////
17 /// Buffers for uint64_to_str() and uint64_to_nicestr()
18 static char bufs
[4][128];
20 /// Thousand separator support in uint64_to_str() and uint64_to_nicestr()
21 static enum { UNKNOWN
, WORKS
, BROKEN
} thousand
= UNKNOWN
;
25 xrealloc(void *ptr
, size_t size
)
29 ptr
= realloc(ptr
, size
);
31 message_fatal("%s", strerror(errno
));
38 xstrdup(const char *src
)
41 const size_t size
= strlen(src
) + 1;
42 char *dest
= xmalloc(size
);
43 return memcpy(dest
, src
, size
);
48 str_to_uint64(const char *name
, const char *value
, uint64_t min
, uint64_t max
)
53 while (*value
== ' ' || *value
== '\t')
56 // Accept special value "max". Supporting "min" doesn't seem useful.
57 if (strcmp(value
, "max") == 0)
60 if (*value
< '0' || *value
> '9')
61 message_fatal(_("%s: Value is not a non-negative "
62 "decimal integer"), value
);
66 if (result
> UINT64_MAX
/ 10)
71 // Another overflow check
72 const uint32_t add
= *value
- '0';
73 if (UINT64_MAX
- add
< result
)
78 } while (*value
>= '0' && *value
<= '9');
81 // Look for suffix. Originally this supported both base-2
82 // and base-10, but since there seems to be little need
83 // for base-10 in this program, treat everything as base-2
84 // and also be more relaxed about the case of the first
85 // letter of the suffix.
86 uint64_t multiplier
= 0;
87 if (*value
== 'k' || *value
== 'K')
88 multiplier
= UINT64_C(1) << 10;
89 else if (*value
== 'm' || *value
== 'M')
90 multiplier
= UINT64_C(1) << 20;
91 else if (*value
== 'g' || *value
== 'G')
92 multiplier
= UINT64_C(1) << 30;
96 // Allow also e.g. Ki, KiB, and KB.
97 if (*value
!= '\0' && strcmp(value
, "i") != 0
98 && strcmp(value
, "iB") != 0
99 && strcmp(value
, "B") != 0)
102 if (multiplier
== 0) {
103 message(V_ERROR
, _("%s: Invalid multiplier suffix"),
105 message_fatal(_("Valid suffixes are `KiB' (2^10), "
106 "`MiB' (2^20), and `GiB' (2^30)."));
109 // Don't overflow here either.
110 if (result
> UINT64_MAX
/ multiplier
)
113 result
*= multiplier
;
116 if (result
< min
|| result
> max
)
122 message_fatal(_("Value of the option `%s' must be in the range "
123 "[%" PRIu64
", %" PRIu64
"]"),
129 round_up_to_mib(uint64_t n
)
131 return (n
>> 20) + ((n
& ((UINT32_C(1) << 20) - 1)) != 0);
135 /// Check if thousand separator is supported. Run-time checking is easiest,
136 /// because it seems to be sometimes lacking even on POSIXish system.
138 check_thousand_sep(uint32_t slot
)
140 if (thousand
== UNKNOWN
) {
141 bufs
[slot
][0] = '\0';
142 snprintf(bufs
[slot
], sizeof(bufs
[slot
]), "%'u", 1U);
143 thousand
= bufs
[slot
][0] == '1' ? WORKS
: BROKEN
;
151 uint64_to_str(uint64_t value
, uint32_t slot
)
153 assert(slot
< ARRAY_SIZE(bufs
));
155 check_thousand_sep(slot
);
157 if (thousand
== WORKS
)
158 snprintf(bufs
[slot
], sizeof(bufs
[slot
]), "%'" PRIu64
, value
);
160 snprintf(bufs
[slot
], sizeof(bufs
[slot
]), "%" PRIu64
, value
);
167 uint64_to_nicestr(uint64_t value
, enum nicestr_unit unit_min
,
168 enum nicestr_unit unit_max
, bool always_also_bytes
,
171 assert(unit_min
<= unit_max
);
172 assert(unit_max
<= NICESTR_TIB
);
173 assert(slot
< ARRAY_SIZE(bufs
));
175 check_thousand_sep(slot
);
177 enum nicestr_unit unit
= NICESTR_B
;
178 char *pos
= bufs
[slot
];
179 size_t left
= sizeof(bufs
[slot
]);
181 if ((unit_min
== NICESTR_B
&& value
< 10000)
182 || unit_max
== NICESTR_B
) {
183 // The value is shown as bytes.
184 if (thousand
== WORKS
)
185 my_snprintf(&pos
, &left
, "%'u", (unsigned int)value
);
187 my_snprintf(&pos
, &left
, "%u", (unsigned int)value
);
189 // Scale the value to a nicer unit. Unless unit_min and
190 // unit_max limit us, we will show at most five significant
191 // digits with one decimal place.
192 double d
= (double)(value
);
196 } while (unit
< unit_min
|| (d
> 9999.9 && unit
< unit_max
));
198 if (thousand
== WORKS
)
199 my_snprintf(&pos
, &left
, "%'.1f", d
);
201 my_snprintf(&pos
, &left
, "%.1f", d
);
204 static const char suffix
[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" };
205 my_snprintf(&pos
, &left
, " %s", suffix
[unit
]);
207 if (always_also_bytes
&& value
>= 10000) {
208 if (thousand
== WORKS
)
209 snprintf(pos
, left
, " (%'" PRIu64
" B)", value
);
211 snprintf(pos
, left
, " (%" PRIu64
" B)", value
);
219 my_snprintf(char **pos
, size_t *left
, const char *fmt
, ...)
223 const int len
= vsnprintf(*pos
, *left
, fmt
, ap
);
226 // If an error occurred, we want the caller to think that the whole
227 // buffer was used. This way no more data will be written to the
228 // buffer. We don't need better error handling here, although it
229 // is possible that the result looks garbage on the terminal if
230 // e.g. an UTF-8 character gets split. That shouldn't (easily)
231 // happen though, because the buffers used have some extra room.
232 if (len
< 0 || (size_t)(len
) >= *left
) {
244 is_empty_filename(const char *filename
)
246 if (filename
[0] == '\0') {
247 message_error(_("Empty filename, skipping"));
258 const bool ret
= isatty(STDIN_FILENO
);
261 message_error(_("Compressed data cannot be read from "
271 const bool ret
= isatty(STDOUT_FILENO
);
274 message_error(_("Compressed data cannot be written to "