xref: /freebsd/contrib/ntp/libntp/mstolfp.c (revision 4e8d558c)
1 /*
2  * mstolfp - convert an ascii string in milliseconds to an l_fp number
3  */
4 #include <config.h>
5 #include <stdio.h>
6 #include <ctype.h>
7 
8 #include "ntp_fp.h"
9 #include "ntp_stdlib.h"
10 
11 int
12 mstolfp(
13 	const char *str,
14 	l_fp *lfp
15 	)
16 {
17 	int        ch, neg = 0;
18 	u_int32    q, r;
19 
20 	/*
21 	 * We understand numbers of the form:
22 	 *
23 	 * [spaces][-|+][digits][.][digits][spaces|\n|\0]
24 	 *
25 	 * This is kinda hack.  We use 'atolfp' to do the basic parsing
26 	 * (after some initial checks) and then divide the result by
27 	 * 1000.  The original implementation avoided that by
28 	 * hacking up the input string to move the decimal point, but
29 	 * that needed string manipulations prone to buffer overruns.
30 	 * To avoid that trouble we do the conversion first and adjust
31 	 * the result.
32 	 */
33 
34 	while (isspace(ch = *(const unsigned char*)str))
35 		++str;
36 
37 	switch (ch) {
38 	    case '-': neg = TRUE;
39 	    case '+': ++str;
40 	    default : break;
41 	}
42 
43 	if (!isdigit(ch = *(const unsigned char*)str) && (ch != '.'))
44 		return 0;
45 	if (!atolfp(str, lfp))
46 		return 0;
47 
48 	/* now do a chained/overlapping division by 1000 to get from
49 	 * seconds to msec. 1000 is small enough to go with temporary
50 	 * 32bit accus for Q and R.
51 	 */
52 	q = lfp->l_ui / 1000u;
53 	r = lfp->l_ui - (q * 1000u);
54 	lfp->l_ui = q;
55 
56 	r = (r << 16) | (lfp->l_uf >> 16);
57 	q = r / 1000u;
58 	r = ((r - q * 1000) << 16) | (lfp->l_uf & 0x0FFFFu);
59 	lfp->l_uf = q << 16;
60 	q = r / 1000;
61 	lfp->l_uf |= q;
62 	r -= q * 1000u;
63 
64 	/* fix sign */
65 	if (neg)
66 		L_NEG(lfp);
67 	/* round */
68 	if (r >= 500)
69 		L_ADDF(lfp, (neg ? -1 : 1));
70 	return 1;
71 }
72