Index: src/xz/util.c |
=================================================================== |
--- src/xz/util.c (revision 50504) |
+++ src/xz/util.c (working copy) |
@@ -14,6 +14,13 @@ |
#include <stdarg.h> |
+/// Buffers for uint64_to_str() and uint64_to_nicestr() |
+static char bufs[4][128]; |
+ |
+/// Thousand separator support in uint64_to_str() and uint64_to_nicestr() |
+static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; |
+ |
+ |
extern void * |
xrealloc(void *ptr, size_t size) |
{ |
@@ -125,22 +132,28 @@ |
} |
-extern const char * |
-uint64_to_str(uint64_t value, uint32_t slot) |
+/// Check if thousand separator is supported. Run-time checking is easiest, |
+/// because it seems to be sometimes lacking even on POSIXish system. |
+static void |
+check_thousand_sep(uint32_t slot) |
{ |
- // 2^64 with thousand separators is 26 bytes plus trailing '\0'. |
- static char bufs[4][32]; |
- |
- assert(slot < ARRAY_SIZE(bufs)); |
- |
- static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; |
if (thousand == UNKNOWN) { |
bufs[slot][0] = '\0'; |
- snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, |
- UINT64_C(1)); |
+ snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U); |
thousand = bufs[slot][0] == '1' ? WORKS : BROKEN; |
} |
+ return; |
+} |
+ |
+ |
+extern const char * |
+uint64_to_str(uint64_t value, uint32_t slot) |
+{ |
+ assert(slot < ARRAY_SIZE(bufs)); |
+ |
+ check_thousand_sep(slot); |
+ |
if (thousand == WORKS) |
snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, value); |
else |
@@ -157,14 +170,21 @@ |
{ |
assert(unit_min <= unit_max); |
assert(unit_max <= NICESTR_TIB); |
+ assert(slot < ARRAY_SIZE(bufs)); |
+ check_thousand_sep(slot); |
+ |
enum nicestr_unit unit = NICESTR_B; |
- const char *str; |
+ char *pos = bufs[slot]; |
+ size_t left = sizeof(bufs[slot]); |
if ((unit_min == NICESTR_B && value < 10000) |
|| unit_max == NICESTR_B) { |
// The value is shown as bytes. |
- str = uint64_to_str(value, slot); |
+ if (thousand == WORKS) |
+ my_snprintf(&pos, &left, "%'u", (unsigned int)value); |
+ else |
+ my_snprintf(&pos, &left, "%u", (unsigned int)value); |
} else { |
// Scale the value to a nicer unit. Unless unit_min and |
// unit_max limit us, we will show at most five significant |
@@ -175,49 +195,23 @@ |
++unit; |
} while (unit < unit_min || (d > 9999.9 && unit < unit_max)); |
- str = double_to_str(d); |
+ if (thousand == WORKS) |
+ my_snprintf(&pos, &left, "%'.1f", d); |
+ else |
+ my_snprintf(&pos, &left, "%.1f", d); |
} |
static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" }; |
+ my_snprintf(&pos, &left, " %s", suffix[unit]); |
- // Minimum buffer size: |
- // 26 2^64 with thousand separators |
- // 4 " KiB" |
- // 2 " (" |
- // 26 2^64 with thousand separators |
- // 3 " B)" |
- // 1 '\0' |
- // 62 Total |
- static char buf[4][64]; |
- char *pos = buf[slot]; |
- size_t left = sizeof(buf[slot]); |
- my_snprintf(&pos, &left, "%s %s", str, suffix[unit]); |
- |
- if (always_also_bytes && value >= 10000) |
- snprintf(pos, left, " (%s B)", uint64_to_str(value, slot)); |
- |
- return buf[slot]; |
-} |
- |
- |
-extern const char * |
-double_to_str(double value) |
-{ |
- static char buf[64]; |
- |
- static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; |
- if (thousand == UNKNOWN) { |
- buf[0] = '\0'; |
- snprintf(buf, sizeof(buf), "%'.1f", 2.0); |
- thousand = buf[0] == '2' ? WORKS : BROKEN; |
+ if (always_also_bytes && value >= 10000) { |
+ if (thousand == WORKS) |
+ snprintf(pos, left, " (%'" PRIu64 " B)", value); |
+ else |
+ snprintf(pos, left, " (%" PRIu64 " B)", value); |
} |
- if (thousand == WORKS) |
- snprintf(buf, sizeof(buf), "%'.1f", value); |
- else |
- snprintf(buf, sizeof(buf), "%.1f", value); |
- |
- return buf; |
+ return bufs[slot]; |
} |
@@ -231,7 +225,10 @@ |
// If an error occurred, we want the caller to think that the whole |
// buffer was used. This way no more data will be written to the |
- // buffer. We don't need better error handling here. |
+ // buffer. We don't need better error handling here, although it |
+ // is possible that the result looks garbage on the terminal if |
+ // e.g. an UTF-8 character gets split. That shouldn't (easily) |
+ // happen though, because the buffers used have some extra room. |
if (len < 0 || (size_t)(len) >= *left) { |
*left = 0; |
} else { |
@@ -243,45 +240,6 @@ |
} |
-/* |
-/// \brief Simple quoting to get rid of ASCII control characters |
-/// |
-/// This is not so cool and locale-dependent, but should be good enough |
-/// At least we don't print any control characters on the terminal. |
-/// |
-extern char * |
-str_quote(const char *str) |
-{ |
- size_t dest_len = 0; |
- bool has_ctrl = false; |
- |
- while (str[dest_len] != '\0') |
- if (*(unsigned char *)(str + dest_len++) < 0x20) |
- has_ctrl = true; |
- |
- char *dest = malloc(dest_len + 1); |
- if (dest != NULL) { |
- if (has_ctrl) { |
- for (size_t i = 0; i < dest_len; ++i) |
- if (*(unsigned char *)(str + i) < 0x20) |
- dest[i] = '?'; |
- else |
- dest[i] = str[i]; |
- |
- dest[dest_len] = '\0'; |
- |
- } else { |
- // Usually there are no control characters, |
- // so we can optimize. |
- memcpy(dest, str, dest_len + 1); |
- } |
- } |
- |
- return dest; |
-} |
-*/ |
- |
- |
extern bool |
is_empty_filename(const char *filename) |
{ |