OLD | NEW |
(Empty) | |
| 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 extern void * |
| 18 xrealloc(void *ptr, size_t size) |
| 19 { |
| 20 assert(size > 0); |
| 21 |
| 22 ptr = realloc(ptr, size); |
| 23 if (ptr == NULL) |
| 24 message_fatal("%s", strerror(errno)); |
| 25 |
| 26 return ptr; |
| 27 } |
| 28 |
| 29 |
| 30 extern char * |
| 31 xstrdup(const char *src) |
| 32 { |
| 33 assert(src != NULL); |
| 34 const size_t size = strlen(src) + 1; |
| 35 char *dest = xmalloc(size); |
| 36 return memcpy(dest, src, size); |
| 37 } |
| 38 |
| 39 |
| 40 extern uint64_t |
| 41 str_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max) |
| 42 { |
| 43 uint64_t result = 0; |
| 44 |
| 45 // Skip blanks. |
| 46 while (*value == ' ' || *value == '\t') |
| 47 ++value; |
| 48 |
| 49 // Accept special value "max". Supporting "min" doesn't seem useful. |
| 50 if (strcmp(value, "max") == 0) |
| 51 return max; |
| 52 |
| 53 if (*value < '0' || *value > '9') |
| 54 message_fatal(_("%s: Value is not a non-negative " |
| 55 "decimal integer"), value); |
| 56 |
| 57 do { |
| 58 // Don't overflow. |
| 59 if (result > UINT64_MAX / 10) |
| 60 goto error; |
| 61 |
| 62 result *= 10; |
| 63 |
| 64 // Another overflow check |
| 65 const uint32_t add = *value - '0'; |
| 66 if (UINT64_MAX - add < result) |
| 67 goto error; |
| 68 |
| 69 result += add; |
| 70 ++value; |
| 71 } while (*value >= '0' && *value <= '9'); |
| 72 |
| 73 if (*value != '\0') { |
| 74 // Look for suffix. Originally this supported both base-2 |
| 75 // and base-10, but since there seems to be little need |
| 76 // for base-10 in this program, treat everything as base-2 |
| 77 // and also be more relaxed about the case of the first |
| 78 // letter of the suffix. |
| 79 uint64_t multiplier = 0; |
| 80 if (*value == 'k' || *value == 'K') |
| 81 multiplier = UINT64_C(1) << 10; |
| 82 else if (*value == 'm' || *value == 'M') |
| 83 multiplier = UINT64_C(1) << 20; |
| 84 else if (*value == 'g' || *value == 'G') |
| 85 multiplier = UINT64_C(1) << 30; |
| 86 |
| 87 ++value; |
| 88 |
| 89 // Allow also e.g. Ki, KiB, and KB. |
| 90 if (*value != '\0' && strcmp(value, "i") != 0 |
| 91 && strcmp(value, "iB") != 0 |
| 92 && strcmp(value, "B") != 0) |
| 93 multiplier = 0; |
| 94 |
| 95 if (multiplier == 0) { |
| 96 message(V_ERROR, _("%s: Invalid multiplier suffix"), |
| 97 value - 1); |
| 98 message_fatal(_("Valid suffixes are `KiB' (2^10), " |
| 99 "`MiB' (2^20), and `GiB' (2^30).")); |
| 100 } |
| 101 |
| 102 // Don't overflow here either. |
| 103 if (result > UINT64_MAX / multiplier) |
| 104 goto error; |
| 105 |
| 106 result *= multiplier; |
| 107 } |
| 108 |
| 109 if (result < min || result > max) |
| 110 goto error; |
| 111 |
| 112 return result; |
| 113 |
| 114 error: |
| 115 message_fatal(_("Value of the option `%s' must be in the range " |
| 116 "[%" PRIu64 ", %" PRIu64 "]"), |
| 117 name, min, max); |
| 118 } |
| 119 |
| 120 |
| 121 extern uint64_t |
| 122 round_up_to_mib(uint64_t n) |
| 123 { |
| 124 return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0); |
| 125 } |
| 126 |
| 127 |
| 128 extern const char * |
| 129 uint64_to_str(uint64_t value, uint32_t slot) |
| 130 { |
| 131 // 2^64 with thousand separators is 26 bytes plus trailing '\0'. |
| 132 static char bufs[4][32]; |
| 133 |
| 134 assert(slot < ARRAY_SIZE(bufs)); |
| 135 |
| 136 static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; |
| 137 if (thousand == UNKNOWN) { |
| 138 bufs[slot][0] = '\0'; |
| 139 snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, |
| 140 UINT64_C(1)); |
| 141 thousand = bufs[slot][0] == '1' ? WORKS : BROKEN; |
| 142 } |
| 143 |
| 144 if (thousand == WORKS) |
| 145 snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, value); |
| 146 else |
| 147 snprintf(bufs[slot], sizeof(bufs[slot]), "%" PRIu64, value); |
| 148 |
| 149 return bufs[slot]; |
| 150 } |
| 151 |
| 152 |
| 153 extern const char * |
| 154 uint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min, |
| 155 enum nicestr_unit unit_max, bool always_also_bytes, |
| 156 uint32_t slot) |
| 157 { |
| 158 assert(unit_min <= unit_max); |
| 159 assert(unit_max <= NICESTR_TIB); |
| 160 |
| 161 enum nicestr_unit unit = NICESTR_B; |
| 162 const char *str; |
| 163 |
| 164 if ((unit_min == NICESTR_B && value < 10000) |
| 165 || unit_max == NICESTR_B) { |
| 166 // The value is shown as bytes. |
| 167 str = uint64_to_str(value, slot); |
| 168 } else { |
| 169 // Scale the value to a nicer unit. Unless unit_min and |
| 170 // unit_max limit us, we will show at most five significant |
| 171 // digits with one decimal place. |
| 172 double d = (double)(value); |
| 173 do { |
| 174 d /= 1024.0; |
| 175 ++unit; |
| 176 } while (unit < unit_min || (d > 9999.9 && unit < unit_max)); |
| 177 |
| 178 str = double_to_str(d); |
| 179 } |
| 180 |
| 181 static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" }; |
| 182 |
| 183 // Minimum buffer size: |
| 184 // 26 2^64 with thousand separators |
| 185 // 4 " KiB" |
| 186 // 2 " (" |
| 187 // 26 2^64 with thousand separators |
| 188 // 3 " B)" |
| 189 // 1 '\0' |
| 190 // 62 Total |
| 191 static char buf[4][64]; |
| 192 char *pos = buf[slot]; |
| 193 size_t left = sizeof(buf[slot]); |
| 194 my_snprintf(&pos, &left, "%s %s", str, suffix[unit]); |
| 195 |
| 196 if (always_also_bytes && value >= 10000) |
| 197 snprintf(pos, left, " (%s B)", uint64_to_str(value, slot)); |
| 198 |
| 199 return buf[slot]; |
| 200 } |
| 201 |
| 202 |
| 203 extern const char * |
| 204 double_to_str(double value) |
| 205 { |
| 206 static char buf[64]; |
| 207 |
| 208 static enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; |
| 209 if (thousand == UNKNOWN) { |
| 210 buf[0] = '\0'; |
| 211 snprintf(buf, sizeof(buf), "%'.1f", 2.0); |
| 212 thousand = buf[0] == '2' ? WORKS : BROKEN; |
| 213 } |
| 214 |
| 215 if (thousand == WORKS) |
| 216 snprintf(buf, sizeof(buf), "%'.1f", value); |
| 217 else |
| 218 snprintf(buf, sizeof(buf), "%.1f", value); |
| 219 |
| 220 return buf; |
| 221 } |
| 222 |
| 223 |
| 224 extern void |
| 225 my_snprintf(char **pos, size_t *left, const char *fmt, ...) |
| 226 { |
| 227 va_list ap; |
| 228 va_start(ap, fmt); |
| 229 const int len = vsnprintf(*pos, *left, fmt, ap); |
| 230 va_end(ap); |
| 231 |
| 232 // If an error occurred, we want the caller to think that the whole |
| 233 // buffer was used. This way no more data will be written to the |
| 234 // buffer. We don't need better error handling here. |
| 235 if (len < 0 || (size_t)(len) >= *left) { |
| 236 *left = 0; |
| 237 } else { |
| 238 *pos += len; |
| 239 *left -= len; |
| 240 } |
| 241 |
| 242 return; |
| 243 } |
| 244 |
| 245 |
| 246 /* |
| 247 /// \brief Simple quoting to get rid of ASCII control characters |
| 248 /// |
| 249 /// This is not so cool and locale-dependent, but should be good enough |
| 250 /// At least we don't print any control characters on the terminal. |
| 251 /// |
| 252 extern char * |
| 253 str_quote(const char *str) |
| 254 { |
| 255 size_t dest_len = 0; |
| 256 bool has_ctrl = false; |
| 257 |
| 258 while (str[dest_len] != '\0') |
| 259 if (*(unsigned char *)(str + dest_len++) < 0x20) |
| 260 has_ctrl = true; |
| 261 |
| 262 char *dest = malloc(dest_len + 1); |
| 263 if (dest != NULL) { |
| 264 if (has_ctrl) { |
| 265 for (size_t i = 0; i < dest_len; ++i) |
| 266 if (*(unsigned char *)(str + i) < 0x20) |
| 267 dest[i] = '?'; |
| 268 else |
| 269 dest[i] = str[i]; |
| 270 |
| 271 dest[dest_len] = '\0'; |
| 272 |
| 273 } else { |
| 274 // Usually there are no control characters, |
| 275 // so we can optimize. |
| 276 memcpy(dest, str, dest_len + 1); |
| 277 } |
| 278 } |
| 279 |
| 280 return dest; |
| 281 } |
| 282 */ |
| 283 |
| 284 |
| 285 extern bool |
| 286 is_empty_filename(const char *filename) |
| 287 { |
| 288 if (filename[0] == '\0') { |
| 289 message_error(_("Empty filename, skipping")); |
| 290 return true; |
| 291 } |
| 292 |
| 293 return false; |
| 294 } |
| 295 |
| 296 |
| 297 extern bool |
| 298 is_tty_stdin(void) |
| 299 { |
| 300 const bool ret = isatty(STDIN_FILENO); |
| 301 |
| 302 if (ret) |
| 303 message_error(_("Compressed data cannot be read from " |
| 304 "a terminal")); |
| 305 |
| 306 return ret; |
| 307 } |
| 308 |
| 309 |
| 310 extern bool |
| 311 is_tty_stdout(void) |
| 312 { |
| 313 const bool ret = isatty(STDOUT_FILENO); |
| 314 |
| 315 if (ret) |
| 316 message_error(_("Compressed data cannot be written to " |
| 317 "a terminal")); |
| 318 |
| 319 return ret; |
| 320 } |
OLD | NEW |