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