remove RSA's md4.c, replace by DP's
[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                         } else if ( c=='\"' || !c ) {
237                                 pi->token[len] = 0;
238                                 *data_p = ( char * ) data;
239                                 return pi->token;
240                         } else if( *data == '\n' ) {
241                                 pi->lines++;
242                         }
243                         if ( len < MAX_TOKEN_CHARS - 1 ) {
244                                 pi->token[len] = c;
245                                 len++;
246                         }
247                 }
248         }
249
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' ) ) {
254                 do  {
255
256                         if (len < MAX_TOKEN_CHARS - 1) {
257                                 pi->token[len] = c;
258                                 len++;
259                         }
260                         data++;
261
262                         c = *data;
263                 } while ( ( c >= '0' && c <= '9' ) || c == '.' );
264
265                 // parse the exponent
266                 if ( c == 'e' || c == 'E' ) {
267                         if (len < MAX_TOKEN_CHARS - 1) {
268                                 pi->token[len] = c;
269                                 len++;
270                         }
271                         data++;
272                         c = *data;
273
274                         if ( c == '-' || c == '+' ) {
275                                 if (len < MAX_TOKEN_CHARS - 1) {
276                                         pi->token[len] = c;
277                                         len++;
278                                 }
279                                 data++;
280                                 c = *data;
281                         }
282
283                         do  {
284                                 if (len < MAX_TOKEN_CHARS - 1) {
285                                         pi->token[len] = c;
286                                         len++;
287                                 }
288                                 data++;
289
290                                 c = *data;
291                         } while ( c >= '0' && c <= '9' );
292                 }
293
294                 if (len == MAX_TOKEN_CHARS) {
295                         len = 0;
296                 }
297                 pi->token[len] = 0;
298
299                 *data_p = ( char * ) data;
300                 return pi->token;
301         }
302
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 == '\\' ) {
307                 do  {
308                         if (len < MAX_TOKEN_CHARS - 1) {
309                                 pi->token[len] = c;
310                                 len++;
311                         }
312                         data++;
313
314                         c = *data;
315                 } while ( ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ) || c == '_' 
316                         || ( c >= '0' && c <= '9' ) || c == '/' || c == '\\' || c == ':' || c == '.' );
317
318                 if (len == MAX_TOKEN_CHARS) {
319                         len = 0;
320                 }
321                 pi->token[len] = 0;
322
323                 *data_p = ( char * ) data;
324                 return pi->token;
325         }
326
327         // check for multi-character punctuation token
328         for ( punc = punctuation ; *punc ; punc++ ) {
329                 int             l;
330                 int             j;
331
332                 l = strlen( *punc );
333                 for ( j = 0 ; j < l ; j++ ) {
334                         if ( data[j] != (*punc)[j] ) {
335                                 break;
336                         }
337                 }
338                 if ( j == l ) {
339                         // a valid multi-character punctuation
340                         memcpy( pi->token, *punc, l );
341                         pi->token[l] = 0;
342                         data += l;
343                         *data_p = (char *)data;
344                         return pi->token;
345                 }
346         }
347
348         // single character punctuation
349         pi->token[0] = *data;
350         pi->token[1] = 0;
351         data++;
352         *data_p = (char *)data;
353
354         return pi->token;
355 }
356
357 /*
358 ===================
359 Com_Parse
360 ===================
361 */
362 const char *Com_Parse( const char *(*data_p) ) {
363         if ( pi->ungetToken ) {
364                 pi->ungetToken = qfalse;
365                 return pi->token;
366         }
367         return Com_ParseExt( data_p, qtrue );
368 }
369
370 /*
371 ===================
372 Com_ParseOnLine
373 ===================
374 */
375 const char *Com_ParseOnLine( const char *(*data_p) ) {
376         if ( pi->ungetToken ) {
377                 pi->ungetToken = qfalse;
378                 return pi->token;
379         }
380         return Com_ParseExt( data_p, qfalse );
381 }
382
383
384
385 /*
386 ==================
387 Com_MatchToken
388 ==================
389 */
390 void Com_MatchToken( const char *(*buf_p), const char *match, qboolean warning ) {
391         const char      *token;
392
393         token = Com_Parse( buf_p );
394         if ( strcmp( token, match ) ) {
395                 if (warning) {
396                         Com_ScriptWarning( "MatchToken: %s != %s", token, match );
397                 } else {
398                         Com_ScriptError( "MatchToken: %s != %s", token, match );
399                 }
400         }
401 }
402
403
404 /*
405 =================
406 Com_SkipBracedSection
407
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.
411 =================
412 */
413 void Com_SkipBracedSection( const char *(*program) ) {
414         const char                      *token;
415         int                             depth;
416
417         depth = 0;
418         do {
419                 token = Com_Parse( program );
420                 if( token[1] == 0 ) {
421                         if( token[0] == '{' ) {
422                                 depth++;
423                         }
424                         else if( token[0] == '}' ) {
425                                 depth--;
426                         }
427                 }
428         } while( depth && *program );
429 }
430
431 /*
432 =================
433 Com_SkipRestOfLine
434 =================
435 */
436 void Com_SkipRestOfLine ( const char *(*data) ) {
437         const char      *p;
438         int             c;
439
440         p = *data;
441         while ( (c = *p++) != 0 ) {
442                 if ( c == '\n' ) {
443                         pi->lines++;
444                         break;
445                 }
446         }
447
448         *data = p;
449 }
450
451 /*
452 ====================
453 Com_ParseRestOfLine
454 ====================
455 */
456 const char *Com_ParseRestOfLine( const char *(*data_p) ) {
457         static char     line[MAX_TOKEN_CHARS];
458         const char *token;
459
460         line[0] = 0;
461         while( 1 ) {
462                 token = Com_ParseOnLine( data_p );
463                 if ( !token[0] ) {
464                         break;
465                 }
466                 if ( line[0] ) {
467                         Q_strcat( line, sizeof(line), " " );
468                 }
469                 Q_strcat( line, sizeof(line), token );
470         }
471
472         return line;
473 }
474
475
476 float Com_ParseFloat( const char *(*buf_p) ) {
477         const char              *token;
478
479         token = Com_Parse( buf_p );
480         if ( !token[0] ) {
481                 return 0;
482         }
483         return atof( token );
484 }
485
486 int Com_ParseInt( const char *(*buf_p) ) {
487         const char              *token;
488
489         token = Com_Parse( buf_p );
490         if ( !token[0] ) {
491                 return 0;
492         }
493         return (int)atof( token );
494 }
495
496
497
498 void Com_Parse1DMatrix( const char *(*buf_p), int x, float *m ) {
499         const char      *token;
500         int             i;
501
502         Com_MatchToken( buf_p, "(" );
503
504         for (i = 0 ; i < x ; i++) {
505                 token = Com_Parse(buf_p);
506                 m[i] = atof(token);
507         }
508
509         Com_MatchToken( buf_p, ")" );
510 }
511
512 void Com_Parse2DMatrix( const char *(*buf_p), int y, int x, float *m ) {
513         int             i;
514
515         Com_MatchToken( buf_p, "(" );
516
517         for (i = 0 ; i < y ; i++) {
518                 Com_Parse1DMatrix (buf_p, x, m + i * x);
519         }
520
521         Com_MatchToken( buf_p, ")" );
522 }
523
524 void Com_Parse3DMatrix( const char *(*buf_p), int z, int y, int x, float *m ) {
525         int             i;
526
527         Com_MatchToken( buf_p, "(" );
528
529         for (i = 0 ; i < z ; i++) {
530                 Com_Parse2DMatrix (buf_p, y, x, m + i * x*y);
531         }
532
533         Com_MatchToken( buf_p, ")" );
534 }
535