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