[quake3-commits] r1692 - trunk/code/game

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Wed Oct 21 06:18:46 EDT 2009


Author: thilo
Date: 2009-10-21 06:18:46 -0400 (Wed, 21 Oct 2009)
New Revision: 1692

Modified:
   trunk/code/game/bg_lib.c
   trunk/code/game/bg_lib.h
Log:
- 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


Modified: trunk/code/game/bg_lib.c
===================================================================
--- trunk/code/game/bg_lib.c	2009-10-20 11:47:34 UTC (rev 1691)
+++ trunk/code/game/bg_lib.c	2009-10-21 10:18:46 UTC (rev 1692)
@@ -49,7 +49,7 @@
 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,7 +890,237 @@
 	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;
 	int		value;
@@ -986,6 +1216,97 @@
 	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 @@
 /* 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 @@
 			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;
 }
 

Modified: trunk/code/game/bg_lib.h
===================================================================
--- trunk/code/game/bg_lib.h	2009-10-20 11:47:34 UTC (rev 1691)
+++ trunk/code/game/bg_lib.h	2009-10-21 10:18:46 UTC (rev 1692)
@@ -45,19 +45,19 @@
 #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 @@
 
 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)));



More information about the quake3-commits mailing list