]>
Commit | Line | Data |
---|---|---|
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 | |
46 | static const int maxscale = 7; | |
3f2457aa A |
47 | |
48 | int | |
18e053d4 | 49 | humanize_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 | } |