diff options
Diffstat (limited to 'src/util/format_tv.c')
-rw-r--r-- | src/util/format_tv.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/src/util/format_tv.c b/src/util/format_tv.c new file mode 100644 index 0000000..a932bdb --- /dev/null +++ b/src/util/format_tv.c @@ -0,0 +1,160 @@ +/*++ +/* NAME +/* format_tv 3 +/* SUMMARY +/* format time value with sane precision +/* SYNOPSIS +/* #include <format_tv.h> +/* +/* VSTRING *format_tv(buffer, sec, usec, sig_dig, max_dig) +/* VSTRING *buffer; +/* long sec; +/* long usec; +/* int sig_dig; +/* int max_dig; +/* DESCRIPTION +/* format_tv() formats the specified time as a floating-point +/* number while suppressing irrelevant digits in the output. +/* Large numbers are always rounded up to an integral number +/* of seconds. Small numbers are produced with a limited number +/* of significant digits, provided that the result does not +/* exceed the limit on the total number of digits after the +/* decimal point. Trailing zeros are always omitted from the +/* output. +/* +/* Arguments: +/* .IP buffer +/* The buffer to which the result is appended. +/* .IP sec +/* The seconds portion of the time value. +/* .IP usec +/* The microseconds portion of the time value. +/* .IP sig_dig +/* The maximal number of significant digits when formatting +/* small numbers. Leading nulls don't count as significant, +/* and trailing nulls are not included in the output. Specify +/* a number in the range 1..6. +/* .IP max_dig +/* The maximal number of all digits after the decimal point. +/* Specify a number in the range 0..6. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this +/* software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +#include <sys_defs.h> + +/* Utility library. */ + +#include <msg.h> +#include <format_tv.h> + +/* Application-specific. */ + +#define MILLION 1000000 + +/* format_tv - print time with limited precision */ + +VSTRING *format_tv(VSTRING *buf, long sec, long usec, + int sig_dig, int max_dig) +{ + static int pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000}; + int n; + int rem; + int wid; + int ures; + + /* + * Sanity check. + */ + if (max_dig < 0 || max_dig > 6) + msg_panic("format_tv: bad maximum decimal count %d", max_dig); + if (sec < 0 || usec < 0 || usec > MILLION) + msg_panic("format_tv: bad time %lds %ldus", sec, usec); + if (sig_dig < 1 || sig_dig > 6) + msg_panic("format_tv: bad significant decimal count %d", sig_dig); + ures = MILLION / pow10[max_dig]; + wid = pow10[sig_dig]; + + /* + * Adjust the resolution to suppress irrelevant digits. + */ + if (ures < MILLION) { + if (sec > 0) { + for (n = 1; sec >= n && n <= wid / 10; n *= 10) + /* void */ ; + ures = (MILLION / wid) * n; + } else { + while (usec >= wid * ures) + ures *= 10; + } + } + + /* + * Round up the number if necessary. Leave thrash below the resolution. + */ + if (ures > 1) { + usec += ures / 2; + if (usec >= MILLION) { + sec += 1; + usec -= MILLION; + } + } + + /* + * Format the number. Truncate trailing null and thrash below resolution. + */ + vstring_sprintf_append(buf, "%ld", sec); + if (usec >= ures) { + VSTRING_ADDCH(buf, '.'); + for (rem = usec, n = MILLION / 10; rem >= ures && n > 0; n /= 10) { + VSTRING_ADDCH(buf, "0123456789"[rem / n]); + rem %= n; + } + } + VSTRING_TERMINATE(buf); + return (buf); +} + +#ifdef TEST + +#include <stdio.h> +#include <stdlib.h> +#include <vstring_vstream.h> + +int main(int argc, char **argv) +{ + VSTRING *in = vstring_alloc(10); + VSTRING *out = vstring_alloc(10); + double tval; + int sec; + int usec; + int sig_dig; + int max_dig; + + while (vstring_get_nonl(in, VSTREAM_IN) > 0) { + vstream_printf(">> %s\n", vstring_str(in)); + if (vstring_str(in)[0] == 0 || vstring_str(in)[0] == '#') + continue; + if (sscanf(vstring_str(in), "%lf %d %d", &tval, &sig_dig, &max_dig) != 3) + msg_fatal("bad input: %s", vstring_str(in)); + sec = (int) tval; /* raw seconds */ + usec = (tval - sec) * MILLION; /* raw microseconds */ + VSTRING_RESET(out); + format_tv(out, sec, usec, sig_dig, max_dig); + vstream_printf("%s\n", vstring_str(out)); + vstream_fflush(VSTREAM_OUT); + } + vstring_free(in); + vstring_free(out); + return (0); +} + +#endif |