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