xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/format_tv.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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 
format_tv(VSTRING * buf,long sec,long usec,int sig_dig,int max_dig)67 VSTRING *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 
main(int argc,char ** argv)134 int     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