/* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */12/*-3* SPDX-License-Identifier: BSD-2-Clause4*5* Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.6* Copyright 2013 John-Mark Gurney <[email protected]>7* All rights reserved.8*9* This code is derived from software contributed to The NetBSD Foundation10* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,11* NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.12*13* Redistribution and use in source and binary forms, with or without14* modification, are permitted provided that the following conditions15* are met:16* 1. Redistributions of source code must retain the above copyright17* notice, this list of conditions and the following disclaimer.18* 2. Redistributions in binary form must reproduce the above copyright19* notice, this list of conditions and the following disclaimer in the20* documentation and/or other materials provided with the distribution.21*22* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS23* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED24* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR25* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS26* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR27* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF28* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS29* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN30* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)31* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE32* POSSIBILITY OF SUCH DAMAGE.33*/3435#include "bsd_compat.h"3637#if !HAVE_HUMANIZE_NUMBER3839#include <sys/types.h>40#include <assert.h>41#include <inttypes.h>42#include <stdio.h>43#include <stdlib.h>44#include <string.h>45#include <locale.h>4647#include "humanize_number.h"4849static const int maxscale = 6;5051int52humanize_number(char *buf, size_t len, int64_t quotient,53const char *suffix, int scale, int flags)54{55const char *prefixes, *sep;56int i, r, remainder, s1, s2, sign;57int divisordeccut;58int64_t divisor, max;59size_t baselen;6061/* Since so many callers don't check -1, NUL terminate the buffer */62if (len > 0)63buf[0] = '\0';6465/* validate args */66if (buf == NULL || suffix == NULL)67return (-1);68if (scale < 0)69return (-1);70else if (scale > maxscale &&71((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))72return (-1);73if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))74return (-1);7576/* setup parameters */77remainder = 0;7879if (flags & HN_IEC_PREFIXES) {80baselen = 2;81/*82* Use the prefixes for power of two recommended by83* the International Electrotechnical Commission84* (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).85*86* HN_IEC_PREFIXES implies a divisor of 1024 here87* (use of HN_DIVISOR_1000 would have triggered88* an assertion earlier).89*/90divisor = 1024;91divisordeccut = 973; /* ceil(.95 * 1024) */92if (flags & HN_B)93prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";94else95prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";96} else {97baselen = 1;98if (flags & HN_DIVISOR_1000) {99divisor = 1000;100divisordeccut = 950;101if (flags & HN_B)102prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";103else104prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";105} else {106divisor = 1024;107divisordeccut = 973; /* ceil(.95 * 1024) */108if (flags & HN_B)109prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";110else111prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";112}113}114115#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])116117if (quotient < 0) {118sign = -1;119quotient = -quotient;120baselen += 2; /* sign, digit */121} else {122sign = 1;123baselen += 1; /* digit */124}125if (flags & HN_NOSPACE)126sep = "";127else {128sep = " ";129baselen++;130}131baselen += strlen(suffix);132133/* Check if enough room for `x y' + suffix + `\0' */134if (len < baselen + 1)135return (-1);136137if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {138/* See if there is additional columns can be used. */139for (max = 1, i = len - baselen; i-- > 0;)140max *= 10;141142/*143* Divide the number until it fits the given column.144* If there will be an overflow by the rounding below,145* divide once more.146*/147for (i = 0;148(quotient >= max || (quotient == max - 1 &&149(remainder >= divisordeccut || remainder >=150divisor / 2))) && i < maxscale; i++) {151remainder = quotient % divisor;152quotient /= divisor;153}154155if (scale & HN_GETSCALE)156return (i);157} else {158for (i = 0; i < scale && i < maxscale; i++) {159remainder = quotient % divisor;160quotient /= divisor;161}162}163164/* If a value <= 9.9 after rounding and ... */165/*166* XXX - should we make sure there is enough space for the decimal167* place and if not, don't do HN_DECIMAL?168*/169if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&170i > 0 && flags & HN_DECIMAL) {171s1 = (int)quotient + ((remainder * 10 + divisor / 2) /172divisor / 10);173s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;174r = snprintf(buf, len, "%d%s%d%s%s%s",175sign * s1, localeconv()->decimal_point, s2,176sep, SCALE2PREFIX(i), suffix);177} else178r = snprintf(buf, len, "%" PRId64 "%s%s%s",179sign * (quotient + (remainder + divisor / 2) / divisor),180sep, SCALE2PREFIX(i), suffix);181182return (r);183}184#endif185186187