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