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