2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
\r
5 This file is part of GtkRadiant.
\r
7 GtkRadiant is free software; you can redistribute it and/or modify
\r
8 it under the terms of the GNU General Public License as published by
\r
9 the Free Software Foundation; either version 2 of the License, or
\r
10 (at your option) any later version.
\r
12 GtkRadiant is distributed in the hope that it will be useful,
\r
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
15 GNU General Public License for more details.
\r
17 You should have received a copy of the GNU General Public License
\r
18 along with GtkRadiant; if not, write to the Free Software
\r
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
\r
22 // q_parse.c -- support for parsing text files
\r
24 #include "q_shared.h"
\r
27 ============================================================================
\r
31 ============================================================================
\r
34 // multiple character punctuation tokens
\r
35 static const char *punctuation[] = {
\r
36 "+=", "-=", "*=", "/=", "&=", "|=", "++", "--",
\r
37 "&&", "||", "<=", ">=", "==", "!=",
\r
42 char token[MAX_TOKEN_CHARS];
\r
44 qboolean ungetToken;
\r
45 char parseFile[MAX_QPATH];
\r
48 #define MAX_PARSE_INFO 16
\r
49 static parseInfo_t parseInfo[MAX_PARSE_INFO];
\r
50 static int parseInfoNum;
\r
51 static parseInfo_t *pi = &parseInfo[0];
\r
55 Com_BeginParseSession
\r
58 void Com_BeginParseSession( const char *filename ) {
\r
59 if ( parseInfoNum == MAX_PARSE_INFO - 1 ) {
\r
60 Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" );
\r
63 pi = &parseInfo[parseInfoNum];
\r
66 Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );
\r
74 void Com_EndParseSession( void ) {
\r
75 if ( parseInfoNum == 0 ) {
\r
76 Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );
\r
79 pi = &parseInfo[parseInfoNum];
\r
84 Com_GetCurrentParseLine
\r
87 int Com_GetCurrentParseLine( void ) {
\r
95 Prints the script name and line number in the message
\r
98 void Com_ScriptError( const char *msg, ... ) {
\r
100 char string[32000];
\r
102 va_start( argptr, msg );
\r
103 vsprintf( string, msg,argptr );
\r
106 Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );
\r
109 void Com_ScriptWarning( const char *msg, ... ) {
\r
111 char string[32000];
\r
113 va_start( argptr, msg );
\r
114 vsprintf( string, msg,argptr );
\r
117 Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );
\r
122 ===================
\r
125 Calling this will make the next Com_Parse return
\r
126 the current token instead of advancing the pointer
\r
127 ===================
\r
129 void Com_UngetToken( void ) {
\r
130 if ( pi->ungetToken ) {
\r
131 Com_ScriptError( "UngetToken called twice" );
\r
133 pi->ungetToken = qtrue;
\r
137 static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {
\r
140 while( (c = *data) <= ' ') {
\r
146 *hasNewLines = qtrue;
\r
158 Parse a token out of a string
\r
159 Will never return NULL, just empty strings.
\r
160 An empty string will only be returned at end of file.
\r
162 If "allowLineBreaks" is qtrue then an empty
\r
163 string will be returned if the next token is
\r
167 static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) {
\r
169 qboolean hasNewLines = qfalse;
\r
174 Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );
\r
181 // make sure incoming data is valid
\r
187 // skip any leading whitespace
\r
190 data = SkipWhitespace( data, &hasNewLines );
\r
195 if ( hasNewLines && !allowLineBreaks ) {
\r
202 // skip double slash comments
\r
203 if ( c == '/' && data[1] == '/' ) {
\r
204 while (*data && *data != '\n') {
\r
210 // skip /* */ comments
\r
211 if ( c=='/' && data[1] == '*' ) {
\r
212 while ( *data && ( *data != '*' || data[1] != '/' ) ) {
\r
213 if( *data == '\n' ) {
\r
224 // a real token to parse
\r
228 // handle quoted strings
\r
233 if ( ( c=='\\' ) && ( *data == '\"' ) ) {
\r
234 // allow quoted strings to use \" to indicate the " character
\r
236 } else if ( c=='\"' || !c ) {
\r
237 pi->token[len] = 0;
\r
238 *data_p = ( char * ) data;
\r
240 } else if( *data == '\n' ) {
\r
243 if ( len < MAX_TOKEN_CHARS - 1 ) {
\r
244 pi->token[len] = c;
\r
250 // check for a number
\r
251 // is this parsing of negative numbers going to cause expression problems
\r
252 if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ||
\r
253 ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) {
\r
256 if (len < MAX_TOKEN_CHARS - 1) {
\r
257 pi->token[len] = c;
\r
263 } while ( ( c >= '0' && c <= '9' ) || c == '.' );
\r
265 // parse the exponent
\r
266 if ( c == 'e' || c == 'E' ) {
\r
267 if (len < MAX_TOKEN_CHARS - 1) {
\r
268 pi->token[len] = c;
\r
274 if ( c == '-' || c == '+' ) {
\r
275 if (len < MAX_TOKEN_CHARS - 1) {
\r
276 pi->token[len] = c;
\r
284 if (len < MAX_TOKEN_CHARS - 1) {
\r
285 pi->token[len] = c;
\r
291 } while ( c >= '0' && c <= '9' );
\r
294 if (len == MAX_TOKEN_CHARS) {
\r
297 pi->token[len] = 0;
\r
299 *data_p = ( char * ) data;
\r
303 // check for a regular word
\r
304 // we still allow forward and back slashes in name tokens for pathnames
\r
305 // and also colons for drive letters
\r
306 if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) {
\r
308 if (len < MAX_TOKEN_CHARS - 1) {
\r
309 pi->token[len] = c;
\r
315 } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'
\r
316 || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
\r
318 if (len == MAX_TOKEN_CHARS) {
\r
321 pi->token[len] = 0;
\r
323 *data_p = ( char * ) data;
\r
327 // check for multi-character punctuation token
\r
328 for ( punc = punctuation ; *punc ; punc++ ) {
\r
332 l = strlen( *punc );
\r
333 for ( j = 0 ; j < l ; j++ ) {
\r
334 if ( data[j] != (*punc)[j] ) {
\r
339 // a valid multi-character punctuation
\r
340 memcpy( pi->token, *punc, l );
\r
343 *data_p = (char *)data;
\r
348 // single character punctuation
\r
349 pi->token[0] = *data;
\r
352 *data_p = (char *)data;
\r
358 ===================
\r
360 ===================
\r
362 const char *Com_Parse( const char *(*data_p) ) {
\r
363 if ( pi->ungetToken ) {
\r
364 pi->ungetToken = qfalse;
\r
367 return Com_ParseExt( data_p, qtrue );
\r
371 ===================
\r
373 ===================
\r
375 const char *Com_ParseOnLine( const char *(*data_p) ) {
\r
376 if ( pi->ungetToken ) {
\r
377 pi->ungetToken = qfalse;
\r
380 return Com_ParseExt( data_p, qfalse );
\r
390 void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {
\r
393 token = Com_Parse( buf_p );
\r
394 if ( strcmp( token, match ) ) {
\r
396 Com_ScriptWarning( "MatchToken: %s != %s", token, match );
\r
398 Com_ScriptError( "MatchToken: %s != %s", token, match );
\r
406 Com_SkipBracedSection
\r
408 The next token should be an open brace.
\r
409 Skips until a matching close brace is found.
\r
410 Internal brace depths are properly skipped.
\r
413 void Com_SkipBracedSection( const char *(*program) ) {
\r
419 token = Com_Parse( program );
\r
420 if( token[1] == 0 ) {
\r
421 if( token[0] == '{' ) {
\r
424 else if( token[0] == '}' ) {
\r
428 } while( depth && *program );
\r
436 void Com_SkipRestOfLine ( const char *(*data) ) {
\r
441 while ( (c = *p++) != 0 ) {
\r
452 ====================
\r
453 Com_ParseRestOfLine
\r
454 ====================
\r
456 const char *Com_ParseRestOfLine( const char *(*data_p) ) {
\r
457 static char line[MAX_TOKEN_CHARS];
\r
462 token = Com_ParseOnLine( data_p );
\r
467 Q_strcat( line, sizeof(line), " " );
\r
469 Q_strcat( line, sizeof(line), token );
\r
476 float Com_ParseFloat( const char *(*buf_p) ) {
\r
479 token = Com_Parse( buf_p );
\r
483 return atof( token );
\r
486 int Com_ParseInt( const char *(*buf_p) ) {
\r
489 token = Com_Parse( buf_p );
\r
493 return (int)atof( token );
\r
498 void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {
\r
502 Com_MatchToken( buf_p, "(" );
\r
504 for (i = 0 ; i < x ; i++) {
\r
505 token = Com_Parse(buf_p);
\r
506 m[i] = atof(token);
\r
509 Com_MatchToken( buf_p, ")" );
\r
512 void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {
\r
515 Com_MatchToken( buf_p, "(" );
\r
517 for (i = 0 ; i < y ; i++) {
\r
518 Com_Parse1DMatrix (buf_p, x, m + i * x);
\r
521 Com_MatchToken( buf_p, ")" );
\r
524 void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {
\r
527 Com_MatchToken( buf_p, "(" );
\r
529 for (i = 0 ; i < z ; i++) {
\r
530 Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);
\r
533 Com_MatchToken( buf_p, ")" );
\r