Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

q_parse.cpp (10583B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // q_parse.c -- support for parsing text files
     23 
     24 #include "q_shared.h"
     25 
     26 /*
     27 ============================================================================
     28 
     29 PARSING
     30 
     31 ============================================================================
     32 */
     33 
     34 // multiple character punctuation tokens
     35 static const char *punctuation[] = {
     36 	"+=", "-=",  "*=",  "/=", "&=", "|=", "++", "--",
     37 		"&&", "||",  "<=",  ">=", "==", "!=",
     38 	NULL
     39 };
     40 
     41 typedef struct {
     42 	char	token[MAX_TOKEN_CHARS];
     43 	int		lines;
     44 	qboolean	ungetToken;
     45 	char	parseFile[MAX_QPATH];
     46 } parseInfo_t;
     47 
     48 #define	MAX_PARSE_INFO	16
     49 static parseInfo_t	parseInfo[MAX_PARSE_INFO];
     50 static int			parseInfoNum;
     51 static parseInfo_t	*pi = &parseInfo[0];
     52 
     53 /*
     54 ===================
     55 Com_BeginParseSession
     56 ===================
     57 */
     58 void Com_BeginParseSession( const char *filename ) {
     59 	if ( parseInfoNum == MAX_PARSE_INFO - 1 ) {
     60 		Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" );
     61 	}
     62 	parseInfoNum++;
     63 	pi = &parseInfo[parseInfoNum];
     64 
     65 	pi->lines = 1;
     66 	Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );
     67 }
     68 
     69 /*
     70 ===================
     71 Com_EndParseSession
     72 ===================
     73 */
     74 void Com_EndParseSession( void ) {
     75 	if ( parseInfoNum == 0 ) {
     76 		Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );
     77 	}
     78 	parseInfoNum--;
     79 	pi = &parseInfo[parseInfoNum];
     80 }
     81 
     82 /*
     83 ===================
     84 Com_GetCurrentParseLine
     85 ===================
     86 */
     87 int Com_GetCurrentParseLine( void ) {
     88 	return pi->lines;
     89 }
     90 
     91 /*
     92 ===================
     93 Com_ScriptError
     94 
     95 Prints the script name and line number in the message
     96 ===================
     97 */
     98 void Com_ScriptError( const char *msg, ... ) {
     99 	va_list		argptr;
    100 	char		string[32000];
    101 
    102 	va_start( argptr, msg );
    103 	vsprintf( string, msg,argptr );
    104 	va_end( argptr );
    105 
    106 	Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );
    107 }
    108 
    109 void Com_ScriptWarning( const char *msg, ... ) {
    110 	va_list		argptr;
    111 	char		string[32000];
    112 
    113 	va_start( argptr, msg );
    114 	vsprintf( string, msg,argptr );
    115 	va_end( argptr );
    116 
    117 	Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );
    118 }
    119 
    120 
    121 /*
    122 ===================
    123 Com_UngetToken
    124 
    125 Calling this will make the next Com_Parse return
    126 the current token instead of advancing the pointer
    127 ===================
    128 */
    129 void Com_UngetToken( void ) {
    130 	if ( pi->ungetToken ) {
    131 		Com_ScriptError( "UngetToken called twice" );
    132 	}
    133 	pi->ungetToken = qtrue;
    134 }
    135 
    136 
    137 static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {
    138 	int c;
    139 
    140 	while( (c = *data) <= ' ') {
    141 		if( !c ) {
    142 			return NULL;
    143 		}
    144 		if( c == '\n' ) {
    145 			pi->lines++;
    146 			*hasNewLines = qtrue;
    147 		}
    148 		data++;
    149 	}
    150 
    151 	return data;
    152 }
    153 
    154 /*
    155 ==============
    156 Com_ParseExt
    157 
    158 Parse a token out of a string
    159 Will never return NULL, just empty strings.
    160 An empty string will only be returned at end of file.
    161 
    162 If "allowLineBreaks" is qtrue then an empty
    163 string will be returned if the next token is
    164 a newline.
    165 ==============
    166 */
    167 static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) {
    168 	int c = 0, len;
    169 	qboolean hasNewLines = qfalse;
    170 	const char *data;
    171 	const char **punc;
    172 
    173 	if ( !data_p ) {
    174 		Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );
    175 	}
    176 
    177 	data = *data_p;
    178 	len = 0;
    179 	pi->token[0] = 0;
    180 
    181 	// make sure incoming data is valid
    182 	if ( !data ) {
    183 		*data_p = NULL;
    184 		return pi->token;
    185 	}
    186 
    187 	// skip any leading whitespace
    188 	while ( 1 ) {
    189 		// skip whitespace
    190 		data = SkipWhitespace( data, &hasNewLines );
    191 		if ( !data ) {
    192 			*data_p = NULL;
    193 			return pi->token;
    194 		}
    195 		if ( hasNewLines && !allowLineBreaks ) {
    196 			*data_p = data;
    197 			return pi->token;
    198 		}
    199 
    200 		c = *data;
    201 
    202 		// skip double slash comments
    203 		if ( c == '/' && data[1] == '/' ) {
    204 			while (*data && *data != '\n') {
    205 				data++;
    206 			}
    207 			continue;
    208 		}
    209 
    210 		// skip /* */ comments
    211 		if ( c=='/' && data[1] == '*' ) {
    212 			while ( *data && ( *data != '*' || data[1] != '/' ) ) {
    213 				if( *data == '\n' ) {
    214 					pi->lines++;
    215 				}
    216 				data++;
    217 			}
    218 			if ( *data ) {
    219 				data += 2;
    220 			}
    221 			continue;
    222 		}
    223 
    224 		// a real token to parse
    225 		break;
    226 	}
    227 
    228 	// handle quoted strings
    229 	if ( c == '\"' ) {
    230 		data++;
    231 		while( 1 ) {
    232 			c = *data++;
    233 			if ( ( c=='\\' ) && ( *data == '\"' ) ) {
    234 				// allow quoted strings to use \" to indicate the " character
    235 				data++;
    236 			} else if ( c=='\"' || !c ) {
    237 				pi->token[len] = 0;
    238 				*data_p = ( char * ) data;
    239 				return pi->token;
    240 			} else if( *data == '\n' ) {
    241 				pi->lines++;
    242 			}
    243 			if ( len < MAX_TOKEN_CHARS - 1 ) {
    244 				pi->token[len] = c;
    245 				len++;
    246 			}
    247 		}
    248 	}
    249 
    250 	// check for a number
    251 	// is this parsing of negative numbers going to cause expression problems
    252 	if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) || 
    253 		( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) {
    254 		do  {
    255 
    256 			if (len < MAX_TOKEN_CHARS - 1) {
    257 				pi->token[len] = c;
    258 				len++;
    259 			}
    260 			data++;
    261 
    262 			c = *data;
    263 		} while ( ( c >= '0' && c <= '9' ) || c == '.' );
    264 
    265 		// parse the exponent
    266 		if ( c == 'e' || c == 'E' ) {
    267 			if (len < MAX_TOKEN_CHARS - 1) {
    268 				pi->token[len] = c;
    269 				len++;
    270 			}
    271 			data++;
    272 			c = *data;
    273 
    274 			if ( c == '-' || c == '+' ) {
    275 				if (len < MAX_TOKEN_CHARS - 1) {
    276 					pi->token[len] = c;
    277 					len++;
    278 				}
    279 				data++;
    280 				c = *data;
    281 			}
    282 
    283 			do  {
    284 				if (len < MAX_TOKEN_CHARS - 1) {
    285 					pi->token[len] = c;
    286 					len++;
    287 				}
    288 				data++;
    289 
    290 				c = *data;
    291 			} while ( c >= '0' && c <= '9' );
    292 		}
    293 
    294 		if (len == MAX_TOKEN_CHARS) {
    295 			len = 0;
    296 		}
    297 		pi->token[len] = 0;
    298 
    299 		*data_p = ( char * ) data;
    300 		return pi->token;
    301    	}
    302 
    303 	// check for a regular word
    304 	// we still allow forward and back slashes in name tokens for pathnames
    305 	// and also colons for drive letters
    306 	if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) {
    307 		do  {
    308 			if (len < MAX_TOKEN_CHARS - 1) {
    309 				pi->token[len] = c;
    310 				len++;
    311 			}
    312 			data++;
    313 
    314 			c = *data;
    315 		} while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' 
    316 			|| ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
    317 
    318 		if (len == MAX_TOKEN_CHARS) {
    319 			len = 0;
    320 		}
    321 		pi->token[len] = 0;
    322 
    323 		*data_p = ( char * ) data;
    324 		return pi->token;
    325 	}
    326 
    327 	// check for multi-character punctuation token
    328 	for ( punc = punctuation ; *punc ; punc++ ) {
    329 		int		l;
    330 		int		j;
    331 
    332 		l = strlen( *punc );
    333 		for ( j = 0 ; j < l ; j++ ) {
    334 			if ( data[j] != (*punc)[j] ) {
    335 				break;
    336 			}
    337 		}
    338 		if ( j == l ) {
    339 			// a valid multi-character punctuation
    340 			memcpy( pi->token, *punc, l );
    341 			pi->token[l] = 0;
    342 			data += l;
    343 			*data_p = (char *)data;
    344 			return pi->token;
    345 		}
    346 	}
    347 
    348 	// single character punctuation
    349 	pi->token[0] = *data;
    350 	pi->token[1] = 0;
    351 	data++;
    352 	*data_p = (char *)data;
    353 
    354 	return pi->token;
    355 }
    356 
    357 /*
    358 ===================
    359 Com_Parse
    360 ===================
    361 */
    362 const char *Com_Parse( const char *(*data_p) ) {
    363 	if ( pi->ungetToken ) {
    364 		pi->ungetToken = qfalse;
    365 		return pi->token;
    366 	}
    367 	return Com_ParseExt( data_p, qtrue );
    368 }
    369 
    370 /*
    371 ===================
    372 Com_ParseOnLine
    373 ===================
    374 */
    375 const char *Com_ParseOnLine( const char *(*data_p) ) {
    376 	if ( pi->ungetToken ) {
    377 		pi->ungetToken = qfalse;
    378 		return pi->token;
    379 	}
    380 	return Com_ParseExt( data_p, qfalse );
    381 }
    382 
    383 
    384 
    385 /*
    386 ==================
    387 Com_MatchToken
    388 ==================
    389 */
    390 void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {
    391 	const char	*token;
    392 
    393 	token = Com_Parse( buf_p );
    394 	if ( strcmp( token, match ) ) {
    395 		if (warning) {
    396 			Com_ScriptWarning( "MatchToken: %s != %s", token, match );
    397 		} else {
    398 			Com_ScriptError( "MatchToken: %s != %s", token, match );
    399 		}
    400 	}
    401 }
    402 
    403 
    404 /*
    405 =================
    406 Com_SkipBracedSection
    407 
    408 The next token should be an open brace.
    409 Skips until a matching close brace is found.
    410 Internal brace depths are properly skipped.
    411 =================
    412 */
    413 void Com_SkipBracedSection( const char *(*program) ) {
    414 	const char			*token;
    415 	int				depth;
    416 
    417 	depth = 0;
    418 	do {
    419 		token = Com_Parse( program );
    420 		if( token[1] == 0 ) {
    421 			if( token[0] == '{' ) {
    422 				depth++;
    423 			}
    424 			else if( token[0] == '}' ) {
    425 				depth--;
    426 			}
    427 		}
    428 	} while( depth && *program );
    429 }
    430 
    431 /*
    432 =================
    433 Com_SkipRestOfLine
    434 =================
    435 */
    436 void Com_SkipRestOfLine ( const char *(*data) ) {
    437 	const char	*p;
    438 	int		c;
    439 
    440 	p = *data;
    441 	while ( (c = *p++) != 0 ) {
    442 		if ( c == '\n' ) {
    443 			pi->lines++;
    444 			break;
    445 		}
    446 	}
    447 
    448 	*data = p;
    449 }
    450 
    451 /*
    452 ====================
    453 Com_ParseRestOfLine
    454 ====================
    455 */
    456 const char *Com_ParseRestOfLine( const char *(*data_p) ) {
    457 	static char	line[MAX_TOKEN_CHARS];
    458 	const char *token;
    459 
    460 	line[0] = 0;
    461 	while( 1 ) {
    462 		token = Com_ParseOnLine( data_p );
    463 		if ( !token[0] ) {
    464 			break;
    465 		}
    466 		if ( line[0] ) {
    467 			Q_strcat( line, sizeof(line), " " );
    468 		}
    469 		Q_strcat( line, sizeof(line), token );
    470 	}
    471 
    472 	return line;
    473 }
    474 
    475 
    476 float Com_ParseFloat( const char *(*buf_p) ) {
    477 	const char		*token;
    478 
    479 	token = Com_Parse( buf_p );
    480 	if ( !token[0] ) {
    481 		return 0;
    482 	}
    483 	return atof( token );
    484 }
    485 
    486 int Com_ParseInt( const char *(*buf_p) ) {
    487 	const char		*token;
    488 
    489 	token = Com_Parse( buf_p );
    490 	if ( !token[0] ) {
    491 		return 0;
    492 	}
    493 	return atof( token );
    494 }
    495 
    496 
    497 
    498 void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {
    499 	const char	*token;
    500 	int		i;
    501 
    502 	Com_MatchToken( buf_p, "(" );
    503 
    504 	for (i = 0 ; i < x ; i++) {
    505 		token = Com_Parse(buf_p);
    506 		m[i] = atof(token);
    507 	}
    508 
    509 	Com_MatchToken( buf_p, ")" );
    510 }
    511 
    512 void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {
    513 	int		i;
    514 
    515 	Com_MatchToken( buf_p, "(" );
    516 
    517 	for (i = 0 ; i < y ; i++) {
    518 		Com_Parse1DMatrix (buf_p, x, m + i * x);
    519 	}
    520 
    521 	Com_MatchToken( buf_p, ")" );
    522 }
    523 
    524 void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {
    525 	int		i;
    526 
    527 	Com_MatchToken( buf_p, "(" );
    528 
    529 	for (i = 0 ; i < z ; i++) {
    530 		Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);
    531 	}
    532 
    533 	Com_MatchToken( buf_p, ")" );
    534 }
    535