Blame


1 dd038bc6 2021-09-21 thomas.ad /*
2 dd038bc6 2021-09-21 thomas.ad * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved.
3 dd038bc6 2021-09-21 thomas.ad *
4 dd038bc6 2021-09-21 thomas.ad * Redistribution and use in source and binary forms, with or without
5 dd038bc6 2021-09-21 thomas.ad * modification, are permitted provided that the following conditions
6 dd038bc6 2021-09-21 thomas.ad * are met:
7 dd038bc6 2021-09-21 thomas.ad * 1. Redistributions of source code must retain the above copyright
8 dd038bc6 2021-09-21 thomas.ad * notice, this list of conditions and the following disclaimer.
9 dd038bc6 2021-09-21 thomas.ad * 2. Redistributions in binary form must reproduce the above copyright
10 dd038bc6 2021-09-21 thomas.ad * notice, this list of conditions and the following disclaimer in the
11 dd038bc6 2021-09-21 thomas.ad * documentation and/or other materials provided with the distribution.
12 dd038bc6 2021-09-21 thomas.ad * 3. The name of the author may not be used to endorse or promote products
13 dd038bc6 2021-09-21 thomas.ad * derived from this software without specific prior written permission.
14 dd038bc6 2021-09-21 thomas.ad *
15 dd038bc6 2021-09-21 thomas.ad * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 dd038bc6 2021-09-21 thomas.ad * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 dd038bc6 2021-09-21 thomas.ad * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 dd038bc6 2021-09-21 thomas.ad * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 dd038bc6 2021-09-21 thomas.ad * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 dd038bc6 2021-09-21 thomas.ad * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 dd038bc6 2021-09-21 thomas.ad * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 dd038bc6 2021-09-21 thomas.ad * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 dd038bc6 2021-09-21 thomas.ad * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 dd038bc6 2021-09-21 thomas.ad * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 dd038bc6 2021-09-21 thomas.ad */
26 dd038bc6 2021-09-21 thomas.ad
27 dd038bc6 2021-09-21 thomas.ad /*
28 dd038bc6 2021-09-21 thomas.ad * fmt_scaled: Format numbers scaled for human comprehension
29 dd038bc6 2021-09-21 thomas.ad * scan_scaled: Scan numbers in this format.
30 dd038bc6 2021-09-21 thomas.ad *
31 dd038bc6 2021-09-21 thomas.ad * "Human-readable" output uses 4 digits max, and puts a unit suffix at
32 dd038bc6 2021-09-21 thomas.ad * the end. Makes output compact and easy-to-read esp. on huge disks.
33 dd038bc6 2021-09-21 thomas.ad * Formatting code was originally in OpenBSD "df", converted to library routine.
34 dd038bc6 2021-09-21 thomas.ad * Scanning code written for OpenBSD libutil.
35 dd038bc6 2021-09-21 thomas.ad */
36 dd038bc6 2021-09-21 thomas.ad
37 dd038bc6 2021-09-21 thomas.ad #include <stdio.h>
38 dd038bc6 2021-09-21 thomas.ad #include <stdlib.h>
39 dd038bc6 2021-09-21 thomas.ad #include <errno.h>
40 dd038bc6 2021-09-21 thomas.ad #include <string.h>
41 dd038bc6 2021-09-21 thomas.ad #include <ctype.h>
42 dd038bc6 2021-09-21 thomas.ad #include <limits.h>
43 dd038bc6 2021-09-21 thomas.ad
44 dd038bc6 2021-09-21 thomas.ad #include "got_compat.h"
45 dd038bc6 2021-09-21 thomas.ad
46 dd038bc6 2021-09-21 thomas.ad typedef enum {
47 dd038bc6 2021-09-21 thomas.ad NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6
48 dd038bc6 2021-09-21 thomas.ad } unit_type;
49 dd038bc6 2021-09-21 thomas.ad
50 dd038bc6 2021-09-21 thomas.ad /* These three arrays MUST be in sync! XXX make a struct */
51 dd038bc6 2021-09-21 thomas.ad static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA };
52 dd038bc6 2021-09-21 thomas.ad static char scale_chars[] = "BKMGTPE";
53 dd038bc6 2021-09-21 thomas.ad static long long scale_factors[] = {
54 dd038bc6 2021-09-21 thomas.ad 1LL,
55 dd038bc6 2021-09-21 thomas.ad 1024LL,
56 dd038bc6 2021-09-21 thomas.ad 1024LL*1024,
57 dd038bc6 2021-09-21 thomas.ad 1024LL*1024*1024,
58 dd038bc6 2021-09-21 thomas.ad 1024LL*1024*1024*1024,
59 dd038bc6 2021-09-21 thomas.ad 1024LL*1024*1024*1024*1024,
60 dd038bc6 2021-09-21 thomas.ad 1024LL*1024*1024*1024*1024*1024,
61 dd038bc6 2021-09-21 thomas.ad };
62 dd038bc6 2021-09-21 thomas.ad #define SCALE_LENGTH (sizeof(units)/sizeof(units[0]))
63 dd038bc6 2021-09-21 thomas.ad
64 dd038bc6 2021-09-21 thomas.ad #define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */
65 dd038bc6 2021-09-21 thomas.ad
66 dd038bc6 2021-09-21 thomas.ad /* Convert the given input string "scaled" into numeric in "result".
67 dd038bc6 2021-09-21 thomas.ad * Return 0 on success, -1 and errno set on error.
68 dd038bc6 2021-09-21 thomas.ad */
69 dd038bc6 2021-09-21 thomas.ad int
70 dd038bc6 2021-09-21 thomas.ad scan_scaled(char *scaled, long long *result)
71 dd038bc6 2021-09-21 thomas.ad {
72 dd038bc6 2021-09-21 thomas.ad char *p = scaled;
73 dd038bc6 2021-09-21 thomas.ad int sign = 0;
74 dd038bc6 2021-09-21 thomas.ad unsigned int i, ndigits = 0, fract_digits = 0;
75 dd038bc6 2021-09-21 thomas.ad long long scale_fact = 1, whole = 0, fpart = 0;
76 dd038bc6 2021-09-21 thomas.ad
77 dd038bc6 2021-09-21 thomas.ad /* Skip leading whitespace */
78 dd038bc6 2021-09-21 thomas.ad while (isascii(*p) && isspace(*p))
79 dd038bc6 2021-09-21 thomas.ad ++p;
80 dd038bc6 2021-09-21 thomas.ad
81 dd038bc6 2021-09-21 thomas.ad /* Then at most one leading + or - */
82 dd038bc6 2021-09-21 thomas.ad while (*p == '-' || *p == '+') {
83 dd038bc6 2021-09-21 thomas.ad if (*p == '-') {
84 dd038bc6 2021-09-21 thomas.ad if (sign) {
85 dd038bc6 2021-09-21 thomas.ad errno = EINVAL;
86 dd038bc6 2021-09-21 thomas.ad return -1;
87 dd038bc6 2021-09-21 thomas.ad }
88 dd038bc6 2021-09-21 thomas.ad sign = -1;
89 dd038bc6 2021-09-21 thomas.ad ++p;
90 dd038bc6 2021-09-21 thomas.ad } else if (*p == '+') {
91 dd038bc6 2021-09-21 thomas.ad if (sign) {
92 dd038bc6 2021-09-21 thomas.ad errno = EINVAL;
93 dd038bc6 2021-09-21 thomas.ad return -1;
94 dd038bc6 2021-09-21 thomas.ad }
95 dd038bc6 2021-09-21 thomas.ad sign = +1;
96 dd038bc6 2021-09-21 thomas.ad ++p;
97 dd038bc6 2021-09-21 thomas.ad }
98 dd038bc6 2021-09-21 thomas.ad }
99 dd038bc6 2021-09-21 thomas.ad
100 dd038bc6 2021-09-21 thomas.ad /* Main loop: Scan digits, find decimal point, if present.
101 dd038bc6 2021-09-21 thomas.ad * We don't allow exponentials, so no scientific notation
102 dd038bc6 2021-09-21 thomas.ad * (but note that E for Exa might look like e to some!).
103 dd038bc6 2021-09-21 thomas.ad * Advance 'p' to end, to get scale factor.
104 dd038bc6 2021-09-21 thomas.ad */
105 dd038bc6 2021-09-21 thomas.ad for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) {
106 dd038bc6 2021-09-21 thomas.ad if (*p == '.') {
107 dd038bc6 2021-09-21 thomas.ad if (fract_digits > 0) { /* oops, more than one '.' */
108 dd038bc6 2021-09-21 thomas.ad errno = EINVAL;
109 dd038bc6 2021-09-21 thomas.ad return -1;
110 dd038bc6 2021-09-21 thomas.ad }
111 dd038bc6 2021-09-21 thomas.ad fract_digits = 1;
112 dd038bc6 2021-09-21 thomas.ad continue;
113 dd038bc6 2021-09-21 thomas.ad }
114 dd038bc6 2021-09-21 thomas.ad
115 dd038bc6 2021-09-21 thomas.ad i = (*p) - '0'; /* whew! finally a digit we can use */
116 dd038bc6 2021-09-21 thomas.ad if (fract_digits > 0) {
117 dd038bc6 2021-09-21 thomas.ad if (fract_digits >= MAX_DIGITS-1)
118 dd038bc6 2021-09-21 thomas.ad /* ignore extra fractional digits */
119 dd038bc6 2021-09-21 thomas.ad continue;
120 dd038bc6 2021-09-21 thomas.ad fract_digits++; /* for later scaling */
121 dd038bc6 2021-09-21 thomas.ad fpart *= 10;
122 dd038bc6 2021-09-21 thomas.ad fpart += i;
123 dd038bc6 2021-09-21 thomas.ad } else { /* normal digit */
124 dd038bc6 2021-09-21 thomas.ad if (++ndigits >= MAX_DIGITS) {
125 dd038bc6 2021-09-21 thomas.ad errno = ERANGE;
126 dd038bc6 2021-09-21 thomas.ad return -1;
127 dd038bc6 2021-09-21 thomas.ad }
128 dd038bc6 2021-09-21 thomas.ad whole *= 10;
129 dd038bc6 2021-09-21 thomas.ad whole += i;
130 dd038bc6 2021-09-21 thomas.ad }
131 dd038bc6 2021-09-21 thomas.ad }
132 dd038bc6 2021-09-21 thomas.ad
133 dd038bc6 2021-09-21 thomas.ad if (sign) {
134 dd038bc6 2021-09-21 thomas.ad whole *= sign;
135 dd038bc6 2021-09-21 thomas.ad fpart *= sign;
136 dd038bc6 2021-09-21 thomas.ad }
137 dd038bc6 2021-09-21 thomas.ad
138 dd038bc6 2021-09-21 thomas.ad /* If no scale factor given, we're done. fraction is discarded. */
139 dd038bc6 2021-09-21 thomas.ad if (!*p) {
140 dd038bc6 2021-09-21 thomas.ad *result = whole;
141 dd038bc6 2021-09-21 thomas.ad return 0;
142 dd038bc6 2021-09-21 thomas.ad }
143 dd038bc6 2021-09-21 thomas.ad
144 dd038bc6 2021-09-21 thomas.ad /* Validate scale factor, and scale whole and fraction by it. */
145 dd038bc6 2021-09-21 thomas.ad for (i = 0; i < SCALE_LENGTH; i++) {
146 dd038bc6 2021-09-21 thomas.ad
147 dd038bc6 2021-09-21 thomas.ad /* Are we there yet? */
148 dd038bc6 2021-09-21 thomas.ad if (*p == scale_chars[i] ||
149 dd038bc6 2021-09-21 thomas.ad *p == tolower(scale_chars[i])) {
150 dd038bc6 2021-09-21 thomas.ad
151 dd038bc6 2021-09-21 thomas.ad /* If it ends with alphanumerics after the scale char, bad. */
152 dd038bc6 2021-09-21 thomas.ad if (isalnum(*(p+1))) {
153 dd038bc6 2021-09-21 thomas.ad errno = EINVAL;
154 dd038bc6 2021-09-21 thomas.ad return -1;
155 dd038bc6 2021-09-21 thomas.ad }
156 dd038bc6 2021-09-21 thomas.ad scale_fact = scale_factors[i];
157 dd038bc6 2021-09-21 thomas.ad
158 dd038bc6 2021-09-21 thomas.ad /* scale whole part */
159 dd038bc6 2021-09-21 thomas.ad whole *= scale_fact;
160 dd038bc6 2021-09-21 thomas.ad
161 dd038bc6 2021-09-21 thomas.ad /* truncate fpart so it does't overflow.
162 dd038bc6 2021-09-21 thomas.ad * then scale fractional part.
163 dd038bc6 2021-09-21 thomas.ad */
164 dd038bc6 2021-09-21 thomas.ad while (fpart >= LLONG_MAX / scale_fact) {
165 dd038bc6 2021-09-21 thomas.ad fpart /= 10;
166 dd038bc6 2021-09-21 thomas.ad fract_digits--;
167 dd038bc6 2021-09-21 thomas.ad }
168 dd038bc6 2021-09-21 thomas.ad fpart *= scale_fact;
169 dd038bc6 2021-09-21 thomas.ad if (fract_digits > 0) {
170 dd038bc6 2021-09-21 thomas.ad for (i = 0; i < fract_digits -1; i++)
171 dd038bc6 2021-09-21 thomas.ad fpart /= 10;
172 dd038bc6 2021-09-21 thomas.ad }
173 dd038bc6 2021-09-21 thomas.ad whole += fpart;
174 dd038bc6 2021-09-21 thomas.ad *result = whole;
175 dd038bc6 2021-09-21 thomas.ad return 0;
176 dd038bc6 2021-09-21 thomas.ad }
177 dd038bc6 2021-09-21 thomas.ad }
178 dd038bc6 2021-09-21 thomas.ad errno = ERANGE;
179 dd038bc6 2021-09-21 thomas.ad return -1;
180 dd038bc6 2021-09-21 thomas.ad }
181 dd038bc6 2021-09-21 thomas.ad
182 dd038bc6 2021-09-21 thomas.ad /* Format the given "number" into human-readable form in "result".
183 dd038bc6 2021-09-21 thomas.ad * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE.
184 dd038bc6 2021-09-21 thomas.ad * Return 0 on success, -1 and errno set if error.
185 dd038bc6 2021-09-21 thomas.ad */
186 dd038bc6 2021-09-21 thomas.ad int
187 dd038bc6 2021-09-21 thomas.ad fmt_scaled(long long number, char *result)
188 dd038bc6 2021-09-21 thomas.ad {
189 dd038bc6 2021-09-21 thomas.ad long long abval, fract = 0;
190 dd038bc6 2021-09-21 thomas.ad unsigned int i;
191 dd038bc6 2021-09-21 thomas.ad unit_type unit = NONE;
192 dd038bc6 2021-09-21 thomas.ad
193 dd038bc6 2021-09-21 thomas.ad abval = llabs(number);
194 dd038bc6 2021-09-21 thomas.ad
195 dd038bc6 2021-09-21 thomas.ad /* Not every negative long long has a positive representation.
196 dd038bc6 2021-09-21 thomas.ad * Also check for numbers that are just too darned big to format
197 dd038bc6 2021-09-21 thomas.ad */
198 dd038bc6 2021-09-21 thomas.ad if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) {
199 dd038bc6 2021-09-21 thomas.ad errno = ERANGE;
200 dd038bc6 2021-09-21 thomas.ad return -1;
201 dd038bc6 2021-09-21 thomas.ad }
202 dd038bc6 2021-09-21 thomas.ad
203 dd038bc6 2021-09-21 thomas.ad /* scale whole part; get unscaled fraction */
204 dd038bc6 2021-09-21 thomas.ad for (i = 0; i < SCALE_LENGTH; i++) {
205 dd038bc6 2021-09-21 thomas.ad if (abval/1024 < scale_factors[i]) {
206 dd038bc6 2021-09-21 thomas.ad unit = units[i];
207 dd038bc6 2021-09-21 thomas.ad fract = (i == 0) ? 0 : abval % scale_factors[i];
208 dd038bc6 2021-09-21 thomas.ad number /= scale_factors[i];
209 dd038bc6 2021-09-21 thomas.ad if (i > 0)
210 dd038bc6 2021-09-21 thomas.ad fract /= scale_factors[i - 1];
211 dd038bc6 2021-09-21 thomas.ad break;
212 dd038bc6 2021-09-21 thomas.ad }
213 dd038bc6 2021-09-21 thomas.ad }
214 dd038bc6 2021-09-21 thomas.ad
215 dd038bc6 2021-09-21 thomas.ad fract = (10 * fract + 512) / 1024;
216 dd038bc6 2021-09-21 thomas.ad /* if the result would be >= 10, round main number */
217 dd038bc6 2021-09-21 thomas.ad if (fract == 10) {
218 dd038bc6 2021-09-21 thomas.ad if (number >= 0)
219 dd038bc6 2021-09-21 thomas.ad number++;
220 dd038bc6 2021-09-21 thomas.ad else
221 dd038bc6 2021-09-21 thomas.ad number--;
222 dd038bc6 2021-09-21 thomas.ad fract = 0;
223 dd038bc6 2021-09-21 thomas.ad }
224 dd038bc6 2021-09-21 thomas.ad
225 dd038bc6 2021-09-21 thomas.ad if (number == 0)
226 dd038bc6 2021-09-21 thomas.ad strlcpy(result, "0B", FMT_SCALED_STRSIZE);
227 dd038bc6 2021-09-21 thomas.ad else if (unit == NONE || number >= 100 || number <= -100) {
228 dd038bc6 2021-09-21 thomas.ad if (fract >= 5) {
229 dd038bc6 2021-09-21 thomas.ad if (number >= 0)
230 dd038bc6 2021-09-21 thomas.ad number++;
231 dd038bc6 2021-09-21 thomas.ad else
232 dd038bc6 2021-09-21 thomas.ad number--;
233 dd038bc6 2021-09-21 thomas.ad }
234 dd038bc6 2021-09-21 thomas.ad (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c",
235 dd038bc6 2021-09-21 thomas.ad number, scale_chars[unit]);
236 dd038bc6 2021-09-21 thomas.ad } else
237 dd038bc6 2021-09-21 thomas.ad (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c",
238 dd038bc6 2021-09-21 thomas.ad number, fract, scale_chars[unit]);
239 dd038bc6 2021-09-21 thomas.ad
240 dd038bc6 2021-09-21 thomas.ad return 0;
241 dd038bc6 2021-09-21 thomas.ad }