]>
Commit | Line | Data |
---|---|---|
1 | /* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */ | |
2 | ||
3 | /* | |
4 | * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. | |
5 | * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org> | |
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. | |
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> | |
35 | __FBSDID("$FreeBSD$"); | |
36 | ||
37 | #include <sys/types.h> | |
38 | #include <assert.h> | |
39 | #include <inttypes.h> | |
40 | #include <stdio.h> | |
41 | #include <stdlib.h> | |
42 | #include <string.h> | |
43 | #include <locale.h> | |
44 | #include <libutil.h> | |
45 | ||
46 | static const int maxscale = 7; | |
47 | ||
48 | int | |
49 | humanize_number(char *buf, size_t len, int64_t quotient, | |
50 | const char *suffix, int scale, int flags) | |
51 | { | |
52 | const char *prefixes, *sep; | |
53 | int i, r, remainder, s1, s2, sign; | |
54 | int divisordeccut; | |
55 | int64_t divisor, max; | |
56 | size_t baselen; | |
57 | ||
58 | /* Since so many callers don't check -1, NUL terminate the buffer */ | |
59 | if (len > 0) | |
60 | buf[0] = '\0'; | |
61 | ||
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; | |
78 | /* | |
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). | |
86 | */ | |
87 | divisor = 1024; | |
88 | divisordeccut = 973; /* ceil(.95 * 1024) */ | |
89 | if (flags & HN_B) | |
90 | prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei"; | |
91 | else | |
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 | } | |
110 | } | |
111 | ||
112 | #define SCALE2PREFIX(scale) (&prefixes[(scale) * 3]) | |
113 | ||
114 | if (quotient < 0) { | |
115 | sign = -1; | |
116 | quotient = -quotient; | |
117 | baselen += 2; /* sign, digit */ | |
118 | } else { | |
119 | sign = 1; | |
120 | baselen += 1; /* digit */ | |
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. */ | |
136 | for (max = 1, i = len - baselen; i-- > 0 && max <= (INT64_MAX / 10);) | |
137 | max *= 10; | |
138 | ||
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 | } | |
150 | ||
151 | if (scale & HN_GETSCALE) | |
152 | return (i); | |
153 | } else { | |
154 | for (i = 0; i < scale && i < maxscale; i++) { | |
155 | remainder = quotient % divisor; | |
156 | quotient /= divisor; | |
157 | } | |
158 | } | |
159 | ||
160 | /* If a value <= 9.9 after rounding and ... */ | |
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, | |
172 | sep, SCALE2PREFIX(i), suffix); | |
173 | } else | |
174 | r = snprintf(buf, len, "%" PRId64 "%s%s%s", | |
175 | sign * (quotient + (remainder + divisor / 2) / divisor), | |
176 | sep, SCALE2PREFIX(i), suffix); | |
177 | ||
178 | return (r); | |
179 | } |