summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Millwood <thebenmachine@gmail.com>2010-03-18 16:40:34 +0000
committerTim Angus <tim@ngus.net>2013-01-03 00:17:31 +0000
commit0a462a69f96b2b1ab38147842dd58d6816f12e17 (patch)
tree4335342da7013d9a56238e3fe820041c5293cacb
parent5c68e66953a13593e1272795ec07c55b27050af8 (diff)
* Add strtod and strtol to bg_lib.c
-rw-r--r--src/game/bg_lib.c344
-rw-r--r--src/game/bg_lib.h2
2 files changed, 346 insertions, 0 deletions
diff --git a/src/game/bg_lib.c b/src/game/bg_lib.c
index 877ca68a..5f242934 100644
--- a/src/game/bg_lib.c
+++ b/src/game/bg_lib.c
@@ -1389,6 +1389,248 @@ double atof( const char *string )
return value * sign;
}
+/*
+==============
+strtod
+
+Without an errno variable, this is a fair bit less useful than it is in libc
+but it's still a fair bit more capable than atof or _atof
+Handles inf[inity], nan (ignoring case), hexadecimals, and decimals
+Handles decimal exponents like 10e10 and hex exponents like 0x7f8p20
+10e10 == 10000000000 (power of ten)
+0x7f8p20 == 0x7f800000 (decimal power of two)
+The variable pointed to by endptr will hold the location of the first character
+in the nptr string that was not used in the conversion
+==============
+*/
+double strtod( const char *nptr, const char **endptr )
+{
+ double res;
+ qboolean neg = qfalse;
+
+ // skip whitespace
+ while( isspace( *nptr ) )
+ nptr++;
+
+ // special string parsing
+ if( Q_stricmpn( nptr, "nan", 3 ) == 0 )
+ {
+ floatint_t nan;
+
+ if( endptr )
+ *endptr = &nptr[3];
+
+ // nan can be followed by a bracketed number (in hex, octal,
+ // or decimal) which is then put in the mantissa
+ // this can be used to generate signalling or quiet NaNs, for
+ // example (though I doubt it'll ever be used)
+ // note that nan(0) is infinity!
+ if( nptr[3] == '(' )
+ {
+ const char *end;
+ int mantissa = strtol( &nptr[4], &end, 0 );
+
+ if( *end == ')' )
+ {
+ nan.ui = 0x7f800000 | ( mantissa & 0x7fffff );
+
+ if( endptr )
+ *endptr = &end[1];
+ return nan.f;
+ }
+ }
+
+ nan.ui = 0x7fffffff;
+ return nan.f;
+ }
+
+ if( Q_stricmpn( nptr, "inf", 3 ) == 0 )
+ {
+ floatint_t inf;
+ inf.ui = 0x7f800000;
+
+ if( endptr == NULL )
+ return inf.f;
+
+ if( Q_stricmpn( &nptr[3], "inity", 5 ) == 0 )
+ *endptr = &nptr[8];
+ else
+ *endptr = &nptr[3];
+
+ return inf.f;
+ }
+
+ // normal numeric parsing
+ // sign
+ if( *nptr == '-' )
+ {
+ nptr++;
+ neg = qtrue;
+ }
+ else if( *nptr == '+' )
+ nptr++;
+
+ // hex
+ if( Q_stricmpn( nptr, "0x", 2 ) == 0 )
+ {
+ // track if we use any digits
+ const char *s = &nptr[1], *end = s;
+ nptr += 2;
+
+ for( res = 0;; )
+ {
+ if( isdigit( *nptr ) )
+ res = 16 * res + ( *nptr++ - '0' );
+ else if( *nptr >= 'A' && *nptr <= 'F' )
+ res = 16 * res + 10 + *nptr++ - 'A';
+ else if( *nptr >= 'a' && *nptr <= 'f' )
+ res = 16 * res + 10 + *nptr++ - 'a';
+ else
+ break;
+ }
+
+ // if nptr moved, save it
+ if( end + 1 < nptr )
+ end = nptr;
+
+ if( *nptr == '.' )
+ {
+ float place;
+ nptr++;
+
+ // 1.0 / 16.0 == 0.0625
+ // I don't expect the float accuracy to hold out for
+ // very long but since we need to know the length of
+ // the string anyway we keep on going regardless
+ for( place = 0.0625;; place /= 16.0 )
+ {
+ if( isdigit( *nptr ) )
+ res += place * ( *nptr++ - '0' );
+ else if( *nptr >= 'A' && *nptr <= 'F' )
+ res += place * ( 10 + *nptr++ - 'A' );
+ else if( *nptr >= 'a' && *nptr <= 'f' )
+ res += place * ( 10 + *nptr++ - 'a' );
+ else
+ break;
+ }
+
+ if( end < nptr )
+ end = nptr;
+ }
+
+ // parse an optional exponent, representing multiplication
+ // by a power of two
+ // exponents are only valid if we encountered at least one
+ // digit already (and have therefore set end to something)
+ if( end != s && tolower( *nptr ) == 'p' )
+ {
+ int exp;
+ float res2;
+ // apparently (confusingly) the exponent should be
+ // decimal
+ exp = strtol( &nptr[1], &end, 10 );
+ if( &nptr[1] == end )
+ {
+ // no exponent
+ if( endptr )
+ *endptr = nptr;
+ return res;
+ }
+ if( exp > 0 )
+ {
+ while( exp-- > 0 )
+ {
+ res2 = res * 2;
+ // check for infinity
+ if( res2 <= res )
+ break;
+ res = res2;
+ }
+ }
+ else
+ {
+ while( exp++ < 0 )
+ {
+ res2 = res / 2;
+ // check for underflow
+ if( res2 >= res )
+ break;
+ res = res2;
+ }
+ }
+ }
+ if( endptr )
+ *endptr = end;
+ return res;
+ }
+ // decimal
+ else
+ {
+ // track if we find any digits
+ const char *end = nptr, *p = nptr;
+ // this is most of the work
+ for( res = 0; isdigit( *nptr );
+ res = 10 * res + *nptr++ - '0' );
+ // if nptr moved, we read something
+ if( end < nptr )
+ end = nptr;
+ if( *nptr == '.' )
+ {
+ // fractional part
+ float place;
+ nptr++;
+ for( place = 0.1; isdigit( *nptr ); place /= 10.0 )
+ res += ( *nptr++ - '0' ) * place;
+ // if nptr moved, we read something
+ if( end + 1 < nptr )
+ end = nptr;
+ }
+ // exponent
+ // meaningless without having already read digits, so check
+ // we've set end to something
+ if( p != end && tolower( *nptr ) == 'e' )
+ {
+ int exp;
+ float res10;
+ exp = strtol( &nptr[1], &end, 10 );
+ if( &nptr[1] == end )
+ {
+ // no exponent
+ if( endptr )
+ *endptr = nptr;
+ return res;
+ }
+ if( exp > 0 )
+ {
+ while( exp-- > 0 )
+ {
+ res10 = res * 10;
+ // check for infinity to save us time
+ if( res10 <= res )
+ break;
+ res = res10;
+ }
+ }
+ else if( exp < 0 )
+ {
+ while( exp++ < 0 )
+ {
+ res10 = res / 10;
+ // check for underflow
+ // (test for 0 would probably be just
+ // as good)
+ if( res10 >= res )
+ break;
+ res = res10;
+ }
+ }
+ }
+ if( endptr )
+ *endptr = end;
+ return res;
+ }
+}
+
double _atof( const char **stringPtr )
{
const char *string;
@@ -1468,6 +1710,108 @@ double _atof( const char **stringPtr )
return value * sign;
}
+/*
+==============
+strtol
+
+Handles any base from 2 to 36. If base is 0 then it guesses
+decimal, hex, or octal based on the format of the number (leading 0 or 0x)
+Will not overflow - returns LONG_MIN or LONG_MAX as appropriate
+*endptr is set to the location of the first character not used
+==============
+*/
+long strtol( const char *nptr, const char **endptr, int base )
+{
+ long res;
+ qboolean pos = qtrue;
+
+ if( endptr )
+ *endptr = nptr;
+
+ // bases other than 0, 2, 8, 16 are very rarely used, but they're
+ // not much extra effort to support
+ if( base < 0 || base == 1 || base > 36 )
+ return 0;
+
+ // skip leading whitespace
+ while( isspace( *nptr ) )
+ nptr++;
+
+ // sign
+ if( *nptr == '-' )
+ {
+ nptr++;
+ pos = qfalse;
+ }
+ else if( *nptr == '+' )
+ nptr++;
+
+ // look for base-identifying sequences e.g. 0x for hex, 0 for octal
+ if( nptr[0] == '0' )
+ {
+ nptr++;
+
+ // 0 is always a valid digit
+ if( endptr )
+ *endptr = nptr;
+
+ if( *nptr == 'x' || *nptr == 'X' )
+ {
+ if( base != 0 && base != 16 )
+ {
+ // can't be hex, reject x (accept 0)
+ if( endptr )
+ *endptr = nptr;
+ return 0;
+ }
+
+ nptr++;
+ base = 16;
+ }
+ else if( base == 0 )
+ base = 8;
+ }
+ else if( base == 0 )
+ base = 10;
+
+ for( res = 0;; )
+ {
+ int val;
+
+ if( isdigit( *nptr ) )
+ val = *nptr - '0';
+ else if( islower( *nptr ) )
+ val = 10 + *nptr - 'a';
+ else if( isupper( *nptr ) )
+ val = 10 + *nptr - 'A';
+ else
+ break;
+
+ if( val >= base )
+ break;
+
+ // we go negative because LONG_MIN is further from 0 than
+ // LONG_MAX
+ if( res < ( LONG_MIN + val ) / base )
+ res = LONG_MIN; // overflow
+ else
+ res = res * base - val;
+
+ nptr++;
+
+ if( endptr )
+ *endptr = nptr;
+ }
+ if( pos )
+ {
+ // can't represent LONG_MIN positive
+ if( res == LONG_MIN )
+ res = LONG_MAX;
+ else
+ res = -res;
+ }
+ return res;
+}
int atoi( const char *string )
{
diff --git a/src/game/bg_lib.h b/src/game/bg_lib.h
index 69cf77ef..50c44558 100644
--- a/src/game/bg_lib.h
+++ b/src/game/bg_lib.h
@@ -104,8 +104,10 @@ int toupper( int c );
double atof( const char *string );
double _atof( const char **stringPtr );
+double strtod( const char *nptr, const char **endptr );
int atoi( const char *string );
int _atoi( const char **stringPtr );
+long strtol( const char *nptr, const char **endptr, int base );
int sscanf( const char *buffer, const char *fmt, ... );