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 #define 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
236 } else if ( c=='\"' || !c ) {
238 *data_p = ( char * ) data;
240 } else if( *data == '\n' ) {
243 if ( len < MAX_TOKEN_CHARS - 1 ) {
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' ) ) {
256 if (len < MAX_TOKEN_CHARS - 1) {
263 } while ( ( c >= '0' && c <= '9' ) || c == '.' );
265 // parse the exponent
266 if ( c == 'e' || c == 'E' ) {
267 if (len < MAX_TOKEN_CHARS - 1) {
274 if ( c == '-' || c == '+' ) {
275 if (len < MAX_TOKEN_CHARS - 1) {
284 if (len < MAX_TOKEN_CHARS - 1) {
291 } while ( c >= '0' && c <= '9' );
294 if (len == MAX_TOKEN_CHARS) {
299 *data_p = ( char * ) data;
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 == '\\' ) {
308 if (len < MAX_TOKEN_CHARS - 1) {
315 } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_'
316 || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
318 if (len == MAX_TOKEN_CHARS) {
323 *data_p = ( char * ) data;
327 // check for multi-character punctuation token
328 for ( punc = punctuation ; *punc ; punc++ ) {
333 for ( j = 0 ; j < l ; j++ ) {
334 if ( data[j] != (*punc)[j] ) {
339 // a valid multi-character punctuation
340 memcpy( pi->token, *punc, l );
343 *data_p = (char *)data;
348 // single character punctuation
349 pi->token[0] = *data;
352 *data_p = (char *)data;
362 const char *Com_Parse( const char *(*data_p) ) {
363 if ( pi->ungetToken ) {
364 pi->ungetToken = qfalse;
367 return Com_ParseExt( data_p, qtrue );
375 const char *Com_ParseOnLine( const char *(*data_p) ) {
376 if ( pi->ungetToken ) {
377 pi->ungetToken = qfalse;
380 return Com_ParseExt( data_p, qfalse );
390 void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {
393 token = Com_Parse( buf_p );
394 if ( strcmp( token, match ) ) {
396 Com_ScriptWarning( "MatchToken: %s != %s", token, match );
398 Com_ScriptError( "MatchToken: %s != %s", token, match );
406 Com_SkipBracedSection
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.
413 void Com_SkipBracedSection( const char *(*program) ) {
419 token = Com_Parse( program );
420 if( token[1] == 0 ) {
421 if( token[0] == '{' ) {
424 else if( token[0] == '}' ) {
428 } while( depth && *program );
436 void Com_SkipRestOfLine ( const char *(*data) ) {
441 while ( (c = *p++) != 0 ) {
456 const char *Com_ParseRestOfLine( const char *(*data_p) ) {
457 static char line[MAX_TOKEN_CHARS];
462 token = Com_ParseOnLine( data_p );
467 Q_strcat( line, sizeof(line), " " );
469 Q_strcat( line, sizeof(line), token );
476 float Com_ParseFloat( const char *(*buf_p) ) {
479 token = Com_Parse( buf_p );
483 return atof( token );
486 int Com_ParseInt( const char *(*buf_p) ) {
489 token = Com_Parse( buf_p );
493 return (int)atof( token );
498 void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {
502 Com_MatchToken( buf_p, "(" );
504 for (i = 0 ; i < x ; i++) {
505 token = Com_Parse(buf_p);
509 Com_MatchToken( buf_p, ")" );
512 void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {
515 Com_MatchToken( buf_p, "(" );
517 for (i = 0 ; i < y ; i++) {
518 Com_Parse1DMatrix (buf_p, x, m + i * x);
521 Com_MatchToken( buf_p, ")" );
524 void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {
527 Com_MatchToken( buf_p, "(" );
529 for (i = 0 ; i < z ; i++) {
530 Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);
533 Com_MatchToken( buf_p, ")" );