t_humanize_number.c revision 276478
146197Sphk/*	$NetBSD: t_humanize_number.c,v 1.8 2012/03/18 07:14:08 jruoho Exp $	*/
246197Sphk
346197Sphk/*-
446197Sphk * Copyright (c) 2010, 2011 The NetBSD Foundation, Inc.
546197Sphk * All rights reserved.
646197Sphk *
746197Sphk * Redistribution and use in source and binary forms, with or without
846197Sphk * modification, are permitted provided that the following conditions
950477Speter * are met:
1046197Sphk * 1. Redistributions of source code must retain the above copyright
1146197Sphk *    notice, this list of conditions and the following disclaimer.
1246155Sphk * 2. Redistributions in binary form must reproduce the above copyright
1346155Sphk *    notice, this list of conditions and the following disclaimer in the
1446155Sphk *    documentation and/or other materials provided with the distribution.
1546155Sphk *
1646155Sphk * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1746155Sphk * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1846155Sphk * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1946155Sphk * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2046155Sphk * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2146155Sphk * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2246155Sphk * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2357163Srwatson * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2446155Sphk * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2546155Sphk * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2646155Sphk * POSSIBILITY OF SUCH DAMAGE.
2746155Sphk */
2846155Sphk
2957163Srwatson#include <atf-c.h>
3057163Srwatson
3157163Srwatson#include <err.h>
3257163Srwatson#include <inttypes.h>
3357163Srwatson#include <stdarg.h>
3457163Srwatson#include <stdio.h>
3557163Srwatson#include <stdlib.h>
3657163Srwatson#include <string.h>
3761235Srwatson#ifdef __FreeBSD__
3861235Srwatson#include <libutil.h>
3961235Srwatson#else
4061235Srwatson#include <util.h>
4161235Srwatson#endif
4268024Srwatson
4368024Srwatsonconst struct hnopts {
4468024Srwatson	size_t ho_len;
4568024Srwatson	int64_t ho_num;
4668024Srwatson	const char *ho_suffix;
4746155Sphk	int ho_scale;
4846155Sphk	int ho_flags;
4972786Srwatson	int ho_retval;			/* expected return value */
5072786Srwatson	const char *ho_retstr;		/* expected string in buffer */
5172786Srwatson} hnopts[] = {
5272786Srwatson	/*
5346155Sphk	 * Rev. 1.6 produces "10.0".
5446155Sphk	 */
5546155Sphk	{ 5, 10737418236ULL * 1024, "",
5646155Sphk	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10T" },
5746155Sphk
5846155Sphk	{ 5, 10450000, "",
5972786Srwatson	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" },
6046155Sphk	{ 5, 10500000, "",		/* just for reference */
6146155Sphk	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 3, "10M" },
6246155Sphk
6346155Sphk	/*
6446155Sphk	 * Trailing space.  Rev. 1.7 produces "1 ".
6546155Sphk	 */
6651398Sphk	{ 5, 1, "", 0, HN_NOSPACE, 1, "1" },
6751398Sphk
6869781Sdwmalone	{ 5, 1, "", 0, 0, 2, "1 " }, /* just for reference */
6946155Sphk	{ 5, 1, "", 0, HN_B, 3, "1 B" }, /* and more ... */
7046155Sphk	{ 5, 1, "", 0, HN_DECIMAL, 2, "1 " },
7146155Sphk	{ 5, 1, "", 0, HN_NOSPACE | HN_B, 2, "1B" },
7246155Sphk	{ 5, 1, "", 0, HN_B | HN_DECIMAL, 3, "1 B" },
7346155Sphk	{ 5, 1, "", 0, HN_NOSPACE | HN_B | HN_DECIMAL, 2, "1B" },
7446155Sphk
7546155Sphk	/*
7646155Sphk	 * Space and HN_B.  Rev. 1.7 produces "1B".
7746155Sphk	 */
7846155Sphk	{ 5, 1, "", HN_AUTOSCALE, HN_B, 3, "1 B" },
7972786Srwatson	{ 5, 1000, "",			/* just for reference */
8072786Srwatson	  HN_AUTOSCALE, HN_B, 3, "1 K" },
8172786Srwatson
8246155Sphk	/*
8346155Sphk	 * Truncated output.  Rev. 1.7 produces "1.0 K".
8446155Sphk	 */
8546155Sphk#ifndef __FreeBSD__
8646155Sphk	{ 6, 1000, "A", HN_AUTOSCALE, HN_DECIMAL, -1, "" },
8746155Sphk
8846155Sphk	/*
8972786Srwatson	 * Failure case reported by Greg Troxel <gdt@NetBSD.org>.
9072786Srwatson	 * Rev. 1.11 incorrectly returns 5 with filling the buffer
9172786Srwatson	 * with "1000".
9272786Srwatson	 */
9372786Srwatson	{ 5, 1048258238, "",
9472786Srwatson	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0G" },
9572786Srwatson	/* Similar case it prints 1000 where it shouldn't */
9672786Srwatson	{ 5, 1023488, "",
9772786Srwatson	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" },
9872786Srwatson#endif
9972786Srwatson	{ 5, 1023999, "",
10072786Srwatson	  HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL, 4, "1.0M" },
10172786Srwatson};
10272786Srwatson
10372786Srwatsonstruct hnflags {
10472786Srwatson	int hf_flags;
10572786Srwatson	const char *hf_name;
10672786Srwatson};
10772786Srwatson
10846155Sphkconst struct hnflags scale_flags[] = {
10972786Srwatson	{ HN_GETSCALE, "HN_GETSCALE" },
11046155Sphk	{ HN_AUTOSCALE, "HN_AUTOSCALE" },
11146155Sphk};
11246155Sphkconst struct hnflags normal_flags[] = {
11372786Srwatson	{ HN_DECIMAL, "HN_DECIMAL" },
11446155Sphk	{ HN_NOSPACE, "HN_NOSPACE" },
11546155Sphk	{ HN_B, "HN_B" },
11646155Sphk	{ HN_DIVISOR_1000, "HN_DIVISOR_1000" },
11746155Sphk};
11846155Sphk
11946155Sphkconst char *formatflags(char *, size_t, const struct hnflags *, size_t, int);
12046155Sphkvoid	    newline(void);
12172786Srwatsonvoid	    w_printf(const char *, ...) __printflike(1, 2);
12246155Sphkint	    main(int, char *[]);
12372786Srwatson
12446155Sphkconst char *
12546155Sphkformatflags(char *buf, size_t buflen, const struct hnflags *hfs,
12672786Srwatson    size_t hfslen, int flags)
12746155Sphk{
12846155Sphk	const struct hnflags *hf;
12946155Sphk	char *p = buf;
13046155Sphk	ssize_t len = buflen;
13146155Sphk	unsigned int i, found;
13272786Srwatson	int n;
13346155Sphk
13446155Sphk	if (flags == 0) {
13546155Sphk		snprintf(buf, buflen, "0");
13672786Srwatson		return (buf);
13746155Sphk	}
13846155Sphk	for (i = found = 0; i < hfslen && flags & ~found; i++) {
13946155Sphk		hf = &hfs[i];
14046155Sphk		if (flags & hf->hf_flags) {
14146155Sphk			found |= hf->hf_flags;
14246155Sphk			n = snprintf(p, len, "|%s", hf->hf_name);
14346155Sphk			if (n >= len) {
14472786Srwatson				p = buf;
14546155Sphk				len = buflen;
14672786Srwatson				/* Print `flags' as number */
14746155Sphk				goto bad;
14846155Sphk			}
14946155Sphk			p += n;
15046155Sphk			len -= n;
15146155Sphk		}
15246155Sphk	}
15372786Srwatson	flags &= ~found;
15446155Sphk	if (flags)
15546155Sphkbad:
15646155Sphk		snprintf(p, len, "|0x%x", flags);
15746155Sphk	return (*buf == '|' ? buf + 1 : buf);
15861235Srwatson}
15961235Srwatson
16061235Srwatsonstatic int col, bol = 1;
16146155Sphkvoid
16272786Srwatsonnewline(void)
16346155Sphk{
16446155Sphk
16546155Sphk	fprintf(stderr, "\n");
16646155Sphk	col = 0;
16746155Sphk	bol = 1;
16872786Srwatson}
16972786Srwatson
17072786Srwatsonvoid
17172786Srwatsonw_printf(const char *fmt, ...)
17272786Srwatson{
17372786Srwatson	char buf[80];
17472786Srwatson	va_list ap;
17572786Srwatson	int n;
17672786Srwatson
17772786Srwatson	va_start(ap, fmt);
17872786Srwatson	if (col >= 0) {
17972786Srwatson		n = vsnprintf(buf, sizeof(buf), fmt, ap);
18072786Srwatson		if (n >= (int)sizeof(buf)) {
18172786Srwatson			col = -1;
18272786Srwatson			goto overflow;
18372786Srwatson		} else if (n == 0)
18472786Srwatson			goto out;
18572786Srwatson
18672786Srwatson		if (!bol) {
18772786Srwatson			if (col + n > 75)
18872786Srwatson				fprintf(stderr, "\n    "), col = 4;
18972786Srwatson			else
19072786Srwatson				fprintf(stderr, " "), col++;
19172786Srwatson		}
19272786Srwatson		fprintf(stderr, "%s", buf);
19372786Srwatson		col += n;
19472786Srwatson		bol = 0;
19572786Srwatson	} else {
19672786Srwatsonoverflow:
197		vfprintf(stderr, fmt, ap);
198	}
199out:
200	va_end(ap);
201}
202
203ATF_TC(humanize_number_basic);
204ATF_TC_HEAD(humanize_number_basic, tc)
205{
206
207	atf_tc_set_md_var(tc, "descr", "Test humanize_number(3)");
208}
209
210ATF_TC_BODY(humanize_number_basic, tc)
211{
212	char fbuf[128];
213	const struct hnopts *ho;
214	char *buf = NULL;
215	size_t buflen = 0;
216	unsigned int i;
217	int rv = 0;
218
219	for (i = 0; i < __arraycount(hnopts); i++) {
220		ho = &hnopts[i];
221		if (buflen < ho->ho_len) {
222			buflen = ho->ho_len;
223			buf = realloc(buf, buflen);
224			if (buf == NULL)
225				atf_tc_fail("realloc(..., %zu) failed", buflen);
226		}
227
228		rv = humanize_number(buf, ho->ho_len, ho->ho_num,
229		    ho->ho_suffix, ho->ho_scale, ho->ho_flags);
230
231		if (rv == ho->ho_retval &&
232		    (rv == -1 || strcmp(buf, ho->ho_retstr) == 0))
233			continue;
234
235		w_printf("humanize_number(\"%s\", %zu, %" PRId64 ",",
236		    ho->ho_retstr, ho->ho_len, ho->ho_num);
237		w_printf("\"%s\",", ho->ho_suffix);
238		w_printf("%s,", formatflags(fbuf, sizeof(fbuf), scale_flags,
239		    sizeof(scale_flags) / sizeof(scale_flags[0]),
240		    ho->ho_scale));
241		w_printf("%s)", formatflags(fbuf, sizeof(fbuf), normal_flags,
242		    sizeof(normal_flags) / sizeof(normal_flags[0]),
243		    ho->ho_flags));
244		w_printf("= %d,", ho->ho_retval);
245		w_printf("but got");
246		w_printf("%d/[%s]", rv, rv == -1 ? "" : buf);
247		newline();
248		atf_tc_fail_nonfatal("Failed for table entry %d", i);
249	}
250}
251
252ATF_TC(humanize_number_big);
253ATF_TC_HEAD(humanize_number_big, tc)
254{
255
256	atf_tc_set_md_var(tc, "descr", "Test humanize "
257	    "big numbers (PR lib/44097)");
258}
259
260ATF_TC_BODY(humanize_number_big, tc)
261{
262	char buf[1024];
263	int rv;
264
265	/*
266	 * Seems to work.
267	 */
268	(void)memset(buf, 0, sizeof(buf));
269
270	rv = humanize_number(buf, 10, 10000, "", HN_AUTOSCALE, HN_NOSPACE);
271
272	ATF_REQUIRE(rv != -1);
273	ATF_CHECK_STREQ(buf, "10000");
274
275	/*
276	 * A bogus value with large number.
277	 */
278	(void)memset(buf, 0, sizeof(buf));
279
280	rv = humanize_number(buf, 10, INT64_MAX, "", HN_AUTOSCALE, HN_NOSPACE);
281
282	ATF_REQUIRE(rv != -1);
283	ATF_REQUIRE(strcmp(buf, "0") != 0);
284
285	/*
286	 * Large buffer with HN_AUTOSCALE. Entirely bogus.
287	 */
288	(void)memset(buf, 0, sizeof(buf));
289
290	rv = humanize_number(buf, sizeof(buf), 10000, "",
291	    HN_AUTOSCALE, HN_NOSPACE);
292
293	ATF_REQUIRE(rv != -1);
294	ATF_REQUIRE(strcmp(buf, "0%d%s%d%s%s%s") != 0);
295
296	/*
297	 * Tight buffer.
298	 *
299	 * The man page says that len must be at least 4.
300	 * 3 works, but anything less that will not. This
301	 * is because baselen starts with 2 for positive
302	 * numbers.
303	 */
304	(void)memset(buf, 0, sizeof(buf));
305
306	rv = humanize_number(buf, 3, 1, "", HN_AUTOSCALE, HN_NOSPACE);
307
308	ATF_REQUIRE(rv != -1);
309}
310
311ATF_TP_ADD_TCS(tp)
312{
313
314	ATF_TP_ADD_TC(tp, humanize_number_basic);
315	ATF_TP_ADD_TC(tp, humanize_number_big);
316
317	return atf_no_error();
318}
319