mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2025-01-18 23:21:39 +00:00
- 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:
parent
c8583df572
commit
6fb304619b
2 changed files with 360 additions and 18 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
|
Loading…
Reference in a new issue