/* See LICENSE file for copyright and license details. */ #include "common.h" static const char *units_big = "KMGTPEZYRQ"; static const char *units_small = "munpfazyrq"; /* 'u' should be "µ" */ const char * humansize1000(off_t s, char *buf) { size_t unit = 0; if (s < 1000) { sprintf(buf, "%u B", (unsigned)s); return buf; } s /= 100; while (units_big[unit + 1U] && s >= 10000) { s /= 1000; unit++; } sprintf(buf, "%u.%u %cB", (unsigned)s / 10U, (unsigned)s % 10U, units_big[unit]); return buf; } const char * humansize1024(off_t s, char *buf) { size_t unit = 0; if (s < 1024) { sprintf(buf, "%u B", (unsigned)s); return buf; } while (units_big[unit + 1U] && s >= 1024 * 1024) { s /= 1024; unit++; } sprintf(buf, "%lu.%lu %ciB", (unsigned long int)s / 1024UL, (unsigned long int)(s * 10 % 10240) / 1024UL, units_big[unit]); return buf; } off_t unhumansize(const char *s, char flag) { off_t sum = 0, term, digit, divisor, power, base; if (!isdigit(s[0]) && !(s[0] == '-' && isdigit(s[1]))) eprintf("value of -%c is not a file size", flag); do { divisor = 1; term = 0; while (isdigit(*s)) { digit = (*s++ & 15); if (term > (OFF_MAX - digit) / 10) eprintf("value of -%c option is too large", flag); term = term * 10 + digit; } if (*s == '.') { s++; while (isdigit(*s)) { digit = (*s++ & 15); if (term > (OFF_MAX - digit) / 10) eprintf("value of -%c option is too large", flag); term = term * 10 + digit; divisor *= 10; } } power = 0; switch (*s) { case 'Q': power++; /* fall through */ case 'R': power++; /* fall through */ case 'Y': power++; /* fall through */ case 'Z': power++; /* fall through */ case 'E': power++; /* fall through */ case 'P': power++; /* fall through */ case 'T': power++; /* fall through */ case 'G': power++; /* fall through */ case 'M': power++; /* fall through */ case 'k': case 'K': power++; if (s[1] == 'i' || s[2] == 'B') { base = 1024; s = &s[3]; } else if (s[1] == 'B') { base = 1000; s = &s[2]; } else { base = 1024; s = &s[1]; } while (power) { term *= base; power--; } break; case 'B': s++; /* fall through */ default: if (divisor > 1) eprintf("value of -%c contains non-integer exact byte count", flag); break; } sum += term /= divisor; while (*s == ' ' || *s == ',' || *s == '+') s++; } while (isdigit(s[0]) || (s[0] == '.' && isdigit(s[1]))); return sum; } const char * durationstr(const struct timespec *dur, char *buf, int second_decimals) { uintmax_t ss, s, m, h, d, ss_div = (uintmax_t)WHOLE_SECOND; char *p; const char *unit; int i; if (dur->tv_sec < 0 || dur->tv_nsec < 0) return "-"; if (second_decimals < 0) second_decimals = 0; else if (second_decimals > 9) second_decimals = 9; for (i = 0; i < second_decimals; i++) ss_div /= 10U; ss = (uintmax_t)dur->tv_nsec / ss_div; s = (uintmax_t)dur->tv_sec % 60U; m = (uintmax_t)dur->tv_sec / 60U % 60U; h = (uintmax_t)dur->tv_sec / 60U / 60U % 24U; d = (uintmax_t)dur->tv_sec / 60U / 60U / 24U; p = buf; if (d) p += sprintf(p, "%ju days, ", d); if (h) { p += sprintf(p, "%ju:%02ju:%02ju", h, m, s); unit = "hours"; } else if (m) { p += sprintf(p, "%ju:%02ju", m, s); unit = "minutes"; } else { p += sprintf(p, "%ju", s); unit = "seconds"; } if (second_decimals) p += sprintf(p, ".%0*ju", second_decimals, ss); p += sprintf(p, " %s", unit); return buf; } const char * humanbytespersecond(double bytes_per_second, char *buf) { int unit = -1; if (bytes_per_second < (double)0.01f) { do { bytes_per_second *= 1000; unit++; } while (units_small[unit + 1] && bytes_per_second < (double)0.01f); if (units_small[unit] == 'u' && have_micro_symbol()) sprintf(buf, "%.02lf µB/s", bytes_per_second); else sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_small[unit]); } else { while (units_big[unit + 1] && bytes_per_second >= 1000) { bytes_per_second /= 1000; unit++; } if (unit < 0) sprintf(buf, "%.02lf B/s", bytes_per_second); else sprintf(buf, "%.02lf %cB/s", bytes_per_second, units_big[unit]); } return buf; } const char * exact_and_human_size(off_t bytes, char *buf, int with_unit) { char *p = buf; p += sprintf(p, "%ji%s", (intmax_t)bytes, !with_unit ? "" : bytes == 1 ? " byte" : " bytes"); if (bytes >= 1024) { p = stpcpy(p, " ("); humansize1000(bytes, p); p = stpcpy(strchr(p, '\0'), ", "); humansize1024(bytes, p); p = stpcpy(strchr(p, '\0'), ")"); } return buf; }