aboutsummaryrefslogtreecommitdiff
path: root/contrib/ntp/libntp/mstolfp.c
blob: 428f71bdbae966c095349cbdf73968dd3b00d621 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/*
 * mstolfp - convert an ascii string in milliseconds to an l_fp number
 */
#include <config.h>
#include <stdio.h>
#include <ctype.h>

#include "ntp_fp.h"
#include "ntp_stdlib.h"

int
mstolfp(
	const char *str,
	l_fp *lfp
	)
{
	int        ch, neg = 0; 
	u_int32    q, r;

	/*
	 * We understand numbers of the form:
	 *
	 * [spaces][-|+][digits][.][digits][spaces|\n|\0]
	 *
	 * This is kinda hack.  We use 'atolfp' to do the basic parsing
	 * (after some initial checks) and then divide the result by
	 * 1000.  The original implementation avoided that by
	 * hacking up the input string to move the decimal point, but
	 * that needed string manipulations prone to buffer overruns.
	 * To avoid that trouble we do the conversion first and adjust
	 * the result.
	 */
	
	while (isspace(ch = *(const unsigned char*)str))
		++str;
	
	switch (ch) {
	    case '-': neg = TRUE;
	    case '+': ++str;
	    default : break;
	}
	
	if (!isdigit(ch = *(const unsigned char*)str) && (ch != '.'))
		return 0;
	if (!atolfp(str, lfp))
		return 0;

	/* now do a chained/overlapping division by 1000 to get from
	 * seconds to msec. 1000 is small enough to go with temporary
	 * 32bit accus for Q and R.
	 */
	q = lfp->l_ui / 1000u;
	r = lfp->l_ui - (q * 1000u);
	lfp->l_ui = q;

	r = (r << 16) | (lfp->l_uf >> 16);
	q = r / 1000u;
	r = ((r - q * 1000) << 16) | (lfp->l_uf & 0x0FFFFu);
	lfp->l_uf = q << 16;
	q = r / 1000;
	lfp->l_uf |= q;
	r -= q * 1000u;

	/* fix sign */
	if (neg)
		L_NEG(lfp);
	/* round */
	if (r >= 500)
		L_ADDF(lfp, (neg ? -1 : 1));
	return 1;
}