transfer from internal tree r5311 branches/1.4-gpl
[xonotic/netradiant.git] / libs / splines / q_parse.cpp
1 /*\r
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
3 For a list of contributors, see the accompanying CONTRIBUTORS file.\r
4 \r
5 This file is part of GtkRadiant.\r
6 \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
11 \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
16 \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
20 */\r
21 \r
22 // q_parse.c -- support for parsing text files\r
23 \r
24 #include "q_shared.h"\r
25 \r
26 /*\r
27 ============================================================================\r
28 \r
29 PARSING\r
30 \r
31 ============================================================================\r
32 */\r
33 \r
34 // multiple character punctuation tokens\r
35 static const char *punctuation[] = {\r
36         "+=", "-=",  "*=",  "/=", "&=", "|=", "++", "--",\r
37                 "&&", "||",  "<=",  ">=", "==", "!=",\r
38         NULL\r
39 };\r
40 \r
41 typedef struct {\r
42         char    token[MAX_TOKEN_CHARS];\r
43         int             lines;\r
44         qboolean        ungetToken;\r
45         char    parseFile[MAX_QPATH];\r
46 } parseInfo_t;\r
47 \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
52 \r
53 /*\r
54 ===================\r
55 Com_BeginParseSession\r
56 ===================\r
57 */\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
61         }\r
62         parseInfoNum++;\r
63         pi = &parseInfo[parseInfoNum];\r
64 \r
65         pi->lines = 1;\r
66         Q_strncpyz( pi->parseFile, filename, sizeof( pi->parseFile ) );\r
67 }\r
68 \r
69 /*\r
70 ===================\r
71 Com_EndParseSession\r
72 ===================\r
73 */\r
74 void Com_EndParseSession( void ) {\r
75         if ( parseInfoNum == 0 ) {\r
76                 Com_Error( ERR_FATAL, "Com_EndParseSession: session underflow" );\r
77         }\r
78         parseInfoNum--;\r
79         pi = &parseInfo[parseInfoNum];\r
80 }\r
81 \r
82 /*\r
83 ===================\r
84 Com_GetCurrentParseLine\r
85 ===================\r
86 */\r
87 int Com_GetCurrentParseLine( void ) {\r
88         return pi->lines;\r
89 }\r
90 \r
91 /*\r
92 ===================\r
93 Com_ScriptError\r
94 \r
95 Prints the script name and line number in the message\r
96 ===================\r
97 */\r
98 void Com_ScriptError( const char *msg, ... ) {\r
99         va_list         argptr;\r
100         char            string[32000];\r
101 \r
102         va_start( argptr, msg );\r
103         vsprintf( string, msg,argptr );\r
104         va_end( argptr );\r
105 \r
106         Com_Error( ERR_DROP, "File %s, line %i: %s", pi->parseFile, pi->lines, string );\r
107 }\r
108 \r
109 void Com_ScriptWarning( const char *msg, ... ) {\r
110         va_list         argptr;\r
111         char            string[32000];\r
112 \r
113         va_start( argptr, msg );\r
114         vsprintf( string, msg,argptr );\r
115         va_end( argptr );\r
116 \r
117         Com_Printf( "File %s, line %i: %s", pi->parseFile, pi->lines, string );\r
118 }\r
119 \r
120 \r
121 /*\r
122 ===================\r
123 Com_UngetToken\r
124 \r
125 Calling this will make the next Com_Parse return\r
126 the current token instead of advancing the pointer\r
127 ===================\r
128 */\r
129 void Com_UngetToken( void ) {\r
130         if ( pi->ungetToken ) {\r
131                 Com_ScriptError( "UngetToken called twice" );\r
132         }\r
133         pi->ungetToken = qtrue;\r
134 }\r
135 \r
136 \r
137 static const char *SkipWhitespace( const char (*data), qboolean *hasNewLines ) {\r
138         int c;\r
139 \r
140         while( (c = *data) <= ' ') {\r
141                 if( !c ) {\r
142                         return NULL;\r
143                 }\r
144                 if( c == '\n' ) {\r
145                         pi->lines++;\r
146                         *hasNewLines = qtrue;\r
147                 }\r
148                 data++;\r
149         }\r
150 \r
151         return data;\r
152 }\r
153 \r
154 /*\r
155 ==============\r
156 Com_ParseExt\r
157 \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
161 \r
162 If "allowLineBreaks" is qtrue then an empty\r
163 string will be returned if the next token is\r
164 a newline.\r
165 ==============\r
166 */\r
167 static char *Com_ParseExt( const char *(*data_p), qboolean allowLineBreaks ) {\r
168         int c = 0, len;\r
169         qboolean hasNewLines = qfalse;\r
170         const char *data;\r
171         const char **punc;\r
172 \r
173         if ( !data_p ) {\r
174                 Com_Error( ERR_FATAL, "Com_ParseExt: NULL data_p" );\r
175         }\r
176 \r
177         data = *data_p;\r
178         len = 0;\r
179         pi->token[0] = 0;\r
180 \r
181         // make sure incoming data is valid\r
182         if ( !data ) {\r
183                 *data_p = NULL;\r
184                 return pi->token;\r
185         }\r
186 \r
187         // skip any leading whitespace\r
188         while ( 1 ) {\r
189                 // skip whitespace\r
190                 data = SkipWhitespace( data, &hasNewLines );\r
191                 if ( !data ) {\r
192                         *data_p = NULL;\r
193                         return pi->token;\r
194                 }\r
195                 if ( hasNewLines && !allowLineBreaks ) {\r
196                         *data_p = data;\r
197                         return pi->token;\r
198                 }\r
199 \r
200                 c = *data;\r
201 \r
202                 // skip double slash comments\r
203                 if ( c == '/' && data[1] == '/' ) {\r
204                         while (*data && *data != '\n') {\r
205                                 data++;\r
206                         }\r
207                         continue;\r
208                 }\r
209 \r
210                 // skip /* */ comments\r
211                 if ( c=='/' && data[1] == '*' ) {\r
212                         while ( *data && ( *data != '*' || data[1] != '/' ) ) {\r
213                                 if( *data == '\n' ) {\r
214                                         pi->lines++;\r
215                                 }\r
216                                 data++;\r
217                         }\r
218                         if ( *data ) {\r
219                                 data += 2;\r
220                         }\r
221                         continue;\r
222                 }\r
223 \r
224                 // a real token to parse\r
225                 break;\r
226         }\r
227 \r
228         // handle quoted strings\r
229         if ( c == '\"' ) {\r
230                 data++;\r
231                 while( 1 ) {\r
232                         c = *data++;\r
233                         if ( ( c=='\\' ) && ( *data == '\"' ) ) {\r
234                                 // allow quoted strings to use \" to indicate the " character\r
235                                 data++;\r
236                         } else if ( c=='\"' || !c ) {\r
237                                 pi->token[len] = 0;\r
238                                 *data_p = ( char * ) data;\r
239                                 return pi->token;\r
240                         } else if( *data == '\n' ) {\r
241                                 pi->lines++;\r
242                         }\r
243                         if ( len < MAX_TOKEN_CHARS - 1 ) {\r
244                                 pi->token[len] = c;\r
245                                 len++;\r
246                         }\r
247                 }\r
248         }\r
249 \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
254                 do  {\r
255 \r
256                         if (len < MAX_TOKEN_CHARS - 1) {\r
257                                 pi->token[len] = c;\r
258                                 len++;\r
259                         }\r
260                         data++;\r
261 \r
262                         c = *data;\r
263                 } while ( ( c >= '0' && c <= '9' ) || c == '.' );\r
264 \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
269                                 len++;\r
270                         }\r
271                         data++;\r
272                         c = *data;\r
273 \r
274                         if ( c == '-' || c == '+' ) {\r
275                                 if (len < MAX_TOKEN_CHARS - 1) {\r
276                                         pi->token[len] = c;\r
277                                         len++;\r
278                                 }\r
279                                 data++;\r
280                                 c = *data;\r
281                         }\r
282 \r
283                         do  {\r
284                                 if (len < MAX_TOKEN_CHARS - 1) {\r
285                                         pi->token[len] = c;\r
286                                         len++;\r
287                                 }\r
288                                 data++;\r
289 \r
290                                 c = *data;\r
291                         } while ( c >= '0' && c <= '9' );\r
292                 }\r
293 \r
294                 if (len == MAX_TOKEN_CHARS) {\r
295                         len = 0;\r
296                 }\r
297                 pi->token[len] = 0;\r
298 \r
299                 *data_p = ( char * ) data;\r
300                 return pi->token;\r
301         }\r
302 \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
307                 do  {\r
308                         if (len < MAX_TOKEN_CHARS - 1) {\r
309                                 pi->token[len] = c;\r
310                                 len++;\r
311                         }\r
312                         data++;\r
313 \r
314                         c = *data;\r
315                 } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' \r
316                         || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );\r
317 \r
318                 if (len == MAX_TOKEN_CHARS) {\r
319                         len = 0;\r
320                 }\r
321                 pi->token[len] = 0;\r
322 \r
323                 *data_p = ( char * ) data;\r
324                 return pi->token;\r
325         }\r
326 \r
327         // check for multi-character punctuation token\r
328         for ( punc = punctuation ; *punc ; punc++ ) {\r
329                 int             l;\r
330                 int             j;\r
331 \r
332                 l = strlen( *punc );\r
333                 for ( j = 0 ; j < l ; j++ ) {\r
334                         if ( data[j] != (*punc)[j] ) {\r
335                                 break;\r
336                         }\r
337                 }\r
338                 if ( j == l ) {\r
339                         // a valid multi-character punctuation\r
340                         memcpy( pi->token, *punc, l );\r
341                         pi->token[l] = 0;\r
342                         data += l;\r
343                         *data_p = (char *)data;\r
344                         return pi->token;\r
345                 }\r
346         }\r
347 \r
348         // single character punctuation\r
349         pi->token[0] = *data;\r
350         pi->token[1] = 0;\r
351         data++;\r
352         *data_p = (char *)data;\r
353 \r
354         return pi->token;\r
355 }\r
356 \r
357 /*\r
358 ===================\r
359 Com_Parse\r
360 ===================\r
361 */\r
362 const char *Com_Parse( const char *(*data_p) ) {\r
363         if ( pi->ungetToken ) {\r
364                 pi->ungetToken = qfalse;\r
365                 return pi->token;\r
366         }\r
367         return Com_ParseExt( data_p, qtrue );\r
368 }\r
369 \r
370 /*\r
371 ===================\r
372 Com_ParseOnLine\r
373 ===================\r
374 */\r
375 const char *Com_ParseOnLine( const char *(*data_p) ) {\r
376         if ( pi->ungetToken ) {\r
377                 pi->ungetToken = qfalse;\r
378                 return pi->token;\r
379         }\r
380         return Com_ParseExt( data_p, qfalse );\r
381 }\r
382 \r
383 \r
384 \r
385 /*\r
386 ==================\r
387 Com_MatchToken\r
388 ==================\r
389 */\r
390 void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {\r
391         const char      *token;\r
392 \r
393         token = Com_Parse( buf_p );\r
394         if ( strcmp( token, match ) ) {\r
395                 if (warning) {\r
396                         Com_ScriptWarning( "MatchToken: %s != %s", token, match );\r
397                 } else {\r
398                         Com_ScriptError( "MatchToken: %s != %s", token, match );\r
399                 }\r
400         }\r
401 }\r
402 \r
403 \r
404 /*\r
405 =================\r
406 Com_SkipBracedSection\r
407 \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
411 =================\r
412 */\r
413 void Com_SkipBracedSection( const char *(*program) ) {\r
414         const char                      *token;\r
415         int                             depth;\r
416 \r
417         depth = 0;\r
418         do {\r
419                 token = Com_Parse( program );\r
420                 if( token[1] == 0 ) {\r
421                         if( token[0] == '{' ) {\r
422                                 depth++;\r
423                         }\r
424                         else if( token[0] == '}' ) {\r
425                                 depth--;\r
426                         }\r
427                 }\r
428         } while( depth && *program );\r
429 }\r
430 \r
431 /*\r
432 =================\r
433 Com_SkipRestOfLine\r
434 =================\r
435 */\r
436 void Com_SkipRestOfLine ( const char *(*data) ) {\r
437         const char      *p;\r
438         int             c;\r
439 \r
440         p = *data;\r
441         while ( (c = *p++) != 0 ) {\r
442                 if ( c == '\n' ) {\r
443                         pi->lines++;\r
444                         break;\r
445                 }\r
446         }\r
447 \r
448         *data = p;\r
449 }\r
450 \r
451 /*\r
452 ====================\r
453 Com_ParseRestOfLine\r
454 ====================\r
455 */\r
456 const char *Com_ParseRestOfLine( const char *(*data_p) ) {\r
457         static char     line[MAX_TOKEN_CHARS];\r
458         const char *token;\r
459 \r
460         line[0] = 0;\r
461         while( 1 ) {\r
462                 token = Com_ParseOnLine( data_p );\r
463                 if ( !token[0] ) {\r
464                         break;\r
465                 }\r
466                 if ( line[0] ) {\r
467                         Q_strcat( line, sizeof(line), " " );\r
468                 }\r
469                 Q_strcat( line, sizeof(line), token );\r
470         }\r
471 \r
472         return line;\r
473 }\r
474 \r
475 \r
476 float Com_ParseFloat( const char *(*buf_p) ) {\r
477         const char              *token;\r
478 \r
479         token = Com_Parse( buf_p );\r
480         if ( !token[0] ) {\r
481                 return 0;\r
482         }\r
483         return atof( token );\r
484 }\r
485 \r
486 int Com_ParseInt( const char *(*buf_p) ) {\r
487         const char              *token;\r
488 \r
489         token = Com_Parse( buf_p );\r
490         if ( !token[0] ) {\r
491                 return 0;\r
492         }\r
493         return (int)atof( token );\r
494 }\r
495 \r
496 \r
497 \r
498 void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {\r
499         const char      *token;\r
500         int             i;\r
501 \r
502         Com_MatchToken( buf_p, "(" );\r
503 \r
504         for (i = 0 ; i < x ; i++) {\r
505                 token = Com_Parse(buf_p);\r
506                 m[i] = atof(token);\r
507         }\r
508 \r
509         Com_MatchToken( buf_p, ")" );\r
510 }\r
511 \r
512 void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {\r
513         int             i;\r
514 \r
515         Com_MatchToken( buf_p, "(" );\r
516 \r
517         for (i = 0 ; i < y ; i++) {\r
518                 Com_Parse1DMatrix (buf_p, x, m + i * x);\r
519         }\r
520 \r
521         Com_MatchToken( buf_p, ")" );\r
522 }\r
523 \r
524 void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {\r
525         int             i;\r
526 \r
527         Com_MatchToken( buf_p, "(" );\r
528 \r
529         for (i = 0 ; i < z ; i++) {\r
530                 Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);\r
531         }\r
532 \r
533         Com_MatchToken( buf_p, ")" );\r
534 }\r
535 \r