- Add Ben Millwood's implementation of strtod/strtol to bg_lib.c

- Add %s scanf patch from M. Kristall to scanf in bg_lib.c
This commit is contained in:
Thilo Schulz 2009-10-21 10:18:46 +00:00
parent c8583df572
commit 6fb304619b
2 changed files with 360 additions and 18 deletions

View file

@ -49,7 +49,7 @@ static char* med3(char *, char *, char *, cmp_t *);
static void swapfunc(char *, char *, int, int);
#ifndef min
#define min(a, b) (a) < (b) ? a : b
#define min(a, b) ((a) < (b) ? (a) : (b))
#endif
/*
@ -890,6 +890,236 @@ double _atof( const char **stringPtr ) {
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 == NULL )
{
nan.ui = 0x7fffffff;
return nan.f;
}
*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;
res = 0;
while( qtrue )
{
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;
}
}
int atoi( const char *string ) {
int sign;
@ -986,6 +1216,97 @@ int _atoi( 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;
res = 0;
while( qtrue )
{
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 abs( int n ) {
return n < 0 ? -n : n;
}
@ -1759,10 +2080,11 @@ int Q_snprintf(char *str, size_t length, const char *fmt, ...)
/* this is really crappy */
int sscanf( const char *buffer, const char *fmt, ... ) {
int cmd;
int **arg;
va_list ap;
int count;
size_t len;
arg = (int **)&fmt + 1;
va_start (ap, fmt);
count = 0;
while ( *fmt ) {
@ -1771,22 +2093,40 @@ int sscanf( const char *buffer, const char *fmt, ... ) {
continue;
}
cmd = fmt[1];
fmt += 2;
fmt++;
cmd = *fmt;
if (isdigit (cmd)) {
len = (size_t)_atoi (&fmt);
cmd = *(fmt - 1);
} else {
len = MAX_STRING_CHARS - 1;
fmt++;
}
switch ( cmd ) {
case 'i':
case 'd':
case 'u':
**arg = _atoi( &buffer );
*(va_arg (ap, int *)) = _atoi( &buffer );
break;
case 'f':
*(float *)*arg = _atof( &buffer );
*(va_arg (ap, float *)) = _atof( &buffer );
break;
case 's':
{
char *s = va_arg (ap, char *);
while (isspace (*buffer))
buffer++;
while (*buffer && !isspace (*buffer) && len-- > 0 )
*s++ = *buffer++;
*s++ = '\0';
break;
}
}
arg++;
}
va_end (ap);
return count;
}

View file

@ -45,19 +45,19 @@ typedef char * va_list;
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )
#define CHAR_BIT 8 /* number of bits in a char */
#define SCHAR_MIN (-128) /* minimum signed char value */
#define SCHAR_MAX 127 /* maximum signed char value */
#define UCHAR_MAX 0xff /* maximum unsigned char value */
#define CHAR_BIT 8 /* number of bits in a char */
#define SCHAR_MAX 0x7f /* maximum signed char value */
#define SCHAR_MIN (-SCHAR_MAX - 1) /* minimum signed char value */
#define UCHAR_MAX 0xff /* maximum unsigned char value */
#define SHRT_MIN (-32768) /* minimum (signed) short value */
#define SHRT_MAX 32767 /* maximum (signed) short value */
#define SHRT_MAX 0x7fff /* maximum (signed) short value */
#define SHRT_MIN (-SHRT_MAX - 1) /* minimum (signed) short value */
#define USHRT_MAX 0xffff /* maximum unsigned short value */
#define INT_MIN (-2147483647 - 1) /* minimum (signed) int value */
#define INT_MAX 2147483647 /* maximum (signed) int value */
#define INT_MAX 0x7fffffff /* maximum (signed) int value */
#define INT_MIN (-INT_MAX - 1) /* minimum (signed) int value */
#define UINT_MAX 0xffffffff /* maximum unsigned int value */
#define LONG_MIN (-2147483647L - 1) /* minimum (signed) long value */
#define LONG_MAX 2147483647L /* maximum (signed) long value */
#define LONG_MAX 0x7fffffffL /* maximum (signed) long value */
#define LONG_MIN (-LONG_MAX - 1) /* minimum (signed) long value */
#define ULONG_MAX 0xffffffffUL /* maximum unsigned long value */
#define isalnum(c) (isalpha(c) || isdigit(c))
@ -95,8 +95,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 Q_vsnprintf( char *buffer, size_t length, const char *fmt, va_list argptr );
int Q_snprintf( char *buffer, size_t length, const char *fmt, ... ) __attribute__ ((format (printf, 3, 4)));