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