Subject: humanize_number again
To: None <tech-userlevel@netbsd.org>
From: Tomas Svensson <tsn@gbdev.net>
List: tech-userlevel
Date: 02/04/2002 01:02:21
Hi,

Here is a modified version of humanize_number(9) that I think should
be sufficient for my df(1)/du(1) humanization needs. Differences
from the kernel version are:

- the bytes argument is signed (negative sizes can be used).
- rounding is done like floating point numbers were used.
- the suffix-prefix K is changed to k, which should be correct
  according to standards.
- three flags can be used : HN_DECIMAL tells it to use one decimal
  for numbers less than 10, HN_NOSPACE removes the space between
  the number and the prefix and HN_B will use the prefix "B" for
  numbers that would normally not have a prefix (bytes less than
  1000). Use 0 if no flags are used (not worth the trouble with
  varargs?).

- Tomas




#define HN_DECIMAL	1
#define HN_NOSPACE      2	
#define HN_B		4

int humanize_number(char *buf, size_t len, int64_t bytes,
        const char *suffix, int divisor, int flags) {

	static const char prefixes[] = " kMGTPE";
	
	int	i, r;
	int64_t	max, s1, s2, sign;
	size_t	baselen, suffixlen;

	if (buf == NULL)
		return -1;

	if (len > 0)
		buf[0] = '\0';
	if (bytes < 0) {
		sign = -1;
		bytes *= -100;
		baselen = 4;
	} else {
		sign = 1;
		bytes *= 100;
		baselen = 3;
	}

	suffixlen = strlen(suffix);

	/* check if enough room for `x y' + suffix + `\0' */
	if (len < baselen + suffixlen + 1)
		return (-1);

	max = 100;
	for (i = 0; i < len - suffixlen - baselen + ((flags & HN_NOSPACE) ?
             1 : 0) ; i++)
		max *= 10;

	for (i = 0; bytes >= max && i < sizeof(prefixes); i++)
		bytes /= divisor;

	if (bytes < 1000 && flags & HN_DECIMAL) {
		if (len < (baselen + 2  + ( (flags & HN_NOSPACE) || (i == 0
		    && !(flags & HN_B)) ? 0 : 1)))
			return (-1);
		s1 = bytes / 100;
		if ((s2 = (( (bytes % 100 ) + 5 ) /  10 ) ) == 10 ) {
			s1++;
			s2 = 0;
		}
		r = snprintf(buf, len, "%qd%s%qd%s%c%s", sign * s1,
		localeconv()->decimal_points2, (i == 0 && !(flags & HN_B) )
		    || flags & HN_NOSPACE ? "" : " ", (i == 0 && (flags &
		    HN_B)) ? 'B' : prefixes[i], suffix);
		
	} else
	r = snprintf(buf, len, "%qd%s%c%s", sign * ((bytes + 50) / 100), 
	    i == 0 || flags & HN_NOSPACE ? "" : " ", (i == 0 &&
	    (flags & HN_B)) ? 'B' : prefixes[i], suffix);

	return r;
}