1 1.10 kre /* $NetBSD: t_humanize_number.c,v 1.10 2019/03/11 17:45:12 kre Exp $ */ 2 1.1 pgoyette 3 1.1 pgoyette /*- 4 1.2 jruoho * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc. 5 1.1 pgoyette * All rights reserved. 6 1.1 pgoyette * 7 1.1 pgoyette * Redistribution and use in source and binary forms, with or without 8 1.1 pgoyette * modification, are permitted provided that the following conditions 9 1.1 pgoyette * are met: 10 1.1 pgoyette * 1. Redistributions of source code must retain the above copyright 11 1.1 pgoyette * notice, this list of conditions and the following disclaimer. 12 1.1 pgoyette * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 pgoyette * notice, this list of conditions and the following disclaimer in the 14 1.1 pgoyette * documentation and/or other materials provided with the distribution. 15 1.1 pgoyette * 16 1.1 pgoyette * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 pgoyette * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 pgoyette * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 pgoyette * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 pgoyette * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 pgoyette * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 pgoyette * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 pgoyette * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 pgoyette * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 pgoyette * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 pgoyette * POSSIBILITY OF SUCH DAMAGE. 27 1.1 pgoyette */ 28 1.1 pgoyette 29 1.1 pgoyette #include <atf-c.h> 30 1.1 pgoyette 31 1.1 pgoyette #include <err.h> 32 1.1 pgoyette #include <inttypes.h> 33 1.1 pgoyette #include <stdarg.h> 34 1.1 pgoyette #include <stdio.h> 35 1.1 pgoyette #include <stdlib.h> 36 1.1 pgoyette #include <string.h> 37 1.1 pgoyette #include <util.h> 38 1.1 pgoyette 39 1.1 pgoyette const struct hnopts { 40 1.1 pgoyette size_t ho_len; 41 1.1 pgoyette int64_t ho_num; 42 1.1 pgoyette const char *ho_suffix; 43 1.1 pgoyette int ho_scale; 44 1.1 pgoyette int ho_flags; 45 1.1 pgoyette int ho_retval; /* expected return value */ 46 1.1 pgoyette const char *ho_retstr; /* expected string in buffer */ 47 1.1 pgoyette } hnopts[] = { 48 1.1 pgoyette /* 49 1.1 pgoyette * Rev. 1.6 produces "10.0". 50 1.1 pgoyette */ 51 1.1 pgoyette { 5, 10737418236ULL * 1024, "", 52 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10T" }, 53 1.1 pgoyette 54 1.1 pgoyette { 5, 10450000, "", 55 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" }, 56 1.1 pgoyette { 5, 10500000, "", /* just for reference */ 57 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" }, 58 1.1 pgoyette 59 1.1 pgoyette /* 60 1.1 pgoyette * Trailing space. Rev. 1.7 produces "1 ". 61 1.1 pgoyette */ 62 1.1 pgoyette { 5, 1, "", 0, HN_NOSPACE, 1, "1" }, 63 1.1 pgoyette 64 1.1 pgoyette { 5, 1, "", 0, 0, 2, "1 " }, /* just for reference */ 65 1.1 pgoyette { 5, 1, "", 0, HN_B, 3, "1 B" }, /* and more ... */ 66 1.1 pgoyette { 5, 1, "", 0, HN_DECIMAL, 2, "1 " }, 67 1.1 pgoyette { 5, 1, "", 0, HN_NOSPACE | HN_B, 2, "1B" }, 68 1.1 pgoyette { 5, 1, "", 0, HN_B | HN_DECIMAL, 3, "1 B" }, 69 1.1 pgoyette { 5, 1, "", 0, HN_NOSPACE | HN_B | HN_DECIMAL, 2, "1B" }, 70 1.1 pgoyette 71 1.1 pgoyette /* 72 1.1 pgoyette * Space and HN_B. Rev. 1.7 produces "1B". 73 1.1 pgoyette */ 74 1.1 pgoyette { 5, 1, "", HN_AUTOSCALE, HN_B, 3, "1 B" }, 75 1.1 pgoyette { 5, 1000, "", /* just for reference */ 76 1.1 pgoyette HN_AUTOSCALE, HN_B, 3, "1 K" }, 77 1.1 pgoyette 78 1.1 pgoyette /* 79 1.1 pgoyette * Truncated output. Rev. 1.7 produces "1.0 K". 80 1.1 pgoyette */ 81 1.1 pgoyette { 6, 1000, "A", HN_AUTOSCALE, HN_DECIMAL, -1, "" }, 82 1.1 pgoyette 83 1.1 pgoyette /* 84 1.1 pgoyette * Failure case reported by Greg Troxel <gdt (at) NetBSD.org>. 85 1.1 pgoyette * Rev. 1.11 incorrectly returns 5 with filling the buffer 86 1.1 pgoyette * with "1000". 87 1.1 pgoyette */ 88 1.1 pgoyette { 5, 1048258238, "", 89 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0G" }, 90 1.1 pgoyette /* Similar case it prints 1000 where it shouldn't */ 91 1.1 pgoyette { 5, 1023488, "", 92 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" }, 93 1.1 pgoyette { 5, 1023999, "", 94 1.1 pgoyette HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" }, 95 1.1 pgoyette }; 96 1.1 pgoyette 97 1.1 pgoyette struct hnflags { 98 1.1 pgoyette int hf_flags; 99 1.1 pgoyette const char *hf_name; 100 1.1 pgoyette }; 101 1.1 pgoyette 102 1.1 pgoyette const struct hnflags scale_flags[] = { 103 1.1 pgoyette { HN_GETSCALE, "HN_GETSCALE" }, 104 1.1 pgoyette { HN_AUTOSCALE, "HN_AUTOSCALE" }, 105 1.1 pgoyette }; 106 1.1 pgoyette const struct hnflags normal_flags[] = { 107 1.1 pgoyette { HN_DECIMAL, "HN_DECIMAL" }, 108 1.1 pgoyette { HN_NOSPACE, "HN_NOSPACE" }, 109 1.1 pgoyette { HN_B, "HN_B" }, 110 1.1 pgoyette { HN_DIVISOR_1000, "HN_DIVISOR_1000" }, 111 1.1 pgoyette }; 112 1.1 pgoyette 113 1.2 jruoho const char *formatflags(char *, size_t, const struct hnflags *, size_t, int); 114 1.2 jruoho void newline(void); 115 1.6 joerg void w_printf(const char *, ...) __printflike(1, 2); 116 1.2 jruoho int main(int, char *[]); 117 1.1 pgoyette 118 1.1 pgoyette const char * 119 1.1 pgoyette formatflags(char *buf, size_t buflen, const struct hnflags *hfs, 120 1.1 pgoyette size_t hfslen, int flags) 121 1.1 pgoyette { 122 1.1 pgoyette const struct hnflags *hf; 123 1.1 pgoyette char *p = buf; 124 1.1 pgoyette ssize_t len = buflen; 125 1.1 pgoyette unsigned int i, found; 126 1.1 pgoyette int n; 127 1.1 pgoyette 128 1.1 pgoyette if (flags == 0) { 129 1.1 pgoyette snprintf(buf, buflen, "0"); 130 1.1 pgoyette return (buf); 131 1.1 pgoyette } 132 1.1 pgoyette for (i = found = 0; i < hfslen && flags & ~found; i++) { 133 1.1 pgoyette hf = &hfs[i]; 134 1.1 pgoyette if (flags & hf->hf_flags) { 135 1.1 pgoyette found |= hf->hf_flags; 136 1.1 pgoyette n = snprintf(p, len, "|%s", hf->hf_name); 137 1.1 pgoyette if (n >= len) { 138 1.1 pgoyette p = buf; 139 1.1 pgoyette len = buflen; 140 1.1 pgoyette /* Print `flags' as number */ 141 1.1 pgoyette goto bad; 142 1.1 pgoyette } 143 1.1 pgoyette p += n; 144 1.1 pgoyette len -= n; 145 1.1 pgoyette } 146 1.1 pgoyette } 147 1.1 pgoyette flags &= ~found; 148 1.1 pgoyette if (flags) 149 1.1 pgoyette bad: 150 1.1 pgoyette snprintf(p, len, "|0x%x", flags); 151 1.1 pgoyette return (*buf == '|' ? buf + 1 : buf); 152 1.1 pgoyette } 153 1.1 pgoyette 154 1.1 pgoyette static int col, bol = 1; 155 1.1 pgoyette void 156 1.1 pgoyette newline(void) 157 1.1 pgoyette { 158 1.1 pgoyette 159 1.1 pgoyette fprintf(stderr, "\n"); 160 1.1 pgoyette col = 0; 161 1.1 pgoyette bol = 1; 162 1.1 pgoyette } 163 1.1 pgoyette 164 1.1 pgoyette void 165 1.1 pgoyette w_printf(const char *fmt, ...) 166 1.1 pgoyette { 167 1.1 pgoyette char buf[80]; 168 1.1 pgoyette va_list ap; 169 1.1 pgoyette int n; 170 1.1 pgoyette 171 1.1 pgoyette va_start(ap, fmt); 172 1.1 pgoyette if (col >= 0) { 173 1.1 pgoyette n = vsnprintf(buf, sizeof(buf), fmt, ap); 174 1.1 pgoyette if (n >= (int)sizeof(buf)) { 175 1.1 pgoyette col = -1; 176 1.1 pgoyette goto overflow; 177 1.1 pgoyette } else if (n == 0) 178 1.1 pgoyette goto out; 179 1.1 pgoyette 180 1.1 pgoyette if (!bol) { 181 1.1 pgoyette if (col + n > 75) 182 1.1 pgoyette fprintf(stderr, "\n "), col = 4; 183 1.1 pgoyette else 184 1.1 pgoyette fprintf(stderr, " "), col++; 185 1.1 pgoyette } 186 1.1 pgoyette fprintf(stderr, "%s", buf); 187 1.1 pgoyette col += n; 188 1.1 pgoyette bol = 0; 189 1.1 pgoyette } else { 190 1.1 pgoyette overflow: 191 1.1 pgoyette vfprintf(stderr, fmt, ap); 192 1.1 pgoyette } 193 1.1 pgoyette out: 194 1.1 pgoyette va_end(ap); 195 1.1 pgoyette } 196 1.1 pgoyette 197 1.5 jruoho ATF_TC(humanize_number_basic); 198 1.5 jruoho ATF_TC_HEAD(humanize_number_basic, tc) 199 1.1 pgoyette { 200 1.1 pgoyette 201 1.1 pgoyette atf_tc_set_md_var(tc, "descr", "Test humanize_number(3)"); 202 1.1 pgoyette } 203 1.1 pgoyette 204 1.5 jruoho ATF_TC_BODY(humanize_number_basic, tc) 205 1.1 pgoyette { 206 1.1 pgoyette char fbuf[128]; 207 1.1 pgoyette const struct hnopts *ho; 208 1.1 pgoyette char *buf = NULL; 209 1.1 pgoyette size_t buflen = 0; 210 1.1 pgoyette unsigned int i; 211 1.1 pgoyette int rv = 0; 212 1.1 pgoyette 213 1.1 pgoyette for (i = 0; i < __arraycount(hnopts); i++) { 214 1.1 pgoyette ho = &hnopts[i]; 215 1.1 pgoyette if (buflen < ho->ho_len) { 216 1.1 pgoyette buflen = ho->ho_len; 217 1.1 pgoyette buf = realloc(buf, buflen); 218 1.1 pgoyette if (buf == NULL) 219 1.4 christos atf_tc_fail("realloc(..., %zu) failed", buflen); 220 1.1 pgoyette } 221 1.1 pgoyette 222 1.1 pgoyette rv = humanize_number(buf, ho->ho_len, ho->ho_num, 223 1.1 pgoyette ho->ho_suffix, ho->ho_scale, ho->ho_flags); 224 1.1 pgoyette 225 1.1 pgoyette if (rv == ho->ho_retval && 226 1.1 pgoyette (rv == -1 || strcmp(buf, ho->ho_retstr) == 0)) 227 1.1 pgoyette continue; 228 1.1 pgoyette 229 1.6 joerg w_printf("humanize_number(\"%s\", %zu, %" PRId64 ",", 230 1.1 pgoyette ho->ho_retstr, ho->ho_len, ho->ho_num); 231 1.1 pgoyette w_printf("\"%s\",", ho->ho_suffix); 232 1.1 pgoyette w_printf("%s,", formatflags(fbuf, sizeof(fbuf), scale_flags, 233 1.1 pgoyette sizeof(scale_flags) / sizeof(scale_flags[0]), 234 1.1 pgoyette ho->ho_scale)); 235 1.1 pgoyette w_printf("%s)", formatflags(fbuf, sizeof(fbuf), normal_flags, 236 1.1 pgoyette sizeof(normal_flags) / sizeof(normal_flags[0]), 237 1.1 pgoyette ho->ho_flags)); 238 1.1 pgoyette w_printf("= %d,", ho->ho_retval); 239 1.1 pgoyette w_printf("but got"); 240 1.1 pgoyette w_printf("%d/[%s]", rv, rv == -1 ? "" : buf); 241 1.1 pgoyette newline(); 242 1.1 pgoyette atf_tc_fail_nonfatal("Failed for table entry %d", i); 243 1.1 pgoyette } 244 1.9 christos free(buf); 245 1.1 pgoyette } 246 1.1 pgoyette 247 1.5 jruoho ATF_TC(humanize_number_big); 248 1.5 jruoho ATF_TC_HEAD(humanize_number_big, tc) 249 1.2 jruoho { 250 1.2 jruoho 251 1.8 jruoho atf_tc_set_md_var(tc, "descr", "Test humanize " 252 1.8 jruoho "big numbers (PR lib/44097)"); 253 1.2 jruoho } 254 1.2 jruoho 255 1.5 jruoho ATF_TC_BODY(humanize_number_big, tc) 256 1.2 jruoho { 257 1.2 jruoho char buf[1024]; 258 1.2 jruoho int rv; 259 1.2 jruoho 260 1.2 jruoho /* 261 1.3 jruoho * Seems to work. 262 1.2 jruoho */ 263 1.2 jruoho (void)memset(buf, 0, sizeof(buf)); 264 1.2 jruoho 265 1.7 christos rv = humanize_number(buf, 10, 10000, "", HN_AUTOSCALE, HN_NOSPACE); 266 1.2 jruoho 267 1.2 jruoho ATF_REQUIRE(rv != -1); 268 1.7 christos ATF_CHECK_STREQ(buf, "10000"); 269 1.2 jruoho 270 1.2 jruoho /* 271 1.2 jruoho * A bogus value with large number. 272 1.2 jruoho */ 273 1.2 jruoho (void)memset(buf, 0, sizeof(buf)); 274 1.2 jruoho 275 1.7 christos rv = humanize_number(buf, 10, INT64_MAX, "", HN_AUTOSCALE, HN_NOSPACE); 276 1.2 jruoho 277 1.2 jruoho ATF_REQUIRE(rv != -1); 278 1.2 jruoho ATF_REQUIRE(strcmp(buf, "0") != 0); 279 1.2 jruoho 280 1.2 jruoho /* 281 1.2 jruoho * Large buffer with HN_AUTOSCALE. Entirely bogus. 282 1.2 jruoho */ 283 1.2 jruoho (void)memset(buf, 0, sizeof(buf)); 284 1.2 jruoho 285 1.2 jruoho rv = humanize_number(buf, sizeof(buf), 10000, "", 286 1.2 jruoho HN_AUTOSCALE, HN_NOSPACE); 287 1.2 jruoho 288 1.2 jruoho ATF_REQUIRE(rv != -1); 289 1.2 jruoho ATF_REQUIRE(strcmp(buf, "0%d%s%d%s%s%s") != 0); 290 1.10 kre /* 291 1.10 kre * PR lib/54053: before version 1.18 the output was nonsense 292 1.10 kre * with HN_AUTOSCALE and a buffer big enough to not need scaling 293 1.10 kre */ 294 1.10 kre ATF_REQUIRE(strcmp(buf, "10000") == 0); 295 1.2 jruoho 296 1.2 jruoho /* 297 1.2 jruoho * Tight buffer. 298 1.2 jruoho * 299 1.7 christos * The man page says that len must be at least 4. 300 1.7 christos * 3 works, but anything less that will not. This 301 1.7 christos * is because baselen starts with 2 for positive 302 1.7 christos * numbers. 303 1.2 jruoho */ 304 1.2 jruoho (void)memset(buf, 0, sizeof(buf)); 305 1.2 jruoho 306 1.7 christos rv = humanize_number(buf, 3, 1, "", HN_AUTOSCALE, HN_NOSPACE); 307 1.2 jruoho 308 1.2 jruoho ATF_REQUIRE(rv != -1); 309 1.2 jruoho } 310 1.2 jruoho 311 1.1 pgoyette ATF_TP_ADD_TCS(tp) 312 1.1 pgoyette { 313 1.1 pgoyette 314 1.5 jruoho ATF_TP_ADD_TC(tp, humanize_number_basic); 315 1.5 jruoho ATF_TP_ADD_TC(tp, humanize_number_big); 316 1.1 pgoyette 317 1.1 pgoyette return atf_no_error(); 318 1.1 pgoyette } 319