2 Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 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.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 // q_parse.c -- support for parsing text files
27 ============================================================================
31 ============================================================================
34 // multiple character punctuation tokens
35 static const char *punctuation[] = {
36 "+=", "-=", "*=", "/=", "&=", "|=", "++", "--",
37 "&&", "||", "<=", ">=", "==", "!=",
42 char token[MAX_TOKEN_CHARS];
45 char parseFile[MAX_QPATH];
48 const int MAX_PARSE_INFO = 16;
49 static parseInfo_t parseInfo[MAX_PARSE_INFO];
50 static int parseInfoNum;
51 static parseInfo_t *pi = &parseInfo[0];
58 void Com_BeginParseSession( const char *filename ) {
59 if ( parseInfoNum == MAX_PARSE_INFO - 1 ) {
60 Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" );
63 pi = &parseInfo[parseInfoNum];
66 Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );
74 void Com_EndParseSession( void ) {
75 if ( parseInfoNum == 0 ) {
76 Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );
79 pi = &parseInfo[parseInfoNum];
84 Com_GetCurrentParseLine
87 int Com_GetCurrentParseLine( void ) {
95 Prints the script name and line number in the message
98 void Com_ScriptError( const char *msg, ... ) {
102 va_start( argptr, msg );
103 vsprintf( string, msg,argptr );
106 Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );
109 void Com_ScriptWarning( const char *msg, ... ) {
113 va_start( argptr, msg );
114 vsprintf( string, msg,argptr );
117 Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );
125 Calling this will make the next Com_Parse return
126 the current token instead of advancing the pointer
129 void Com_UngetToken( void ) {
130 if ( pi->ungetToken ) {
131 Com_ScriptError( "UngetToken called twice" );
133 pi->ungetToken = qtrue;
137 static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {
140 while ( ( c = *data ) <= ' ' ) {
146 *hasNewLines = qtrue;
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.
162 If "allowLineBreaks" is qtrue then an empty
163 string will be returned if the next token is
167 static char *Com_ParseExt( const char *( *data_p ), qboolean allowLineBreaks ) {
169 qboolean hasNewLines = qfalse;
174 Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );
181 // make sure incoming data is valid
187 // skip any leading whitespace
190 data = SkipWhitespace( data, &hasNewLines );
195 if ( hasNewLines && !allowLineBreaks ) {
202 // skip double slash comments
203 if ( c == '/' && data[1] == '/' ) {
204 while ( *data && *data != '\n' ) {
210 // skip /* */ comments
211 if ( c == '/' && data[1] == '*' ) {
212 while ( *data && ( *data != '*' || data[1] != '/' ) ) {
213 if ( *data == '\n' ) {
224 // a real token to parse
228 // handle quoted strings
233 if ( ( c == '\\' ) && ( *data == '\"' ) ) {
234 // allow quoted strings to use \" to indicate the " character
237 else if ( c == '\"' || !c ) {
239 *data_p = ( char * ) data;
242 else if ( *data == '\n' ) {
245 if ( len < MAX_TOKEN_CHARS - 1 ) {
252 // check for a number
253 // is this parsing of negative numbers going to cause expression problems
254 if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ||
255 ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) {
258 if ( len < MAX_TOKEN_CHARS - 1 ) {
265 } while ( ( c >= '0' && c <= '9' ) || c == '.' );
267 // parse the exponent
268 if ( c == 'e' || c == 'E' ) {
269 if ( len < MAX_TOKEN_CHARS - 1 ) {
276 if ( c == '-' || c == '+' ) {
277 if ( len < MAX_TOKEN_CHARS - 1 ) {
286 if ( len < MAX_TOKEN_CHARS - 1 ) {
293 } while ( c >= '0' && c <= '9' );
296 if ( len == MAX_TOKEN_CHARS ) {
301 *data_p = ( char * ) data;
305 // check for a regular word
306 // we still allow forward and back slashes in name tokens for pathnames
307 // and also colons for drive letters
308 if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) {
310 if ( len < MAX_TOKEN_CHARS - 1 ) {
317 } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'
318 || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
320 if ( len == MAX_TOKEN_CHARS ) {
325 *data_p = ( char * ) data;
329 // check for multi-character punctuation token
330 for ( punc = punctuation ; *punc ; punc++ ) {
335 for ( j = 0 ; j < l ; j++ ) {
336 if ( data[j] != ( *punc )[j] ) {
341 // a valid multi-character punctuation
342 memcpy( pi->token, *punc, l );
345 *data_p = (char *)data;
350 // single character punctuation
351 pi->token[0] = *data;
354 *data_p = (char *)data;
364 const char *Com_Parse( const char *( *data_p ) ) {
365 if ( pi->ungetToken ) {
366 pi->ungetToken = qfalse;
369 return Com_ParseExt( data_p, qtrue );
377 const char *Com_ParseOnLine( const char *( *data_p ) ) {
378 if ( pi->ungetToken ) {
379 pi->ungetToken = qfalse;
382 return Com_ParseExt( data_p, qfalse );
392 void Com_MatchToken( const char *( *buf_p ), const char *match, qboolean warning ) {
395 token = Com_Parse( buf_p );
396 if ( strcmp( token, match ) ) {
398 Com_ScriptWarning( "MatchToken: %s != %s", token, match );
401 Com_ScriptError( "MatchToken: %s != %s", token, match );
409 Com_SkipBracedSection
411 The next token should be an open brace.
412 Skips until a matching close brace is found.
413 Internal brace depths are properly skipped.
416 void Com_SkipBracedSection( const char *( *program ) ) {
422 token = Com_Parse( program );
423 if ( token[1] == 0 ) {
424 if ( token[0] == '{' ) {
427 else if ( token[0] == '}' ) {
431 } while ( depth && *program );
439 void Com_SkipRestOfLine( const char *( *data ) ) {
444 while ( ( c = *p++ ) != 0 ) {
459 const char *Com_ParseRestOfLine( const char *( *data_p ) ) {
460 static char line[MAX_TOKEN_CHARS];
465 token = Com_ParseOnLine( data_p );
470 Q_strcat( line, sizeof( line ), " " );
472 Q_strcat( line, sizeof( line ), token );
479 float Com_ParseFloat( const char *( *buf_p ) ) {
482 token = Com_Parse( buf_p );
486 return atof( token );
489 int Com_ParseInt( const char *( *buf_p ) ) {
492 token = Com_Parse( buf_p );
496 return (int)atof( token );
501 void Com_Parse1DMatrix( const char *( *buf_p ), int x, float *m ) {
505 Com_MatchToken( buf_p, "(" );
507 for ( i = 0 ; i < x ; i++ ) {
508 token = Com_Parse( buf_p );
509 m[i] = atof( token );
512 Com_MatchToken( buf_p, ")" );
515 void Com_Parse2DMatrix( const char *( *buf_p ), int y, int x, float *m ) {
518 Com_MatchToken( buf_p, "(" );
520 for ( i = 0 ; i < y ; i++ ) {
521 Com_Parse1DMatrix( buf_p, x, m + i * x );
524 Com_MatchToken( buf_p, ")" );
527 void Com_Parse3DMatrix( const char *( *buf_p ), int z, int y, int x, float *m ) {
530 Com_MatchToken( buf_p, "(" );
532 for ( i = 0 ; i < z ; i++ ) {
533 Com_Parse2DMatrix( buf_p, y, x, m + i * x * y );
536 Com_MatchToken( buf_p, ")" );