]> git.saurik.com Git - apple/libutil.git/blame - humanize_number.c
libutil-47.20.1.tar.gz
[apple/libutil.git] / humanize_number.c
CommitLineData
18e053d4 1/* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */
3f2457aa
A
2
3/*
4 * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
18e053d4 5 * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org>
3f2457aa
A
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10 * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
3f2457aa
A
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include <sys/cdefs.h>
18e053d4 35__FBSDID("$FreeBSD$");
3f2457aa
A
36
37#include <sys/types.h>
38#include <assert.h>
18e053d4 39#include <inttypes.h>
3f2457aa
A
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <locale.h>
44#include <libutil.h>
18e053d4
A
45
46static const int maxscale = 7;
3f2457aa
A
47
48int
18e053d4 49humanize_number(char *buf, size_t len, int64_t quotient,
3f2457aa
A
50 const char *suffix, int scale, int flags)
51{
52 const char *prefixes, *sep;
18e053d4
A
53 int i, r, remainder, s1, s2, sign;
54 int divisordeccut;
55 int64_t divisor, max;
3f2457aa
A
56 size_t baselen;
57
18e053d4
A
58 /* Since so many callers don't check -1, NUL terminate the buffer */
59 if (len > 0)
60 buf[0] = '\0';
3f2457aa 61
18e053d4
A
62 /* validate args */
63 if (buf == NULL || suffix == NULL)
64 return (-1);
65 if (scale < 0)
66 return (-1);
67 else if (scale >= maxscale &&
68 ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
69 return (-1);
70 if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
71 return (-1);
72
73 /* setup parameters */
74 remainder = 0;
75
76 if (flags & HN_IEC_PREFIXES) {
77 baselen = 2;
3f2457aa 78 /*
18e053d4
A
79 * Use the prefixes for power of two recommended by
80 * the International Electrotechnical Commission
81 * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
82 *
83 * HN_IEC_PREFIXES implies a divisor of 1024 here
84 * (use of HN_DIVISOR_1000 would have triggered
85 * an assertion earlier).
3f2457aa
A
86 */
87 divisor = 1024;
18e053d4 88 divisordeccut = 973; /* ceil(.95 * 1024) */
3f2457aa 89 if (flags & HN_B)
18e053d4 90 prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
3f2457aa 91 else
18e053d4
A
92 prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
93 } else {
94 baselen = 1;
95 if (flags & HN_DIVISOR_1000) {
96 divisor = 1000;
97 divisordeccut = 950;
98 if (flags & HN_B)
99 prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
100 else
101 prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
102 } else {
103 divisor = 1024;
104 divisordeccut = 973; /* ceil(.95 * 1024) */
105 if (flags & HN_B)
106 prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
107 else
108 prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
109 }
3f2457aa
A
110 }
111
18e053d4 112#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
3f2457aa 113
18e053d4 114 if (quotient < 0) {
3f2457aa 115 sign = -1;
18e053d4
A
116 quotient = -quotient;
117 baselen += 2; /* sign, digit */
3f2457aa
A
118 } else {
119 sign = 1;
18e053d4 120 baselen += 1; /* digit */
3f2457aa
A
121 }
122 if (flags & HN_NOSPACE)
123 sep = "";
124 else {
125 sep = " ";
126 baselen++;
127 }
128 baselen += strlen(suffix);
129
130 /* Check if enough room for `x y' + suffix + `\0' */
131 if (len < baselen + 1)
132 return (-1);
133
134 if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
135 /* See if there is additional columns can be used. */
1d1fa3c6 136 for (max = 1, i = len - baselen; i-- > 0 && max <= (INT64_MAX / 10);)
3f2457aa
A
137 max *= 10;
138
18e053d4
A
139 /*
140 * Divide the number until it fits the given column.
141 * If there will be an overflow by the rounding below,
142 * divide once more.
143 */
144 for (i = 0;
145 (quotient >= max || (quotient == max - 1 &&
146 remainder >= divisordeccut)) && i < maxscale; i++) {
147 remainder = quotient % divisor;
148 quotient /= divisor;
149 }
3f2457aa
A
150
151 if (scale & HN_GETSCALE)
152 return (i);
18e053d4
A
153 } else {
154 for (i = 0; i < scale && i < maxscale; i++) {
155 remainder = quotient % divisor;
156 quotient /= divisor;
157 }
158 }
3f2457aa
A
159
160 /* If a value <= 9.9 after rounding and ... */
18e053d4
A
161 /*
162 * XXX - should we make sure there is enough space for the decimal
163 * place and if not, don't do HN_DECIMAL?
164 */
165 if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
166 i > 0 && flags & HN_DECIMAL) {
167 s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
168 divisor / 10);
169 s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
170 r = snprintf(buf, len, "%d%s%d%s%s%s",
171 sign * s1, localeconv()->decimal_point, s2,
3f2457aa
A
172 sep, SCALE2PREFIX(i), suffix);
173 } else
18e053d4
A
174 r = snprintf(buf, len, "%" PRId64 "%s%s%s",
175 sign * (quotient + (remainder + divisor / 2) / divisor),
3f2457aa
A
176 sep, SCALE2PREFIX(i), suffix);
177
178 return (r);
179}