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