1/*	$NetBSD: format_tv.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
2
3/*++
4/* NAME
5/*	format_tv 3
6/* SUMMARY
7/*	format time value with sane precision
8/* SYNOPSIS
9/*	#include <format_tv.h>
10/*
11/*	VSTRING	*format_tv(buffer, sec, usec, sig_dig, max_dig)
12/*	VSTRING	*buffer;
13/*	long	sec;
14/*	long	usec;
15/*	int	sig_dig;
16/*	int	max_dig;
17/* DESCRIPTION
18/*	format_tv() formats the specified time as a floating-point
19/*	number while suppressing irrelevant digits in the output.
20/*	Large numbers are always rounded up to an integral number
21/*	of seconds. Small numbers are produced with a limited number
22/*	of significant digits, provided that the result does not
23/*	exceed the limit on the total number of digits after the
24/*	decimal point.  Trailing zeros are always omitted from the
25/*	output.
26/*
27/*	Arguments:
28/* .IP buffer
29/*	The buffer to which the result is appended.
30/* .IP sec
31/*	The seconds portion of the time value.
32/* .IP usec
33/*	The microseconds portion of the time value.
34/* .IP sig_dig
35/*	The maximal number of significant digits when formatting
36/*	small numbers. Leading nulls don't count as significant,
37/*	and trailing nulls are not included in the output.  Specify
38/*	a number in the range 1..6.
39/* .IP max_dig
40/*	The maximal number of all digits after the decimal point.
41/*	Specify a number in the range 0..6.
42/* LICENSE
43/* .ad
44/* .fi
45/*	The Secure Mailer license must be distributed with this
46/*	software.
47/* AUTHOR(S)
48/*	Wietse Venema
49/*	IBM T.J. Watson Research
50/*	P.O. Box 704
51/*	Yorktown Heights, NY 10598, USA
52/*--*/
53
54#include <sys_defs.h>
55
56/* Utility library. */
57
58#include <msg.h>
59#include <format_tv.h>
60
61/* Application-specific. */
62
63#define MILLION	1000000
64
65/* format_tv - print time with limited precision */
66
67VSTRING *format_tv(VSTRING *buf, long sec, long usec,
68		           int sig_dig, int max_dig)
69{
70    static int pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000};
71    int     n;
72    int     rem;
73    int     wid;
74    int     ures;
75
76    /*
77     * Sanity check.
78     */
79    if (max_dig < 0 || max_dig > 6)
80	msg_panic("format_tv: bad maximum decimal count %d", max_dig);
81    if (sec < 0 || usec < 0 || usec > MILLION)
82	msg_panic("format_tv: bad time %lds %ldus", sec, usec);
83    if (sig_dig < 1 || sig_dig > 6)
84	msg_panic("format_tv: bad significant decimal count %d", sig_dig);
85    ures = MILLION / pow10[max_dig];
86    wid = pow10[sig_dig];
87
88    /*
89     * Adjust the resolution to suppress irrelevant digits.
90     */
91    if (ures < MILLION) {
92	if (sec > 0) {
93	    for (n = 1; sec >= n && n <= wid / 10; n *= 10)
94		 /* void */ ;
95	    ures = (MILLION / wid) * n;
96	} else {
97	    while (usec >= wid * ures)
98		ures *= 10;
99	}
100    }
101
102    /*
103     * Round up the number if necessary. Leave thrash below the resolution.
104     */
105    if (ures > 1) {
106	usec += ures / 2;
107	if (usec >= MILLION) {
108	    sec += 1;
109	    usec -= MILLION;
110	}
111    }
112
113    /*
114     * Format the number. Truncate trailing null and thrash below resolution.
115     */
116    vstring_sprintf_append(buf, "%ld", sec);
117    if (usec >= ures) {
118	VSTRING_ADDCH(buf, '.');
119	for (rem = usec, n = MILLION / 10; rem >= ures && n > 0; n /= 10) {
120	    VSTRING_ADDCH(buf, "0123456789"[rem / n]);
121	    rem %= n;
122	}
123    }
124    VSTRING_TERMINATE(buf);
125    return (buf);
126}
127
128#ifdef TEST
129
130#include <stdio.h>
131#include <stdlib.h>
132#include <vstring_vstream.h>
133
134int     main(int argc, char **argv)
135{
136    VSTRING *in = vstring_alloc(10);
137    VSTRING *out = vstring_alloc(10);
138    double  tval;
139    int     sec;
140    int     usec;
141    int     sig_dig;
142    int     max_dig;
143
144    while (vstring_get_nonl(in, VSTREAM_IN) > 0) {
145	vstream_printf(">> %s\n", vstring_str(in));
146	if (vstring_str(in)[0] == 0 || vstring_str(in)[0] == '#')
147	    continue;
148	if (sscanf(vstring_str(in), "%lf %d %d", &tval, &sig_dig, &max_dig) != 3)
149	    msg_fatal("bad input: %s", vstring_str(in));
150	sec = (int) tval;			/* raw seconds */
151	usec = (tval - sec) * MILLION;		/* raw microseconds */
152	VSTRING_RESET(out);
153	format_tv(out, sec, usec, sig_dig, max_dig);
154	vstream_printf("%s\n", vstring_str(out));
155	vstream_fflush(VSTREAM_OUT);
156    }
157    vstring_free(in);
158    vstring_free(out);
159    return (0);
160}
161
162#endif
163