--- /dev/null
+/*\r
+Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
+For a list of contributors, see the accompanying CONTRIBUTORS file.\r
+\r
+This file is part of GtkRadiant.\r
+\r
+GtkRadiant is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License as published by\r
+the Free Software Foundation; either version 2 of the License, or\r
+(at your option) any later version.\r
+\r
+GtkRadiant is distributed in the hope that it will be useful,\r
+but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+GNU General Public License for more details.\r
+\r
+You should have received a copy of the GNU General Public License\r
+along with GtkRadiant; if not, write to the Free Software\r
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\r
+*/\r
+\r
+// q_parse.c -- support for parsing text files\r
+\r
+#include "q_shared.h"\r
+\r
+/*\r
+============================================================================\r
+\r
+PARSING\r
+\r
+============================================================================\r
+*/\r
+\r
+// multiple character punctuation tokens\r
+static const char *punctuation[] = {\r
+ "+=", "-=", "*=", "/=", "&=", "|=", "++", "--",\r
+ "&&", "||", "<=", ">=", "==", "!=",\r
+ NULL\r
+};\r
+\r
+typedef struct {\r
+ char token[MAX_TOKEN_CHARS];\r
+ int lines;\r
+ qboolean ungetToken;\r
+ char parseFile[MAX_QPATH];\r
+} parseInfo_t;\r
+\r
+#define MAX_PARSE_INFO 16\r
+static parseInfo_t parseInfo[MAX_PARSE_INFO];\r
+static int parseInfoNum;\r
+static parseInfo_t *pi = &parseInfo[0];\r
+\r
+/*\r
+===================\r
+Com_BeginParseSession\r
+===================\r
+*/\r
+void Com_BeginParseSession( const char *filename ) {\r
+ if ( parseInfoNum == MAX_PARSE_INFO - 1 ) {\r
+ Com_Error( ERR_FATAL, "Com_BeginParseSession: session overflow" );\r
+ }\r
+ parseInfoNum++;\r
+ pi = &parseInfo[parseInfoNum];\r
+\r
+ pi->lines = 1;\r
+ Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );\r
+}\r
+\r
+/*\r
+===================\r
+Com_EndParseSession\r
+===================\r
+*/\r
+void Com_EndParseSession( void ) {\r
+ if ( parseInfoNum == 0 ) {\r
+ Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );\r
+ }\r
+ parseInfoNum--;\r
+ pi = &parseInfo[parseInfoNum];\r
+}\r
+\r
+/*\r
+===================\r
+Com_GetCurrentParseLine\r
+===================\r
+*/\r
+int Com_GetCurrentParseLine( void ) {\r
+ return pi->lines;\r
+}\r
+\r
+/*\r
+===================\r
+Com_ScriptError\r
+\r
+Prints the script name and line number in the message\r
+===================\r
+*/\r
+void Com_ScriptError( const char *msg, ... ) {\r
+ va_list argptr;\r
+ char string[32000];\r
+\r
+ va_start( argptr, msg );\r
+ vsprintf( string, msg,argptr );\r
+ va_end( argptr );\r
+\r
+ Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );\r
+}\r
+\r
+void Com_ScriptWarning( const char *msg, ... ) {\r
+ va_list argptr;\r
+ char string[32000];\r
+\r
+ va_start( argptr, msg );\r
+ vsprintf( string, msg,argptr );\r
+ va_end( argptr );\r
+\r
+ Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );\r
+}\r
+\r
+\r
+/*\r
+===================\r
+Com_UngetToken\r
+\r
+Calling this will make the next Com_Parse return\r
+the current token instead of advancing the pointer\r
+===================\r
+*/\r
+void Com_UngetToken( void ) {\r
+ if ( pi->ungetToken ) {\r
+ Com_ScriptError( "UngetToken called twice" );\r
+ }\r
+ pi->ungetToken = qtrue;\r
+}\r
+\r
+\r
+static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {\r
+ int c;\r
+\r
+ while( (c = *data) <= ' ') {\r
+ if( !c ) {\r
+ return NULL;\r
+ }\r
+ if( c == '\n' ) {\r
+ pi->lines++;\r
+ *hasNewLines = qtrue;\r
+ }\r
+ data++;\r
+ }\r
+\r
+ return data;\r
+}\r
+\r
+/*\r
+==============\r
+Com_ParseExt\r
+\r
+Parse a token out of a string\r
+Will never return NULL, just empty strings.\r
+An empty string will only be returned at end of file.\r
+\r
+If "allowLineBreaks" is qtrue then an empty\r
+string will be returned if the next token is\r
+a newline.\r
+==============\r
+*/\r
+static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) {\r
+ int c = 0, len;\r
+ qboolean hasNewLines = qfalse;\r
+ const char *data;\r
+ const char **punc;\r
+\r
+ if ( !data_p ) {\r
+ Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );\r
+ }\r
+\r
+ data = *data_p;\r
+ len = 0;\r
+ pi->token[0] = 0;\r
+\r
+ // make sure incoming data is valid\r
+ if ( !data ) {\r
+ *data_p = NULL;\r
+ return pi->token;\r
+ }\r
+\r
+ // skip any leading whitespace\r
+ while ( 1 ) {\r
+ // skip whitespace\r
+ data = SkipWhitespace( data, &hasNewLines );\r
+ if ( !data ) {\r
+ *data_p = NULL;\r
+ return pi->token;\r
+ }\r
+ if ( hasNewLines && !allowLineBreaks ) {\r
+ *data_p = data;\r
+ return pi->token;\r
+ }\r
+\r
+ c = *data;\r
+\r
+ // skip double slash comments\r
+ if ( c == '/' && data[1] == '/' ) {\r
+ while (*data && *data != '\n') {\r
+ data++;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ // skip /* */ comments\r
+ if ( c=='/' && data[1] == '*' ) {\r
+ while ( *data && ( *data != '*' || data[1] != '/' ) ) {\r
+ if( *data == '\n' ) {\r
+ pi->lines++;\r
+ }\r
+ data++;\r
+ }\r
+ if ( *data ) {\r
+ data += 2;\r
+ }\r
+ continue;\r
+ }\r
+\r
+ // a real token to parse\r
+ break;\r
+ }\r
+\r
+ // handle quoted strings\r
+ if ( c == '\"' ) {\r
+ data++;\r
+ while( 1 ) {\r
+ c = *data++;\r
+ if ( ( c=='\\' ) && ( *data == '\"' ) ) {\r
+ // allow quoted strings to use \" to indicate the " character\r
+ data++;\r
+ } else if ( c=='\"' || !c ) {\r
+ pi->token[len] = 0;\r
+ *data_p = ( char * ) data;\r
+ return pi->token;\r
+ } else if( *data == '\n' ) {\r
+ pi->lines++;\r
+ }\r
+ if ( len < MAX_TOKEN_CHARS - 1 ) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ }\r
+ }\r
+\r
+ // check for a number\r
+ // is this parsing of negative numbers going to cause expression problems\r
+ if ( ( c >= '0' && c <= '9' ) || ( c == '-' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) || \r
+ ( c == '.' && data[ 1 ] >= '0' && data[ 1 ] <= '9' ) ) {\r
+ do {\r
+\r
+ if (len < MAX_TOKEN_CHARS - 1) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ data++;\r
+\r
+ c = *data;\r
+ } while ( ( c >= '0' && c <= '9' ) || c == '.' );\r
+\r
+ // parse the exponent\r
+ if ( c == 'e' || c == 'E' ) {\r
+ if (len < MAX_TOKEN_CHARS - 1) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ data++;\r
+ c = *data;\r
+\r
+ if ( c == '-' || c == '+' ) {\r
+ if (len < MAX_TOKEN_CHARS - 1) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ data++;\r
+ c = *data;\r
+ }\r
+\r
+ do {\r
+ if (len < MAX_TOKEN_CHARS - 1) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ data++;\r
+\r
+ c = *data;\r
+ } while ( c >= '0' && c <= '9' );\r
+ }\r
+\r
+ if (len == MAX_TOKEN_CHARS) {\r
+ len = 0;\r
+ }\r
+ pi->token[len] = 0;\r
+\r
+ *data_p = ( char * ) data;\r
+ return pi->token;\r
+ }\r
+\r
+ // check for a regular word\r
+ // we still allow forward and back slashes in name tokens for pathnames\r
+ // and also colons for drive letters\r
+ if ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' || c == '/' || c == '\\' ) {\r
+ do {\r
+ if (len < MAX_TOKEN_CHARS - 1) {\r
+ pi->token[len] = c;\r
+ len++;\r
+ }\r
+ data++;\r
+\r
+ c = *data;\r
+ } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' \r
+ || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );\r
+\r
+ if (len == MAX_TOKEN_CHARS) {\r
+ len = 0;\r
+ }\r
+ pi->token[len] = 0;\r
+\r
+ *data_p = ( char * ) data;\r
+ return pi->token;\r
+ }\r
+\r
+ // check for multi-character punctuation token\r
+ for ( punc = punctuation ; *punc ; punc++ ) {\r
+ int l;\r
+ int j;\r
+\r
+ l = strlen( *punc );\r
+ for ( j = 0 ; j < l ; j++ ) {\r
+ if ( data[j] != (*punc)[j] ) {\r
+ break;\r
+ }\r
+ }\r
+ if ( j == l ) {\r
+ // a valid multi-character punctuation\r
+ memcpy( pi->token, *punc, l );\r
+ pi->token[l] = 0;\r
+ data += l;\r
+ *data_p = (char *)data;\r
+ return pi->token;\r
+ }\r
+ }\r
+\r
+ // single character punctuation\r
+ pi->token[0] = *data;\r
+ pi->token[1] = 0;\r
+ data++;\r
+ *data_p = (char *)data;\r
+\r
+ return pi->token;\r
+}\r
+\r
+/*\r
+===================\r
+Com_Parse\r
+===================\r
+*/\r
+const char *Com_Parse( const char *(*data_p) ) {\r
+ if ( pi->ungetToken ) {\r
+ pi->ungetToken = qfalse;\r
+ return pi->token;\r
+ }\r
+ return Com_ParseExt( data_p, qtrue );\r
+}\r
+\r
+/*\r
+===================\r
+Com_ParseOnLine\r
+===================\r
+*/\r
+const char *Com_ParseOnLine( const char *(*data_p) ) {\r
+ if ( pi->ungetToken ) {\r
+ pi->ungetToken = qfalse;\r
+ return pi->token;\r
+ }\r
+ return Com_ParseExt( data_p, qfalse );\r
+}\r
+\r
+\r
+\r
+/*\r
+==================\r
+Com_MatchToken\r
+==================\r
+*/\r
+void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {\r
+ const char *token;\r
+\r
+ token = Com_Parse( buf_p );\r
+ if ( strcmp( token, match ) ) {\r
+ if (warning) {\r
+ Com_ScriptWarning( "MatchToken: %s != %s", token, match );\r
+ } else {\r
+ Com_ScriptError( "MatchToken: %s != %s", token, match );\r
+ }\r
+ }\r
+}\r
+\r
+\r
+/*\r
+=================\r
+Com_SkipBracedSection\r
+\r
+The next token should be an open brace.\r
+Skips until a matching close brace is found.\r
+Internal brace depths are properly skipped.\r
+=================\r
+*/\r
+void Com_SkipBracedSection( const char *(*program) ) {\r
+ const char *token;\r
+ int depth;\r
+\r
+ depth = 0;\r
+ do {\r
+ token = Com_Parse( program );\r
+ if( token[1] == 0 ) {\r
+ if( token[0] == '{' ) {\r
+ depth++;\r
+ }\r
+ else if( token[0] == '}' ) {\r
+ depth--;\r
+ }\r
+ }\r
+ } while( depth && *program );\r
+}\r
+\r
+/*\r
+=================\r
+Com_SkipRestOfLine\r
+=================\r
+*/\r
+void Com_SkipRestOfLine ( const char *(*data) ) {\r
+ const char *p;\r
+ int c;\r
+\r
+ p = *data;\r
+ while ( (c = *p++) != 0 ) {\r
+ if ( c == '\n' ) {\r
+ pi->lines++;\r
+ break;\r
+ }\r
+ }\r
+\r
+ *data = p;\r
+}\r
+\r
+/*\r
+====================\r
+Com_ParseRestOfLine\r
+====================\r
+*/\r
+const char *Com_ParseRestOfLine( const char *(*data_p) ) {\r
+ static char line[MAX_TOKEN_CHARS];\r
+ const char *token;\r
+\r
+ line[0] = 0;\r
+ while( 1 ) {\r
+ token = Com_ParseOnLine( data_p );\r
+ if ( !token[0] ) {\r
+ break;\r
+ }\r
+ if ( line[0] ) {\r
+ Q_strcat( line, sizeof(line), " " );\r
+ }\r
+ Q_strcat( line, sizeof(line), token );\r
+ }\r
+\r
+ return line;\r
+}\r
+\r
+\r
+float Com_ParseFloat( const char *(*buf_p) ) {\r
+ const char *token;\r
+\r
+ token = Com_Parse( buf_p );\r
+ if ( !token[0] ) {\r
+ return 0;\r
+ }\r
+ return atof( token );\r
+}\r
+\r
+int Com_ParseInt( const char *(*buf_p) ) {\r
+ const char *token;\r
+\r
+ token = Com_Parse( buf_p );\r
+ if ( !token[0] ) {\r
+ return 0;\r
+ }\r
+ return (int)atof( token );\r
+}\r
+\r
+\r
+\r
+void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {\r
+ const char *token;\r
+ int i;\r
+\r
+ Com_MatchToken( buf_p, "(" );\r
+\r
+ for (i = 0 ; i < x ; i++) {\r
+ token = Com_Parse(buf_p);\r
+ m[i] = atof(token);\r
+ }\r
+\r
+ Com_MatchToken( buf_p, ")" );\r
+}\r
+\r
+void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {\r
+ int i;\r
+\r
+ Com_MatchToken( buf_p, "(" );\r
+\r
+ for (i = 0 ; i < y ; i++) {\r
+ Com_Parse1DMatrix (buf_p, x, m + i * x);\r
+ }\r
+\r
+ Com_MatchToken( buf_p, ")" );\r
+}\r
+\r
+void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {\r
+ int i;\r
+\r
+ Com_MatchToken( buf_p, "(" );\r
+\r
+ for (i = 0 ; i < z ; i++) {\r
+ Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);\r
+ }\r
+\r
+ Com_MatchToken( buf_p, ")" );\r
+}\r
+\r