/* 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;
}