xref: /minix3/lib/libc/gen/humanize_number.c (revision f14fb602092e015ff630df58e17c2a9cd57d29b3)
1*f14fb602SLionel Sambuc /*	$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $	*/
22fe8fb19SBen Gras 
32fe8fb19SBen Gras /*
42fe8fb19SBen Gras  * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
52fe8fb19SBen Gras  * All rights reserved.
62fe8fb19SBen Gras  *
72fe8fb19SBen Gras  * This code is derived from software contributed to The NetBSD Foundation
82fe8fb19SBen Gras  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
92fe8fb19SBen Gras  * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
102fe8fb19SBen Gras  *
112fe8fb19SBen Gras  * Redistribution and use in source and binary forms, with or without
122fe8fb19SBen Gras  * modification, are permitted provided that the following conditions
132fe8fb19SBen Gras  * are met:
142fe8fb19SBen Gras  * 1. Redistributions of source code must retain the above copyright
152fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer.
162fe8fb19SBen Gras  * 2. Redistributions in binary form must reproduce the above copyright
172fe8fb19SBen Gras  *    notice, this list of conditions and the following disclaimer in the
182fe8fb19SBen Gras  *    documentation and/or other materials provided with the distribution.
192fe8fb19SBen Gras  *
202fe8fb19SBen Gras  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
212fe8fb19SBen Gras  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
222fe8fb19SBen Gras  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
232fe8fb19SBen Gras  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
242fe8fb19SBen Gras  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
252fe8fb19SBen Gras  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
262fe8fb19SBen Gras  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
272fe8fb19SBen Gras  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
282fe8fb19SBen Gras  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
292fe8fb19SBen Gras  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
302fe8fb19SBen Gras  * POSSIBILITY OF SUCH DAMAGE.
312fe8fb19SBen Gras  */
322fe8fb19SBen Gras 
332fe8fb19SBen Gras #include <sys/cdefs.h>
342fe8fb19SBen Gras #if defined(LIBC_SCCS) && !defined(lint)
35*f14fb602SLionel Sambuc __RCSID("$NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp $");
362fe8fb19SBen Gras #endif /* LIBC_SCCS and not lint */
372fe8fb19SBen Gras 
382fe8fb19SBen Gras #include "namespace.h"
392fe8fb19SBen Gras #include <assert.h>
402fe8fb19SBen Gras #include <inttypes.h>
412fe8fb19SBen Gras #include <stdio.h>
422fe8fb19SBen Gras #include <stdlib.h>
432fe8fb19SBen Gras #include <string.h>
442fe8fb19SBen Gras #include <locale.h>
452fe8fb19SBen Gras 
462fe8fb19SBen Gras int
humanize_number(char * buf,size_t len,int64_t bytes,const char * suffix,int scale,int flags)472fe8fb19SBen Gras humanize_number(char *buf, size_t len, int64_t bytes,
482fe8fb19SBen Gras     const char *suffix, int scale, int flags)
492fe8fb19SBen Gras {
502fe8fb19SBen Gras 	const char *prefixes, *sep;
51*f14fb602SLionel Sambuc 	int	b, r, s1, s2, sign;
52*f14fb602SLionel Sambuc 	int64_t	divisor, max, post = 1;
53*f14fb602SLionel Sambuc 	size_t	i, baselen, maxscale;
542fe8fb19SBen Gras 
552fe8fb19SBen Gras 	_DIAGASSERT(buf != NULL);
562fe8fb19SBen Gras 	_DIAGASSERT(suffix != NULL);
572fe8fb19SBen Gras 	_DIAGASSERT(scale >= 0);
582fe8fb19SBen Gras 
592fe8fb19SBen Gras 	if (flags & HN_DIVISOR_1000) {
602fe8fb19SBen Gras 		/* SI for decimal multiplies */
612fe8fb19SBen Gras 		divisor = 1000;
622fe8fb19SBen Gras 		if (flags & HN_B)
632fe8fb19SBen Gras 			prefixes = "B\0k\0M\0G\0T\0P\0E";
642fe8fb19SBen Gras 		else
652fe8fb19SBen Gras 			prefixes = "\0\0k\0M\0G\0T\0P\0E";
662fe8fb19SBen Gras 	} else {
672fe8fb19SBen Gras 		/*
682fe8fb19SBen Gras 		 * binary multiplies
692fe8fb19SBen Gras 		 * XXX IEC 60027-2 recommends Ki, Mi, Gi...
702fe8fb19SBen Gras 		 */
712fe8fb19SBen Gras 		divisor = 1024;
722fe8fb19SBen Gras 		if (flags & HN_B)
732fe8fb19SBen Gras 			prefixes = "B\0K\0M\0G\0T\0P\0E";
742fe8fb19SBen Gras 		else
752fe8fb19SBen Gras 			prefixes = "\0\0K\0M\0G\0T\0P\0E";
762fe8fb19SBen Gras 	}
772fe8fb19SBen Gras 
782fe8fb19SBen Gras #define	SCALE2PREFIX(scale)	(&prefixes[(scale) << 1])
792fe8fb19SBen Gras 	maxscale = 7;
802fe8fb19SBen Gras 
81*f14fb602SLionel Sambuc 	if ((size_t)scale >= maxscale &&
822fe8fb19SBen Gras 	    (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0)
832fe8fb19SBen Gras 		return (-1);
842fe8fb19SBen Gras 
852fe8fb19SBen Gras 	if (buf == NULL || suffix == NULL)
862fe8fb19SBen Gras 		return (-1);
872fe8fb19SBen Gras 
882fe8fb19SBen Gras 	if (len > 0)
892fe8fb19SBen Gras 		buf[0] = '\0';
902fe8fb19SBen Gras 	if (bytes < 0) {
912fe8fb19SBen Gras 		sign = -1;
922fe8fb19SBen Gras 		baselen = 3;		/* sign, digit, prefix */
93*f14fb602SLionel Sambuc 		if (-bytes < INT64_MAX / 100)
94*f14fb602SLionel Sambuc 			bytes *= -100;
95*f14fb602SLionel Sambuc 		else {
96*f14fb602SLionel Sambuc 			bytes = -bytes;
97*f14fb602SLionel Sambuc 			post = 100;
98*f14fb602SLionel Sambuc 			baselen += 2;
99*f14fb602SLionel Sambuc 		}
1002fe8fb19SBen Gras 	} else {
1012fe8fb19SBen Gras 		sign = 1;
1022fe8fb19SBen Gras 		baselen = 2;		/* digit, prefix */
103*f14fb602SLionel Sambuc 		if (bytes < INT64_MAX / 100)
104*f14fb602SLionel Sambuc 			bytes *= 100;
105*f14fb602SLionel Sambuc 		else {
106*f14fb602SLionel Sambuc 			post = 100;
107*f14fb602SLionel Sambuc 			baselen += 2;
108*f14fb602SLionel Sambuc 		}
1092fe8fb19SBen Gras 	}
1102fe8fb19SBen Gras 	if (flags & HN_NOSPACE)
1112fe8fb19SBen Gras 		sep = "";
1122fe8fb19SBen Gras 	else {
1132fe8fb19SBen Gras 		sep = " ";
1142fe8fb19SBen Gras 		baselen++;
1152fe8fb19SBen Gras 	}
1162fe8fb19SBen Gras 	baselen += strlen(suffix);
1172fe8fb19SBen Gras 
1182fe8fb19SBen Gras 	/* Check if enough room for `x y' + suffix + `\0' */
1192fe8fb19SBen Gras 	if (len < baselen + 1)
1202fe8fb19SBen Gras 		return (-1);
1212fe8fb19SBen Gras 
1222fe8fb19SBen Gras 	if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
1232fe8fb19SBen Gras 		/* See if there is additional columns can be used. */
1242fe8fb19SBen Gras 		for (max = 100, i = len - baselen; i-- > 0;)
1252fe8fb19SBen Gras 			max *= 10;
1262fe8fb19SBen Gras 
1272fe8fb19SBen Gras 		/*
1282fe8fb19SBen Gras 		 * Divide the number until it fits the given column.
1292fe8fb19SBen Gras 		 * If there will be an overflow by the rounding below,
1302fe8fb19SBen Gras 		 * divide once more.
1312fe8fb19SBen Gras 		 */
1322fe8fb19SBen Gras 		for (i = 0; bytes >= max - 50 && i < maxscale; i++)
1332fe8fb19SBen Gras 			bytes /= divisor;
1342fe8fb19SBen Gras 
135*f14fb602SLionel Sambuc 		if (scale & HN_GETSCALE) {
136*f14fb602SLionel Sambuc 			_DIAGASSERT(__type_fit(int, i));
137*f14fb602SLionel Sambuc 			return (int)i;
138*f14fb602SLionel Sambuc 		}
1392fe8fb19SBen Gras 	} else
140*f14fb602SLionel Sambuc 		for (i = 0; i < (size_t)scale && i < maxscale; i++)
1412fe8fb19SBen Gras 			bytes /= divisor;
142*f14fb602SLionel Sambuc 	bytes *= post;
1432fe8fb19SBen Gras 
1442fe8fb19SBen Gras 	/* If a value <= 9.9 after rounding and ... */
1452fe8fb19SBen Gras 	if (bytes < 995 && i > 0 && flags & HN_DECIMAL) {
1462fe8fb19SBen Gras 		/* baselen + \0 + .N */
1472fe8fb19SBen Gras 		if (len < baselen + 1 + 2)
1482fe8fb19SBen Gras 			return (-1);
1492fe8fb19SBen Gras 		b = ((int)bytes + 5) / 10;
1502fe8fb19SBen Gras 		s1 = b / 10;
1512fe8fb19SBen Gras 		s2 = b % 10;
1522fe8fb19SBen Gras 		r = snprintf(buf, len, "%d%s%d%s%s%s",
1532fe8fb19SBen Gras 		    sign * s1, localeconv()->decimal_point, s2,
1542fe8fb19SBen Gras 		    sep, SCALE2PREFIX(i), suffix);
1552fe8fb19SBen Gras 	} else
1562fe8fb19SBen Gras 		r = snprintf(buf, len, "%" PRId64 "%s%s%s",
1572fe8fb19SBen Gras 		    sign * ((bytes + 50) / 100),
1582fe8fb19SBen Gras 		    sep, SCALE2PREFIX(i), suffix);
1592fe8fb19SBen Gras 
1602fe8fb19SBen Gras 	return (r);
1612fe8fb19SBen Gras }
162