1207753Smm/////////////////////////////////////////////////////////////////////////////// 2207753Smm// 3207753Smm/// \file util.c 4207753Smm/// \brief Miscellaneous utility functions 5207753Smm// 6207753Smm// Author: Lasse Collin 7207753Smm// 8207753Smm// This file has been put into the public domain. 9207753Smm// You can do whatever you want with this file. 10207753Smm// 11207753Smm/////////////////////////////////////////////////////////////////////////////// 12207753Smm 13207753Smm#include "private.h" 14207753Smm#include <stdarg.h> 15207753Smm 16207753Smm 17213700Smm/// Buffers for uint64_to_str() and uint64_to_nicestr() 18213700Smmstatic char bufs[4][128]; 19213700Smm 20213700Smm/// Thousand separator support in uint64_to_str() and uint64_to_nicestr() 21213700Smmstatic enum { UNKNOWN, WORKS, BROKEN } thousand = UNKNOWN; 22213700Smm 23213700Smm 24207753Smmextern void * 25207753Smmxrealloc(void *ptr, size_t size) 26207753Smm{ 27207753Smm assert(size > 0); 28207753Smm 29262754Sdelphij // Save ptr so that we can free it if realloc fails. 30262754Sdelphij // The point is that message_fatal ends up calling stdio functions 31262754Sdelphij // which in some libc implementations might allocate memory from 32262754Sdelphij // the heap. Freeing ptr improves the chances that there's free 33262754Sdelphij // memory for stdio functions if they need it. 34262754Sdelphij void *p = ptr; 35207753Smm ptr = realloc(ptr, size); 36207753Smm 37262754Sdelphij if (ptr == NULL) { 38262754Sdelphij const int saved_errno = errno; 39262754Sdelphij free(p); 40262754Sdelphij message_fatal("%s", strerror(saved_errno)); 41262754Sdelphij } 42262754Sdelphij 43207753Smm return ptr; 44207753Smm} 45207753Smm 46207753Smm 47207753Smmextern char * 48207753Smmxstrdup(const char *src) 49207753Smm{ 50207753Smm assert(src != NULL); 51207753Smm const size_t size = strlen(src) + 1; 52207753Smm char *dest = xmalloc(size); 53207753Smm return memcpy(dest, src, size); 54207753Smm} 55207753Smm 56207753Smm 57207753Smmextern uint64_t 58207753Smmstr_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max) 59207753Smm{ 60207753Smm uint64_t result = 0; 61207753Smm 62207753Smm // Skip blanks. 63207753Smm while (*value == ' ' || *value == '\t') 64207753Smm ++value; 65207753Smm 66207753Smm // Accept special value "max". Supporting "min" doesn't seem useful. 67207753Smm if (strcmp(value, "max") == 0) 68207753Smm return max; 69207753Smm 70207753Smm if (*value < '0' || *value > '9') 71207753Smm message_fatal(_("%s: Value is not a non-negative " 72207753Smm "decimal integer"), value); 73207753Smm 74207753Smm do { 75207753Smm // Don't overflow. 76213700Smm if (result > UINT64_MAX / 10) 77207753Smm goto error; 78207753Smm 79207753Smm result *= 10; 80213700Smm 81213700Smm // Another overflow check 82360523Sdelphij const uint32_t add = (uint32_t)(*value - '0'); 83213700Smm if (UINT64_MAX - add < result) 84213700Smm goto error; 85213700Smm 86213700Smm result += add; 87207753Smm ++value; 88207753Smm } while (*value >= '0' && *value <= '9'); 89207753Smm 90207753Smm if (*value != '\0') { 91207753Smm // Look for suffix. Originally this supported both base-2 92207753Smm // and base-10, but since there seems to be little need 93207753Smm // for base-10 in this program, treat everything as base-2 94207753Smm // and also be more relaxed about the case of the first 95207753Smm // letter of the suffix. 96207753Smm uint64_t multiplier = 0; 97207753Smm if (*value == 'k' || *value == 'K') 98207753Smm multiplier = UINT64_C(1) << 10; 99207753Smm else if (*value == 'm' || *value == 'M') 100207753Smm multiplier = UINT64_C(1) << 20; 101207753Smm else if (*value == 'g' || *value == 'G') 102207753Smm multiplier = UINT64_C(1) << 30; 103207753Smm 104207753Smm ++value; 105207753Smm 106207753Smm // Allow also e.g. Ki, KiB, and KB. 107207753Smm if (*value != '\0' && strcmp(value, "i") != 0 108207753Smm && strcmp(value, "iB") != 0 109207753Smm && strcmp(value, "B") != 0) 110207753Smm multiplier = 0; 111207753Smm 112207753Smm if (multiplier == 0) { 113207753Smm message(V_ERROR, _("%s: Invalid multiplier suffix"), 114207753Smm value - 1); 115207753Smm message_fatal(_("Valid suffixes are `KiB' (2^10), " 116207753Smm "`MiB' (2^20), and `GiB' (2^30).")); 117207753Smm } 118207753Smm 119207753Smm // Don't overflow here either. 120207753Smm if (result > UINT64_MAX / multiplier) 121207753Smm goto error; 122207753Smm 123207753Smm result *= multiplier; 124207753Smm } 125207753Smm 126207753Smm if (result < min || result > max) 127207753Smm goto error; 128207753Smm 129207753Smm return result; 130207753Smm 131207753Smmerror: 132207753Smm message_fatal(_("Value of the option `%s' must be in the range " 133207753Smm "[%" PRIu64 ", %" PRIu64 "]"), 134207753Smm name, min, max); 135207753Smm} 136207753Smm 137207753Smm 138207753Smmextern uint64_t 139207753Smmround_up_to_mib(uint64_t n) 140207753Smm{ 141207753Smm return (n >> 20) + ((n & ((UINT32_C(1) << 20) - 1)) != 0); 142207753Smm} 143207753Smm 144207753Smm 145360523Sdelphij/// Check if thousands separator is supported. Run-time checking is easiest 146360523Sdelphij/// because it seems to be sometimes lacking even on a POSIXish system. 147360523Sdelphij/// Note that trying to use thousands separators when snprintf() doesn't 148360523Sdelphij/// support them results in undefined behavior. This just has happened to 149360523Sdelphij/// work well enough in practice. 150360523Sdelphij/// 151360523Sdelphij/// DJGPP 2.05 added support for thousands separators but it's broken 152360523Sdelphij/// at least under WinXP with Finnish locale that uses a non-breaking space 153360523Sdelphij/// as the thousands separator. Workaround by disabling thousands separators 154360523Sdelphij/// for DJGPP builds. 155213700Smmstatic void 156213700Smmcheck_thousand_sep(uint32_t slot) 157207753Smm{ 158207753Smm if (thousand == UNKNOWN) { 159207753Smm bufs[slot][0] = '\0'; 160360523Sdelphij#ifndef __DJGPP__ 161213700Smm snprintf(bufs[slot], sizeof(bufs[slot]), "%'u", 1U); 162360523Sdelphij#endif 163207753Smm thousand = bufs[slot][0] == '1' ? WORKS : BROKEN; 164207753Smm } 165207753Smm 166213700Smm return; 167213700Smm} 168213700Smm 169213700Smm 170213700Smmextern const char * 171213700Smmuint64_to_str(uint64_t value, uint32_t slot) 172213700Smm{ 173213700Smm assert(slot < ARRAY_SIZE(bufs)); 174213700Smm 175213700Smm check_thousand_sep(slot); 176213700Smm 177207753Smm if (thousand == WORKS) 178207753Smm snprintf(bufs[slot], sizeof(bufs[slot]), "%'" PRIu64, value); 179207753Smm else 180207753Smm snprintf(bufs[slot], sizeof(bufs[slot]), "%" PRIu64, value); 181207753Smm 182207753Smm return bufs[slot]; 183207753Smm} 184207753Smm 185207753Smm 186207753Smmextern const char * 187207753Smmuint64_to_nicestr(uint64_t value, enum nicestr_unit unit_min, 188207753Smm enum nicestr_unit unit_max, bool always_also_bytes, 189207753Smm uint32_t slot) 190207753Smm{ 191207753Smm assert(unit_min <= unit_max); 192207753Smm assert(unit_max <= NICESTR_TIB); 193213700Smm assert(slot < ARRAY_SIZE(bufs)); 194207753Smm 195213700Smm check_thousand_sep(slot); 196213700Smm 197207753Smm enum nicestr_unit unit = NICESTR_B; 198213700Smm char *pos = bufs[slot]; 199213700Smm size_t left = sizeof(bufs[slot]); 200207753Smm 201207753Smm if ((unit_min == NICESTR_B && value < 10000) 202207753Smm || unit_max == NICESTR_B) { 203207753Smm // The value is shown as bytes. 204213700Smm if (thousand == WORKS) 205213700Smm my_snprintf(&pos, &left, "%'u", (unsigned int)value); 206213700Smm else 207213700Smm my_snprintf(&pos, &left, "%u", (unsigned int)value); 208207753Smm } else { 209207753Smm // Scale the value to a nicer unit. Unless unit_min and 210207753Smm // unit_max limit us, we will show at most five significant 211207753Smm // digits with one decimal place. 212207753Smm double d = (double)(value); 213207753Smm do { 214207753Smm d /= 1024.0; 215207753Smm ++unit; 216207753Smm } while (unit < unit_min || (d > 9999.9 && unit < unit_max)); 217207753Smm 218213700Smm if (thousand == WORKS) 219213700Smm my_snprintf(&pos, &left, "%'.1f", d); 220213700Smm else 221213700Smm my_snprintf(&pos, &left, "%.1f", d); 222207753Smm } 223207753Smm 224207753Smm static const char suffix[5][4] = { "B", "KiB", "MiB", "GiB", "TiB" }; 225213700Smm my_snprintf(&pos, &left, " %s", suffix[unit]); 226207753Smm 227213700Smm if (always_also_bytes && value >= 10000) { 228213700Smm if (thousand == WORKS) 229213700Smm snprintf(pos, left, " (%'" PRIu64 " B)", value); 230213700Smm else 231213700Smm snprintf(pos, left, " (%" PRIu64 " B)", value); 232207753Smm } 233207753Smm 234213700Smm return bufs[slot]; 235207753Smm} 236207753Smm 237207753Smm 238207753Smmextern void 239207753Smmmy_snprintf(char **pos, size_t *left, const char *fmt, ...) 240207753Smm{ 241207753Smm va_list ap; 242207753Smm va_start(ap, fmt); 243207753Smm const int len = vsnprintf(*pos, *left, fmt, ap); 244207753Smm va_end(ap); 245207753Smm 246207753Smm // If an error occurred, we want the caller to think that the whole 247207753Smm // buffer was used. This way no more data will be written to the 248213700Smm // buffer. We don't need better error handling here, although it 249213700Smm // is possible that the result looks garbage on the terminal if 250213700Smm // e.g. an UTF-8 character gets split. That shouldn't (easily) 251213700Smm // happen though, because the buffers used have some extra room. 252207753Smm if (len < 0 || (size_t)(len) >= *left) { 253207753Smm *left = 0; 254207753Smm } else { 255207753Smm *pos += len; 256360523Sdelphij *left -= (size_t)(len); 257207753Smm } 258207753Smm 259207753Smm return; 260207753Smm} 261207753Smm 262207753Smm 263207753Smmextern bool 264207753Smmis_empty_filename(const char *filename) 265207753Smm{ 266207753Smm if (filename[0] == '\0') { 267207753Smm message_error(_("Empty filename, skipping")); 268207753Smm return true; 269207753Smm } 270207753Smm 271207753Smm return false; 272207753Smm} 273207753Smm 274207753Smm 275207753Smmextern bool 276207753Smmis_tty_stdin(void) 277207753Smm{ 278207753Smm const bool ret = isatty(STDIN_FILENO); 279207753Smm 280207753Smm if (ret) 281207753Smm message_error(_("Compressed data cannot be read from " 282207753Smm "a terminal")); 283207753Smm 284207753Smm return ret; 285207753Smm} 286207753Smm 287207753Smm 288207753Smmextern bool 289207753Smmis_tty_stdout(void) 290207753Smm{ 291207753Smm const bool ret = isatty(STDOUT_FILENO); 292207753Smm 293207753Smm if (ret) 294207753Smm message_error(_("Compressed data cannot be written to " 295207753Smm "a terminal")); 296207753Smm 297207753Smm return ret; 298207753Smm} 299