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 = (uint32_t)(*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 thousands separator is supported. Run-time checking is easiest 146 /// because it seems to be sometimes lacking even on a POSIXish system. 147 /// Note that trying to use thousands separators when snprintf() doesn't 148 /// support them results in undefined behavior. This just has happened to 149 /// work well enough in practice. 150 /// 151 /// DJGPP 2.05 added support for thousands separators but it's broken 152 /// at least under WinXP with Finnish locale that uses a non-breaking space 153 /// as the thousands separator. Workaround by disabling thousands separators 154 /// for DJGPP builds. 155 static void 156 check_thousand_sep(uint32_t slot) 157 { 158 if (thousand == UNKNOWN) { 159 bufs[slot][0] = '\0'; 160 #ifndef __DJGPP__ 161 snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U); 162 #endif 163 thousand = bufs[slot][0] == '1' ? WORKS : BROKEN; 164 } 165 166 return; 167 } 168 169 170 extern const char * 171 uint64_to_str(uint64_t value, uint32_t slot) 172 { 173 assert(slot < ARRAY_SIZE(bufs)); 174 175 check_thousand_sep(slot); 176 177 if (thousand == WORKS) 178 snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, value); 179 else 180 snprintf(bufs[slot], sizeof(bufs[slot]), "%" PRIu64, value); 181 182 return bufs[slot]; 183 } 184 185 186 extern const char * 187 uint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min, 188 enum nicestr_unit unit_max, bool always_also_bytes, 189 uint32_t slot) 190 { 191 assert(unit_min <= unit_max); 192 assert(unit_max <= NICESTR_TIB); 193 assert(slot < ARRAY_SIZE(bufs)); 194 195 check_thousand_sep(slot); 196 197 enum nicestr_unit unit = NICESTR_B; 198 char *pos = bufs[slot]; 199 size_t left = sizeof(bufs[slot]); 200 201 if ((unit_min == NICESTR_B && value < 10000) 202 || unit_max == NICESTR_B) { 203 // The value is shown as bytes. 204 if (thousand == WORKS) 205 my_snprintf(&pos, &left, "%'u", (unsigned int)value); 206 else 207 my_snprintf(&pos, &left, "%u", (unsigned int)value); 208 } else { 209 // Scale the value to a nicer unit. Unless unit_min and 210 // unit_max limit us, we will show at most five significant 211 // digits with one decimal place. 212 double d = (double)(value); 213 do { 214 d /= 1024.0; 215 ++unit; 216 } while (unit < unit_min || (d > 9999.9 && unit < unit_max)); 217 218 if (thousand == WORKS) 219 my_snprintf(&pos, &left, "%'.1f", d); 220 else 221 my_snprintf(&pos, &left, "%.1f", d); 222 } 223 224 static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" }; 225 my_snprintf(&pos, &left, " %s", suffix[unit]); 226 227 if (always_also_bytes && value >= 10000) { 228 if (thousand == WORKS) 229 snprintf(pos, left, " (%'" PRIu64 " B)", value); 230 else 231 snprintf(pos, left, " (%" PRIu64 " B)", value); 232 } 233 234 return bufs[slot]; 235 } 236 237 238 extern void 239 my_snprintf(char **pos, size_t *left, const char *fmt, ...) 240 { 241 va_list ap; 242 va_start(ap, fmt); 243 const int len = vsnprintf(*pos, *left, fmt, ap); 244 va_end(ap); 245 246 // If an error occurred, we want the caller to think that the whole 247 // buffer was used. This way no more data will be written to the 248 // buffer. We don't need better error handling here, although it 249 // is possible that the result looks garbage on the terminal if 250 // e.g. an UTF-8 character gets split. That shouldn't (easily) 251 // happen though, because the buffers used have some extra room. 252 if (len < 0 || (size_t)(len) >= *left) { 253 *left = 0; 254 } else { 255 *pos += len; 256 *left -= (size_t)(len); 257 } 258 259 return; 260 } 261 262 263 extern bool 264 is_empty_filename(const char *filename) 265 { 266 if (filename[0] == '\0') { 267 message_error(_("Empty filename, skipping")); 268 return true; 269 } 270 271 return false; 272 } 273 274 275 extern bool 276 is_tty_stdin(void) 277 { 278 const bool ret = isatty(STDIN_FILENO); 279 280 if (ret) 281 message_error(_("Compressed data cannot be read from " 282 "a terminal")); 283 284 return ret; 285 } 286 287 288 extern bool 289 is_tty_stdout(void) 290 { 291 const bool ret = isatty(STDOUT_FILENO); 292 293 if (ret) 294 message_error(_("Compressed data cannot be written to " 295 "a terminal")); 296 297 return ret; 298 } 299