/*++ /* NAME /* format_tv 3 /* SUMMARY /* format time value with sane precision /* SYNOPSIS /* #include /* /* 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 /* Utility library. */ #include #include /* 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 #include #include 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