#include <sys/types.h>
#include <assert.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <libutil.h>
static const int maxscale = 7;
int
humanize_number(char *buf, size_t len, int64_t quotient,
const char *suffix, int scale, int flags)
{
const char *prefixes, *sep;
int i, r, remainder, s1, s2, sign;
int divisordeccut;
int64_t divisor, max;
size_t baselen;
if (buf == NULL)
return (-1);
if (scale < 0)
return (-1);
else if (scale >= maxscale &&
((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
return (-1);
if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
return (-1);
if (len > 0)
buf[0] = '\0';
if (suffix == NULL)
suffix = "";
remainder = 0;
if (flags & HN_IEC_PREFIXES) {
baselen = 2;
divisor = 1024;
divisordeccut = 973;
if (flags & HN_B)
prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
else
prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
} else {
baselen = 1;
if (flags & HN_DIVISOR_1000) {
divisor = 1000;
divisordeccut = 950;
if (flags & HN_B)
prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
} else {
divisor = 1024;
divisordeccut = 973;
if (flags & HN_B)
prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
else
prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
}
}
#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
if (quotient < 0) {
sign = -1;
quotient = -quotient;
baselen += 2;
} else {
sign = 1;
baselen += 1;
}
if (flags & HN_NOSPACE)
sep = "";
else {
sep = " ";
baselen++;
}
baselen += strlen(suffix);
if (len < baselen + 1)
return (-1);
if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
for (max = 1, i = len - baselen; i-- > 0;)
max *= 10;
for (i = 0;
(quotient >= max || (quotient == max - 1 &&
remainder >= divisordeccut)) && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
if (scale & HN_GETSCALE)
return (i);
} else {
for (i = 0; i < scale && i < maxscale; i++) {
remainder = quotient % divisor;
quotient /= divisor;
}
}
r = snprintf(buf, len, "%" PRId64 "%s%s%s",
sign * (quotient + (remainder + divisor / 2) / divisor),
sep, SCALE2PREFIX(i), suffix);
if ((flags & HN_FRACTIONAL) && (u_int)r + 3 <= len && i) {
int64_t frac;
int n;
n = (int)len - r - 2;
frac = 1;
if (n > 2)
n = 2;
while (n) {
frac = frac * 10;
--n;
}
s1 = (int)quotient + ((remainder * frac + divisor / 2) /
divisor / frac);
s2 = ((remainder * frac + divisor / 2) / divisor) % frac;
r = snprintf(buf, len, "%d%s%d%s%s%s",
sign * s1, localeconv()->decimal_point, s2,
sep, SCALE2PREFIX(i), suffix);
} else if ((flags & HN_DECIMAL) && (u_int)r + 3 <= len &&
(((quotient == 9 && remainder < divisordeccut) ||
quotient < 9) && i > 0)) {
s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
divisor / 10);
s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
r = snprintf(buf, len, "%d%s%d%s%s%s",
sign * s1, localeconv()->decimal_point, s2,
sep, SCALE2PREFIX(i), suffix);
}
return (r);
}