1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "idlib/Heap.h"
31 #include "framework/Common.h"
32 #include "framework/FileSystem.h"
33 
34 #include "idlib/Lexer.h"
35 
36 #define PUNCTABLE
37 
38 //longer punctuations first
39 static const punctuation_t default_punctuations[] = {
40 	//binary operators
41 	{">>=",P_RSHIFT_ASSIGN},
42 	{"<<=",P_LSHIFT_ASSIGN},
43 	//
44 	{"...",P_PARMS},
45 	//define merge operator
46 	{"##",P_PRECOMPMERGE},				// pre-compiler
47 	//logic operators
48 	{"&&",P_LOGIC_AND},					// pre-compiler
49 	{"||",P_LOGIC_OR},					// pre-compiler
50 	{">=",P_LOGIC_GEQ},					// pre-compiler
51 	{"<=",P_LOGIC_LEQ},					// pre-compiler
52 	{"==",P_LOGIC_EQ},					// pre-compiler
53 	{"!=",P_LOGIC_UNEQ},				// pre-compiler
54 	//arithmatic operators
55 	{"*=",P_MUL_ASSIGN},
56 	{"/=",P_DIV_ASSIGN},
57 	{"%=",P_MOD_ASSIGN},
58 	{"+=",P_ADD_ASSIGN},
59 	{"-=",P_SUB_ASSIGN},
60 	{"++",P_INC},
61 	{"--",P_DEC},
62 	//binary operators
63 	{"&=",P_BIN_AND_ASSIGN},
64 	{"|=",P_BIN_OR_ASSIGN},
65 	{"^=",P_BIN_XOR_ASSIGN},
66 	{">>",P_RSHIFT},					// pre-compiler
67 	{"<<",P_LSHIFT},					// pre-compiler
68 	//reference operators
69 	{"->",P_POINTERREF},
70 	//C++
71 	{"::",P_CPP1},
72 	{".*",P_CPP2},
73 	//arithmatic operators
74 	{"*",P_MUL},						// pre-compiler
75 	{"/",P_DIV},						// pre-compiler
76 	{"%",P_MOD},						// pre-compiler
77 	{"+",P_ADD},						// pre-compiler
78 	{"-",P_SUB},						// pre-compiler
79 	{"=",P_ASSIGN},
80 	//binary operators
81 	{"&",P_BIN_AND},					// pre-compiler
82 	{"|",P_BIN_OR},						// pre-compiler
83 	{"^",P_BIN_XOR},					// pre-compiler
84 	{"~",P_BIN_NOT},					// pre-compiler
85 	//logic operators
86 	{"!",P_LOGIC_NOT},					// pre-compiler
87 	{">",P_LOGIC_GREATER},				// pre-compiler
88 	{"<",P_LOGIC_LESS},					// pre-compiler
89 	//reference operator
90 	{".",P_REF},
91 	//seperators
92 	{",",P_COMMA},						// pre-compiler
93 	{";",P_SEMICOLON},
94 	//label indication
95 	{":",P_COLON},						// pre-compiler
96 	//if statement
97 	{"?",P_QUESTIONMARK},				// pre-compiler
98 	//embracements
99 	{"(",P_PARENTHESESOPEN},			// pre-compiler
100 	{")",P_PARENTHESESCLOSE},			// pre-compiler
101 	{"{",P_BRACEOPEN},					// pre-compiler
102 	{"}",P_BRACECLOSE},					// pre-compiler
103 	{"[",P_SQBRACKETOPEN},
104 	{"]",P_SQBRACKETCLOSE},
105 	//
106 	{"\\",P_BACKSLASH},
107 	//precompiler operator
108 	{"#",P_PRECOMP},					// pre-compiler
109 	{"$",P_DOLLAR},
110 	{NULL, 0}
111 };
112 
113 int default_punctuationtable[256];
114 int default_nextpunctuation[sizeof(default_punctuations) / sizeof(punctuation_t)];
115 int default_setup;
116 
117 char idLexer::baseFolder[ 256 ];
118 
119 /*
120 ================
121 idLexer::CreatePunctuationTable
122 ================
123 */
CreatePunctuationTable(const punctuation_t * punctuations)124 void idLexer::CreatePunctuationTable( const punctuation_t *punctuations ) {
125 	int i, n, lastp;
126 	const punctuation_t *p, *newp;
127 
128 	//get memory for the table
129 	if ( punctuations == default_punctuations ) {
130 		idLexer::punctuationtable = default_punctuationtable;
131 		idLexer::nextpunctuation = default_nextpunctuation;
132 		if ( default_setup ) {
133 			return;
134 		}
135 		default_setup = true;
136 		i = sizeof(default_punctuations) / sizeof(punctuation_t);
137 	}
138 	else {
139 		if ( !idLexer::punctuationtable || idLexer::punctuationtable == default_punctuationtable ) {
140 			idLexer::punctuationtable = (int *) Mem_Alloc(256 * sizeof(int));
141 		}
142 		if ( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation ) {
143 			Mem_Free( idLexer::nextpunctuation );
144 		}
145 		for (i = 0; punctuations[i].p; i++) {
146 		}
147 		idLexer::nextpunctuation = (int *) Mem_Alloc(i * sizeof(int));
148 	}
149 	memset(idLexer::punctuationtable, 0xFF, 256 * sizeof(int));
150 	memset(idLexer::nextpunctuation, 0xFF, i * sizeof(int));
151 	//add the punctuations in the list to the punctuation table
152 	for (i = 0; punctuations[i].p; i++) {
153 		newp = &punctuations[i];
154 		lastp = -1;
155 		//sort the punctuations in this table entry on length (longer punctuations first)
156 		for (n = idLexer::punctuationtable[(unsigned int) newp->p[0]]; n >= 0; n = idLexer::nextpunctuation[n] ) {
157 			p = &punctuations[n];
158 			if (strlen(p->p) < strlen(newp->p)) {
159 				idLexer::nextpunctuation[i] = n;
160 				if (lastp >= 0) {
161 					idLexer::nextpunctuation[lastp] = i;
162 				}
163 				else {
164 					idLexer::punctuationtable[(unsigned int) newp->p[0]] = i;
165 				}
166 				break;
167 			}
168 			lastp = n;
169 		}
170 		if (n < 0) {
171 			idLexer::nextpunctuation[i] = -1;
172 			if (lastp >= 0) {
173 				idLexer::nextpunctuation[lastp] = i;
174 			}
175 			else {
176 				idLexer::punctuationtable[(unsigned int) newp->p[0]] = i;
177 			}
178 		}
179 	}
180 }
181 
182 /*
183 ================
184 idLexer::GetPunctuationFromId
185 ================
186 */
GetPunctuationFromId(int id)187 const char *idLexer::GetPunctuationFromId( int id ) {
188 	int i;
189 
190 	for (i = 0; idLexer::punctuations[i].p; i++) {
191 		if ( idLexer::punctuations[i].n == id ) {
192 			return idLexer::punctuations[i].p;
193 		}
194 	}
195 	return "unknown punctuation";
196 }
197 
198 /*
199 ================
200 idLexer::GetPunctuationId
201 ================
202 */
GetPunctuationId(const char * p)203 int idLexer::GetPunctuationId( const char *p ) {
204 	int i;
205 
206 	for (i = 0; idLexer::punctuations[i].p; i++) {
207 		if ( !strcmp(idLexer::punctuations[i].p, p) ) {
208 			return idLexer::punctuations[i].n;
209 		}
210 	}
211 	return 0;
212 }
213 
214 /*
215 ================
216 idLexer::Error
217 ================
218 */
Error(const char * str,...)219 void idLexer::Error( const char *str, ... ) {
220 	char text[MAX_STRING_CHARS];
221 	va_list ap;
222 
223 	hadError = true;
224 
225 	if ( idLexer::flags & LEXFL_NOERRORS ) {
226 		return;
227 	}
228 
229 	va_start(ap, str);
230 	vsprintf(text, str, ap);
231 	va_end(ap);
232 
233 	if ( idLexer::flags & LEXFL_NOFATALERRORS ) {
234 		idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
235 	} else {
236 		idLib::common->Error( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
237 	}
238 }
239 
240 /*
241 ================
242 idLexer::Warning
243 ================
244 */
Warning(const char * str,...)245 void idLexer::Warning( const char *str, ... ) {
246 	char text[MAX_STRING_CHARS];
247 	va_list ap;
248 
249 	if ( idLexer::flags & LEXFL_NOWARNINGS ) {
250 		return;
251 	}
252 
253 	va_start( ap, str );
254 	vsprintf( text, str, ap );
255 	va_end( ap );
256 	idLib::common->Warning( "file %s, line %d: %s", idLexer::filename.c_str(), idLexer::line, text );
257 }
258 
259 /*
260 ================
261 idLexer::SetPunctuations
262 ================
263 */
SetPunctuations(const punctuation_t * p)264 void idLexer::SetPunctuations( const punctuation_t *p ) {
265 #ifdef PUNCTABLE
266 	if (p) {
267 		idLexer::CreatePunctuationTable( p );
268 	}
269 	else {
270 		idLexer::CreatePunctuationTable( default_punctuations );
271 	}
272 #endif //PUNCTABLE
273 	if (p) {
274 		idLexer::punctuations = p;
275 	}
276 	else {
277 		idLexer::punctuations = default_punctuations;
278 	}
279 }
280 
281 /*
282 ================
283 idLexer::ReadWhiteSpace
284 
285 Reads spaces, tabs, C-like comments etc.
286 When a newline character is found the scripts line counter is increased.
287 ================
288 */
ReadWhiteSpace(void)289 int idLexer::ReadWhiteSpace( void ) {
290 	while(1) {
291 		// skip white space
292 		while(*idLexer::script_p <= ' ') {
293 			if (!*idLexer::script_p) {
294 				return 0;
295 			}
296 			if (*idLexer::script_p == '\n') {
297 				idLexer::line++;
298 			}
299 			idLexer::script_p++;
300 		}
301 		// skip comments
302 		if (*idLexer::script_p == '/') {
303 			// comments //
304 			if (*(idLexer::script_p+1) == '/') {
305 				idLexer::script_p++;
306 				do {
307 					idLexer::script_p++;
308 					if ( !*idLexer::script_p ) {
309 						return 0;
310 					}
311 				}
312 				while( *idLexer::script_p != '\n' );
313 				idLexer::line++;
314 				idLexer::script_p++;
315 				if ( !*idLexer::script_p ) {
316 					return 0;
317 				}
318 				continue;
319 			}
320 			// comments /* */
321 			else if (*(idLexer::script_p+1) == '*') {
322 				idLexer::script_p++;
323 				while( 1 ) {
324 					idLexer::script_p++;
325 					if ( !*idLexer::script_p ) {
326 						return 0;
327 					}
328 					if ( *idLexer::script_p == '\n' ) {
329 						idLexer::line++;
330 					}
331 					else if ( *idLexer::script_p == '/' ) {
332 						if ( *(idLexer::script_p-1) == '*' ) {
333 							break;
334 						}
335 						if ( *(idLexer::script_p+1) == '*' ) {
336 							idLexer::Warning( "nested comment" );
337 						}
338 					}
339 				}
340 				idLexer::script_p++;
341 				if ( !*idLexer::script_p ) {
342 					return 0;
343 				}
344 				idLexer::script_p++;
345 				if ( !*idLexer::script_p ) {
346 					return 0;
347 				}
348 				continue;
349 			}
350 		}
351 		break;
352 	}
353 	return 1;
354 }
355 
356 /*
357 ================
358 idLexer::ReadEscapeCharacter
359 ================
360 */
ReadEscapeCharacter(char * ch)361 int idLexer::ReadEscapeCharacter( char *ch ) {
362 	int c, val, i;
363 
364 	// step over the leading '\\'
365 	idLexer::script_p++;
366 	// determine the escape character
367 	switch(*idLexer::script_p) {
368 		case '\\': c = '\\'; break;
369 		case 'n': c = '\n'; break;
370 		case 'r': c = '\r'; break;
371 		case 't': c = '\t'; break;
372 		case 'v': c = '\v'; break;
373 		case 'b': c = '\b'; break;
374 		case 'f': c = '\f'; break;
375 		case 'a': c = '\a'; break;
376 		case '\'': c = '\''; break;
377 		case '\"': c = '\"'; break;
378 		case '\?': c = '\?'; break;
379 		case 'x':
380 		{
381 			idLexer::script_p++;
382 			for (i = 0, val = 0; ; i++, idLexer::script_p++) {
383 				c = *idLexer::script_p;
384 				if (c >= '0' && c <= '9')
385 					c = c - '0';
386 				else if (c >= 'A' && c <= 'Z')
387 					c = c - 'A' + 10;
388 				else if (c >= 'a' && c <= 'z')
389 					c = c - 'a' + 10;
390 				else
391 					break;
392 				val = (val << 4) + c;
393 			}
394 			idLexer::script_p--;
395 			if (val > 0xFF) {
396 				idLexer::Warning( "too large value in escape character" );
397 				val = 0xFF;
398 			}
399 			c = val;
400 			break;
401 		}
402 		default: //NOTE: decimal ASCII code, NOT octal
403 		{
404 			if (*idLexer::script_p < '0' || *idLexer::script_p > '9') {
405 				idLexer::Error("unknown escape char");
406 			}
407 			for (i = 0, val = 0; ; i++, idLexer::script_p++) {
408 				c = *idLexer::script_p;
409 				if (c >= '0' && c <= '9')
410 					c = c - '0';
411 				else
412 					break;
413 				val = val * 10 + c;
414 			}
415 			idLexer::script_p--;
416 			if (val > 0xFF) {
417 				idLexer::Warning( "too large value in escape character" );
418 				val = 0xFF;
419 			}
420 			c = val;
421 			break;
422 		}
423 	}
424 	// step over the escape character or the last digit of the number
425 	idLexer::script_p++;
426 	// store the escape character
427 	*ch = c;
428 	// succesfully read escape character
429 	return 1;
430 }
431 
432 /*
433 ================
434 idLexer::ReadString
435 
436 Escape characters are interpretted.
437 Reads two strings with only a white space between them as one string.
438 ================
439 */
ReadString(idToken * token,int quote)440 int idLexer::ReadString( idToken *token, int quote ) {
441 	int tmpline;
442 	const char *tmpscript_p;
443 	char ch;
444 
445 	if ( quote == '\"' ) {
446 		token->type = TT_STRING;
447 	} else {
448 		token->type = TT_LITERAL;
449 	}
450 
451 	// leading quote
452 	idLexer::script_p++;
453 
454 	while(1) {
455 		// if there is an escape character and escape characters are allowed
456 		if (*idLexer::script_p == '\\' && !(idLexer::flags & LEXFL_NOSTRINGESCAPECHARS)) {
457 			if ( !idLexer::ReadEscapeCharacter( &ch ) ) {
458 				return 0;
459 			}
460 			token->AppendDirty( ch );
461 		}
462 		// if a trailing quote
463 		else if (*idLexer::script_p == quote) {
464 			// step over the quote
465 			idLexer::script_p++;
466 			// if consecutive strings should not be concatenated
467 			if ( (idLexer::flags & LEXFL_NOSTRINGCONCAT) &&
468 					(!(idLexer::flags & LEXFL_ALLOWBACKSLASHSTRINGCONCAT) || (quote != '\"')) ) {
469 				break;
470 			}
471 
472 			tmpscript_p = idLexer::script_p;
473 			tmpline = idLexer::line;
474 			// read white space between possible two consecutive strings
475 			if ( !idLexer::ReadWhiteSpace() ) {
476 				idLexer::script_p = tmpscript_p;
477 				idLexer::line = tmpline;
478 				break;
479 			}
480 
481 			if ( idLexer::flags & LEXFL_NOSTRINGCONCAT ) {
482 				if ( *idLexer::script_p != '\\' ) {
483 					idLexer::script_p = tmpscript_p;
484 					idLexer::line = tmpline;
485 					break;
486 				}
487 				// step over the '\\'
488 				idLexer::script_p++;
489 				if ( !idLexer::ReadWhiteSpace() || ( *idLexer::script_p != quote ) ) {
490 					idLexer::Error( "expecting string after '\' terminated line" );
491 					return 0;
492 				}
493 			}
494 
495 			// if there's no leading qoute
496 			if ( *idLexer::script_p != quote ) {
497 				idLexer::script_p = tmpscript_p;
498 				idLexer::line = tmpline;
499 				break;
500 			}
501 			// step over the new leading quote
502 			idLexer::script_p++;
503 		}
504 		else {
505 			if (*idLexer::script_p == '\0') {
506 				idLexer::Error( "missing trailing quote" );
507 				return 0;
508 			}
509 			if (*idLexer::script_p == '\n') {
510 				idLexer::Error( "newline inside string" );
511 				return 0;
512 			}
513 			token->AppendDirty( *idLexer::script_p++ );
514 		}
515 	}
516 	token->data[token->len] = '\0';
517 
518 	if ( token->type == TT_LITERAL ) {
519 		if ( !(idLexer::flags & LEXFL_ALLOWMULTICHARLITERALS) ) {
520 			if ( token->Length() != 1 ) {
521 				idLexer::Warning( "literal is not one character long" );
522 			}
523 		}
524 		token->subtype = (*token)[0];
525 	}
526 	else {
527 		// the sub type is the length of the string
528 		token->subtype = token->Length();
529 	}
530 	return 1;
531 }
532 
533 /*
534 ================
535 idLexer::ReadName
536 ================
537 */
ReadName(idToken * token)538 int idLexer::ReadName( idToken *token ) {
539 	char c;
540 
541 	token->type = TT_NAME;
542 	do {
543 		token->AppendDirty( *idLexer::script_p++ );
544 		c = *idLexer::script_p;
545 	} while ((c >= 'a' && c <= 'z') ||
546 				(c >= 'A' && c <= 'Z') ||
547 				(c >= '0' && c <= '9') ||
548 				c == '_' ||
549 				// if treating all tokens as strings, don't parse '-' as a seperate token
550 				((idLexer::flags & LEXFL_ONLYSTRINGS) && (c == '-')) ||
551 				// if special path name characters are allowed
552 				((idLexer::flags & LEXFL_ALLOWPATHNAMES) && (c == '/' || c == '\\' || c == ':' || c == '.')) );
553 	token->data[token->len] = '\0';
554 	//the sub type is the length of the name
555 	token->subtype = token->Length();
556 	return 1;
557 }
558 
559 /*
560 ================
561 idLexer::CheckString
562 ================
563 */
CheckString(const char * str) const564 ID_INLINE int idLexer::CheckString( const char *str ) const {
565 	int i;
566 
567 	for ( i = 0; str[i]; i++ ) {
568 		if ( idLexer::script_p[i] != str[i] ) {
569 			return false;
570 		}
571 	}
572 	return true;
573 }
574 
575 /*
576 ================
577 idLexer::ReadNumber
578 ================
579 */
ReadNumber(idToken * token)580 int idLexer::ReadNumber( idToken *token ) {
581 	int i;
582 	int dot;
583 	char c, c2;
584 
585 	token->type = TT_NUMBER;
586 	token->subtype = 0;
587 	token->intvalue = 0;
588 	token->floatvalue = 0;
589 
590 	c = *idLexer::script_p;
591 	c2 = *(idLexer::script_p + 1);
592 
593 	if ( c == '0' && c2 != '.' ) {
594 		// check for a hexadecimal number
595 		if ( c2 == 'x' || c2 == 'X' ) {
596 			token->AppendDirty( *idLexer::script_p++ );
597 			token->AppendDirty( *idLexer::script_p++ );
598 			c = *idLexer::script_p;
599 			while((c >= '0' && c <= '9') ||
600 						(c >= 'a' && c <= 'f') ||
601 						(c >= 'A' && c <= 'F')) {
602 				token->AppendDirty( c );
603 				c = *(++idLexer::script_p);
604 			}
605 			token->subtype = TT_HEX | TT_INTEGER;
606 		}
607 		// check for a binary number
608 		else if ( c2 == 'b' || c2 == 'B' ) {
609 			token->AppendDirty( *idLexer::script_p++ );
610 			token->AppendDirty( *idLexer::script_p++ );
611 			c = *idLexer::script_p;
612 			while( c == '0' || c == '1' ) {
613 				token->AppendDirty( c );
614 				c = *(++idLexer::script_p);
615 			}
616 			token->subtype = TT_BINARY | TT_INTEGER;
617 		}
618 		// its an octal number
619 		else {
620 			token->AppendDirty( *idLexer::script_p++ );
621 			c = *idLexer::script_p;
622 			while( c >= '0' && c <= '7' ) {
623 				token->AppendDirty( c );
624 				c = *(++idLexer::script_p);
625 			}
626 			token->subtype = TT_OCTAL | TT_INTEGER;
627 		}
628 	}
629 	else {
630 		// decimal integer or floating point number or ip address
631 		dot = 0;
632 		while( 1 ) {
633 			if ( c >= '0' && c <= '9' ) {
634 			}
635 			else if ( c == '.' ) {
636 				dot++;
637 			}
638 			else {
639 				break;
640 			}
641 			token->AppendDirty( c );
642 			c = *(++idLexer::script_p);
643 		}
644 		if( c == 'e' && dot == 0) {
645 			//We have scientific notation without a decimal point
646 			dot++;
647 		}
648 		// if a floating point number
649 		if ( dot == 1 ) {
650 			token->subtype = TT_DECIMAL | TT_FLOAT;
651 			// check for floating point exponent
652 			if ( c == 'e' ) {
653 				//Append the e so that GetFloatValue code works
654 				token->AppendDirty( c );
655 				c = *(++idLexer::script_p);
656 				if ( c == '-' ) {
657 					token->AppendDirty( c );
658 					c = *(++idLexer::script_p);
659 				}
660 				else if ( c == '+' ) {
661 					token->AppendDirty( c );
662 					c = *(++idLexer::script_p);
663 				}
664 				while( c >= '0' && c <= '9' ) {
665 					token->AppendDirty( c );
666 					c = *(++idLexer::script_p);
667 				}
668 			}
669 			// check for floating point exception infinite 1.#INF or indefinite 1.#IND or NaN
670 			else if ( c == '#' ) {
671 				c2 = 4;
672 				if ( CheckString( "INF" ) ) {
673 					token->subtype |= TT_INFINITE;
674 				}
675 				else if ( CheckString( "IND" ) ) {
676 					token->subtype |= TT_INDEFINITE;
677 				}
678 				else if ( CheckString( "NAN" ) ) {
679 					token->subtype |= TT_NAN;
680 				}
681 				else if ( CheckString( "QNAN" ) ) {
682 					token->subtype |= TT_NAN;
683 					c2++;
684 				}
685 				else if ( CheckString( "SNAN" ) ) {
686 					token->subtype |= TT_NAN;
687 					c2++;
688 				}
689 				for ( i = 0; i < c2; i++ ) {
690 					token->AppendDirty( c );
691 					c = *(++idLexer::script_p);
692 				}
693 				while( c >= '0' && c <= '9' ) {
694 					token->AppendDirty( c );
695 					c = *(++idLexer::script_p);
696 				}
697 				if ( !(idLexer::flags & LEXFL_ALLOWFLOATEXCEPTIONS) ) {
698 					token->AppendDirty( 0 );	// zero terminate for c_str
699 					idLexer::Error( "parsed %s", token->c_str() );
700 				}
701 			}
702 		}
703 		else if ( dot > 1 ) {
704 			if ( !( idLexer::flags & LEXFL_ALLOWIPADDRESSES ) ) {
705 				idLexer::Error( "more than one dot in number" );
706 				return 0;
707 			}
708 			if ( dot != 3 ) {
709 				idLexer::Error( "ip address should have three dots" );
710 				return 0;
711 			}
712 			token->subtype = TT_IPADDRESS;
713 		}
714 		else {
715 			token->subtype = TT_DECIMAL | TT_INTEGER;
716 		}
717 	}
718 
719 	if ( token->subtype & TT_FLOAT ) {
720 		if ( c > ' ' ) {
721 			// single-precision: float
722 			if ( c == 'f' || c == 'F' ) {
723 				token->subtype |= TT_SINGLE_PRECISION;
724 				idLexer::script_p++;
725 			}
726 			// extended-precision: long double
727 			else if ( c == 'l' || c == 'L' ) {
728 				token->subtype |= TT_EXTENDED_PRECISION;
729 				idLexer::script_p++;
730 			}
731 			// default is double-precision: double
732 			else {
733 				token->subtype |= TT_DOUBLE_PRECISION;
734 			}
735 		}
736 		else {
737 			token->subtype |= TT_DOUBLE_PRECISION;
738 		}
739 	}
740 	else if ( token->subtype & TT_INTEGER ) {
741 		if ( c > ' ' ) {
742 			// default: signed long
743 			for ( i = 0; i < 2; i++ ) {
744 				// long integer
745 				if ( c == 'l' || c == 'L' ) {
746 					token->subtype |= TT_LONG;
747 				}
748 				// unsigned integer
749 				else if ( c == 'u' || c == 'U' ) {
750 					token->subtype |= TT_UNSIGNED;
751 				}
752 				else {
753 					break;
754 				}
755 				c = *(++idLexer::script_p);
756 			}
757 		}
758 	}
759 	else if ( token->subtype & TT_IPADDRESS ) {
760 		if ( c == ':' ) {
761 			token->AppendDirty( c );
762 			c = *(++idLexer::script_p);
763 			while( c >= '0' && c <= '9' ) {
764 				token->AppendDirty( c );
765 				c = *(++idLexer::script_p);
766 			}
767 			token->subtype |= TT_IPPORT;
768 		}
769 	}
770 	token->data[token->len] = '\0';
771 	return 1;
772 }
773 
774 /*
775 ================
776 idLexer::ReadPunctuation
777 ================
778 */
ReadPunctuation(idToken * token)779 int idLexer::ReadPunctuation( idToken *token ) {
780 	int l, n, i;
781 	const char *p;
782 	const punctuation_t *punc;
783 
784 #ifdef PUNCTABLE
785 	for (n = idLexer::punctuationtable[(unsigned int)*(idLexer::script_p)]; n >= 0; n = idLexer::nextpunctuation[n])
786 	{
787 		punc = &(idLexer::punctuations[n]);
788 #else
789 	int i;
790 
791 	for (i = 0; idLexer::punctuations[i].p; i++) {
792 		punc = &idLexer::punctuations[i];
793 #endif
794 		p = punc->p;
795 		// check for this punctuation in the script
796 		for ( l = 0; p[l] && idLexer::script_p[l]; l++ ) {
797 			if ( idLexer::script_p[l] != p[l] ) {
798 				break;
799 			}
800 		}
801 		if ( !p[l] ) {
802 			//
803 			token->EnsureAlloced( l+1, false );
804 			for ( i = 0; i <= l; i++ ) {
805 				token->data[i] = p[i];
806 			}
807 			token->len = l;
808 			//
809 			idLexer::script_p += l;
810 			token->type = TT_PUNCTUATION;
811 			// sub type is the punctuation id
812 			token->subtype = punc->n;
813 			return 1;
814 		}
815 	}
816 	return 0;
817 }
818 
819 /*
820 ================
821 idLexer::ReadToken
822 ================
823 */
824 int idLexer::ReadToken( idToken *token ) {
825 	int c;
826 
827 	if ( !loaded ) {
828 		idLib::common->Error( "idLexer::ReadToken: no file loaded" );
829 		return 0;
830 	}
831 
832 	// if there is a token available (from unreadToken)
833 	if ( tokenavailable ) {
834 		tokenavailable = 0;
835 		*token = idLexer::token;
836 		return 1;
837 	}
838 	// save script pointer
839 	lastScript_p = script_p;
840 	// save line counter
841 	lastline = line;
842 	// clear the token stuff
843 	token->data[0] = '\0';
844 	token->len = 0;
845 	// start of the white space
846 	whiteSpaceStart_p = script_p;
847 	token->whiteSpaceStart_p = script_p;
848 	// read white space before token
849 	if ( !ReadWhiteSpace() ) {
850 		return 0;
851 	}
852 	// end of the white space
853 	idLexer::whiteSpaceEnd_p = script_p;
854 	token->whiteSpaceEnd_p = script_p;
855 	// line the token is on
856 	token->line = line;
857 	// number of lines crossed before token
858 	token->linesCrossed = line - lastline;
859 	// clear token flags
860 	token->flags = 0;
861 
862 	c = *idLexer::script_p;
863 
864 	// if we're keeping everything as whitespace deliminated strings
865 	if ( idLexer::flags & LEXFL_ONLYSTRINGS ) {
866 		// if there is a leading quote
867 		if ( c == '\"' || c == '\'' ) {
868 			if (!idLexer::ReadString( token, c )) {
869 				return 0;
870 			}
871 		} else if ( !idLexer::ReadName( token ) ) {
872 			return 0;
873 		}
874 	}
875 	// if there is a number
876 	else if ( (c >= '0' && c <= '9') ||
877 			(c == '.' && (*(idLexer::script_p + 1) >= '0' && *(idLexer::script_p + 1) <= '9')) ) {
878 		if ( !idLexer::ReadNumber( token ) ) {
879 			return 0;
880 		}
881 		// if names are allowed to start with a number
882 		if ( idLexer::flags & LEXFL_ALLOWNUMBERNAMES ) {
883 			c = *idLexer::script_p;
884 			if ( (c >= 'a' && c <= 'z') ||	(c >= 'A' && c <= 'Z') || c == '_' ) {
885 				if ( !idLexer::ReadName( token ) ) {
886 					return 0;
887 				}
888 			}
889 		}
890 	}
891 	// if there is a leading quote
892 	else if ( c == '\"' || c == '\'' ) {
893 		if (!idLexer::ReadString( token, c )) {
894 			return 0;
895 		}
896 	}
897 	// if there is a name
898 	else if ( (c >= 'a' && c <= 'z') ||	(c >= 'A' && c <= 'Z') || c == '_' ) {
899 		if ( !idLexer::ReadName( token ) ) {
900 			return 0;
901 		}
902 	}
903 	// names may also start with a slash when pathnames are allowed
904 	else if ( ( idLexer::flags & LEXFL_ALLOWPATHNAMES ) && ( (c == '/' || c == '\\') || c == '.' ) ) {
905 		if ( !idLexer::ReadName( token ) ) {
906 			return 0;
907 		}
908 	}
909 	// check for punctuations
910 	else if ( !idLexer::ReadPunctuation( token ) ) {
911 		idLexer::Error( "unknown punctuation %c", c );
912 		return 0;
913 	}
914 	// succesfully read a token
915 	return 1;
916 }
917 
918 /*
919 ================
920 idLexer::ExpectTokenString
921 ================
922 */
923 int idLexer::ExpectTokenString( const char *string ) {
924 	idToken token;
925 
926 	if (!idLexer::ReadToken( &token )) {
927 		idLexer::Error( "couldn't find expected '%s'", string );
928 		return 0;
929 	}
930 	if ( token != string ) {
931 		idLexer::Error( "expected '%s' but found '%s'", string, token.c_str() );
932 		return 0;
933 	}
934 	return 1;
935 }
936 
937 /*
938 ================
939 idLexer::ExpectTokenType
940 ================
941 */
942 int idLexer::ExpectTokenType( int type, int subtype, idToken *token ) {
943 	idStr str;
944 
945 	if ( !idLexer::ReadToken( token ) ) {
946 		idLexer::Error( "couldn't read expected token" );
947 		return 0;
948 	}
949 
950 	if ( token->type != type ) {
951 		switch( type ) {
952 			case TT_STRING: str = "string"; break;
953 			case TT_LITERAL: str = "literal"; break;
954 			case TT_NUMBER: str = "number"; break;
955 			case TT_NAME: str = "name"; break;
956 			case TT_PUNCTUATION: str = "punctuation"; break;
957 			default: str = "unknown type"; break;
958 		}
959 		idLexer::Error( "expected a %s but found '%s'", str.c_str(), token->c_str() );
960 		return 0;
961 	}
962 	if ( token->type == TT_NUMBER ) {
963 		if ( (token->subtype & subtype) != subtype ) {
964 			str.Clear();
965 			if ( subtype & TT_DECIMAL ) str = "decimal ";
966 			if ( subtype & TT_HEX ) str = "hex ";
967 			if ( subtype & TT_OCTAL ) str = "octal ";
968 			if ( subtype & TT_BINARY ) str = "binary ";
969 			if ( subtype & TT_UNSIGNED ) str += "unsigned ";
970 			if ( subtype & TT_LONG ) str += "long ";
971 			if ( subtype & TT_FLOAT ) str += "float ";
972 			if ( subtype & TT_INTEGER ) str += "integer ";
973 			str.StripTrailing( ' ' );
974 			idLexer::Error( "expected %s but found '%s'", str.c_str(), token->c_str() );
975 			return 0;
976 		}
977 	}
978 	else if ( token->type == TT_PUNCTUATION ) {
979 		if ( subtype < 0 ) {
980 			idLexer::Error( "BUG: wrong punctuation subtype" );
981 			return 0;
982 		}
983 		if ( token->subtype != subtype ) {
984 			idLexer::Error( "expected '%s' but found '%s'", GetPunctuationFromId( subtype ), token->c_str() );
985 			return 0;
986 		}
987 	}
988 	return 1;
989 }
990 
991 /*
992 ================
993 idLexer::ExpectAnyToken
994 ================
995 */
996 int idLexer::ExpectAnyToken( idToken *token ) {
997 	if (!idLexer::ReadToken( token )) {
998 		idLexer::Error( "couldn't read expected token" );
999 		return 0;
1000 	}
1001 	else {
1002 		return 1;
1003 	}
1004 }
1005 
1006 /*
1007 ================
1008 idLexer::CheckTokenString
1009 ================
1010 */
1011 int idLexer::CheckTokenString( const char *string ) {
1012 	idToken tok;
1013 
1014 	if ( !ReadToken( &tok ) ) {
1015 		return 0;
1016 	}
1017 	// if the given string is available
1018 	if ( tok == string ) {
1019 		return 1;
1020 	}
1021 	// unread token
1022 	script_p = lastScript_p;
1023 	line = lastline;
1024 	return 0;
1025 }
1026 
1027 /*
1028 ================
1029 idLexer::CheckTokenType
1030 ================
1031 */
1032 int idLexer::CheckTokenType( int type, int subtype, idToken *token ) {
1033 	idToken tok;
1034 
1035 	if ( !ReadToken( &tok ) ) {
1036 		return 0;
1037 	}
1038 	// if the type matches
1039 	if (tok.type == type && (tok.subtype & subtype) == subtype) {
1040 		*token = tok;
1041 		return 1;
1042 	}
1043 	// unread token
1044 	script_p = lastScript_p;
1045 	line = lastline;
1046 	return 0;
1047 }
1048 
1049 /*
1050 ================
1051 idLexer::PeekTokenString
1052 ================
1053 */
1054 int idLexer::PeekTokenString( const char *string ) {
1055 	idToken tok;
1056 
1057 	if ( !ReadToken( &tok ) ) {
1058 		return 0;
1059 	}
1060 
1061 	// unread token
1062 	script_p = lastScript_p;
1063 	line = lastline;
1064 
1065 	// if the given string is available
1066 	if ( tok == string ) {
1067 		return 1;
1068 	}
1069 	return 0;
1070 }
1071 
1072 /*
1073 ================
1074 idLexer::PeekTokenType
1075 ================
1076 */
1077 int idLexer::PeekTokenType( int type, int subtype, idToken *token ) {
1078 	idToken tok;
1079 
1080 	if ( !ReadToken( &tok ) ) {
1081 		return 0;
1082 	}
1083 
1084 	// unread token
1085 	script_p = lastScript_p;
1086 	line = lastline;
1087 
1088 	// if the type matches
1089 	if ( tok.type == type && ( tok.subtype & subtype ) == subtype ) {
1090 		*token = tok;
1091 		return 1;
1092 	}
1093 	return 0;
1094 }
1095 
1096 /*
1097 ================
1098 idLexer::SkipUntilString
1099 ================
1100 */
1101 int idLexer::SkipUntilString( const char *string ) {
1102 	idToken token;
1103 
1104 	while(idLexer::ReadToken( &token )) {
1105 		if ( token == string ) {
1106 			return 1;
1107 		}
1108 	}
1109 	return 0;
1110 }
1111 
1112 /*
1113 ================
1114 idLexer::SkipRestOfLine
1115 ================
1116 */
1117 int idLexer::SkipRestOfLine( void ) {
1118 	idToken token;
1119 
1120 	while(idLexer::ReadToken( &token )) {
1121 		if ( token.linesCrossed ) {
1122 			idLexer::script_p = lastScript_p;
1123 			idLexer::line = lastline;
1124 			return 1;
1125 		}
1126 	}
1127 	return 0;
1128 }
1129 
1130 /*
1131 =================
1132 idLexer::SkipBracedSection
1133 
1134 Skips until a matching close brace is found.
1135 Internal brace depths are properly skipped.
1136 =================
1137 */
1138 int idLexer::SkipBracedSection( bool parseFirstBrace ) {
1139 	idToken token;
1140 	int depth;
1141 
1142 	depth = parseFirstBrace ? 0 : 1;
1143 	do {
1144 		if ( !ReadToken( &token ) ) {
1145 			return false;
1146 		}
1147 		if ( token.type == TT_PUNCTUATION ) {
1148 			if ( token == "{" ) {
1149 				depth++;
1150 			} else if ( token == "}" ) {
1151 				depth--;
1152 			}
1153 		}
1154 	} while( depth );
1155 	return true;
1156 }
1157 
1158 /*
1159 ================
1160 idLexer::UnreadToken
1161 ================
1162 */
1163 void idLexer::UnreadToken( const idToken *token ) {
1164 	if ( idLexer::tokenavailable ) {
1165 		idLib::common->FatalError( "idLexer::unreadToken, unread token twice\n" );
1166 	}
1167 	idLexer::token = *token;
1168 	idLexer::tokenavailable = 1;
1169 }
1170 
1171 /*
1172 ================
1173 idLexer::ReadTokenOnLine
1174 ================
1175 */
1176 int idLexer::ReadTokenOnLine( idToken *token ) {
1177 	idToken tok;
1178 
1179 	if (!idLexer::ReadToken( &tok )) {
1180 		idLexer::script_p = lastScript_p;
1181 		idLexer::line = lastline;
1182 		return false;
1183 	}
1184 	// if no lines were crossed before this token
1185 	if ( !tok.linesCrossed ) {
1186 		*token = tok;
1187 		return true;
1188 	}
1189 	// restore our position
1190 	idLexer::script_p = lastScript_p;
1191 	idLexer::line = lastline;
1192 	token->Clear();
1193 	return false;
1194 }
1195 
1196 /*
1197 ================
1198 idLexer::ReadRestOfLine
1199 ================
1200 */
1201 const char*	idLexer::ReadRestOfLine(idStr& out) {
1202 	while(1) {
1203 
1204 		if(*idLexer::script_p == '\n') {
1205 			idLexer::line++;
1206 			break;
1207 		}
1208 
1209 		if(!*idLexer::script_p) {
1210 			break;
1211 		}
1212 
1213 		if(*idLexer::script_p <= ' ') {
1214 			out += " ";
1215 		} else {
1216 			out += *idLexer::script_p;
1217 		}
1218 		idLexer::script_p++;
1219 
1220 	}
1221 
1222 	out.Strip(' ');
1223 	return out.c_str();
1224 }
1225 
1226 /*
1227 ================
1228 idLexer::ParseInt
1229 ================
1230 */
1231 int idLexer::ParseInt( void ) {
1232 	idToken token;
1233 
1234 	if ( !idLexer::ReadToken( &token ) ) {
1235 		idLexer::Error( "couldn't read expected integer" );
1236 		return 0;
1237 	}
1238 	if ( token.type == TT_PUNCTUATION && token == "-" ) {
1239 		idLexer::ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
1240 		return -((signed int) token.GetIntValue());
1241 	}
1242 	else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
1243 		idLexer::Error( "expected integer value, found '%s'", token.c_str() );
1244 	}
1245 	return token.GetIntValue();
1246 }
1247 
1248 /*
1249 ================
1250 idLexer::ParseBool
1251 ================
1252 */
1253 bool idLexer::ParseBool( void ) {
1254 	idToken token;
1255 
1256 	if ( !idLexer::ExpectTokenType( TT_NUMBER, 0, &token ) ) {
1257 		idLexer::Error( "couldn't read expected boolean" );
1258 		return false;
1259 	}
1260 	return ( token.GetIntValue() != 0 );
1261 }
1262 
1263 /*
1264 ================
1265 idLexer::ParseFloat
1266 ================
1267 */
1268 float idLexer::ParseFloat( bool *errorFlag ) {
1269 	idToken token;
1270 
1271 	if ( errorFlag ) {
1272 		*errorFlag = false;
1273 	}
1274 
1275 	if ( !idLexer::ReadToken( &token ) ) {
1276 		if ( errorFlag ) {
1277 			idLexer::Warning( "couldn't read expected floating point number" );
1278 			*errorFlag = true;
1279 		} else {
1280 			idLexer::Error( "couldn't read expected floating point number" );
1281 		}
1282 		return 0;
1283 	}
1284 	if ( token.type == TT_PUNCTUATION && token == "-" ) {
1285 		idLexer::ExpectTokenType( TT_NUMBER, 0, &token );
1286 		return -token.GetFloatValue();
1287 	}
1288 	else if ( token.type != TT_NUMBER ) {
1289 		if ( errorFlag ) {
1290 			idLexer::Warning( "expected float value, found '%s'", token.c_str() );
1291 			*errorFlag = true;
1292 		} else {
1293 			idLexer::Error( "expected float value, found '%s'", token.c_str() );
1294 		}
1295 	}
1296 	return token.GetFloatValue();
1297 }
1298 
1299 /*
1300 ================
1301 idLexer::Parse1DMatrix
1302 ================
1303 */
1304 int idLexer::Parse1DMatrix( int x, float *m ) {
1305 	int i;
1306 
1307 	if ( !idLexer::ExpectTokenString( "(" ) ) {
1308 		return false;
1309 	}
1310 
1311 	for ( i = 0; i < x; i++ ) {
1312 		m[i] = idLexer::ParseFloat();
1313 	}
1314 
1315 	if ( !idLexer::ExpectTokenString( ")" ) ) {
1316 		return false;
1317 	}
1318 	return true;
1319 }
1320 
1321 /*
1322 ================
1323 idLexer::Parse2DMatrix
1324 ================
1325 */
1326 int idLexer::Parse2DMatrix( int y, int x, float *m ) {
1327 	int i;
1328 
1329 	if ( !idLexer::ExpectTokenString( "(" ) ) {
1330 		return false;
1331 	}
1332 
1333 	for ( i = 0; i < y; i++ ) {
1334 		if ( !idLexer::Parse1DMatrix( x, m + i * x ) ) {
1335 			return false;
1336 		}
1337 	}
1338 
1339 	if ( !idLexer::ExpectTokenString( ")" ) ) {
1340 		return false;
1341 	}
1342 	return true;
1343 }
1344 
1345 /*
1346 ================
1347 idLexer::Parse3DMatrix
1348 ================
1349 */
1350 int idLexer::Parse3DMatrix( int z, int y, int x, float *m ) {
1351 	int i;
1352 
1353 	if ( !idLexer::ExpectTokenString( "(" ) ) {
1354 		return false;
1355 	}
1356 
1357 	for ( i = 0 ; i < z; i++ ) {
1358 		if ( !idLexer::Parse2DMatrix( y, x, m + i * x*y ) ) {
1359 			return false;
1360 		}
1361 	}
1362 
1363 	if ( !idLexer::ExpectTokenString( ")" ) ) {
1364 		return false;
1365 	}
1366 	return true;
1367 }
1368 
1369 /*
1370 =================
1371 idParser::ParseBracedSection
1372 
1373 The next token should be an open brace.
1374 Parses until a matching close brace is found.
1375 Maintains exact characters between braces.
1376 
1377   FIXME: this should use ReadToken and replace the token white space with correct indents and newlines
1378 =================
1379 */
1380 const char *idLexer::ParseBracedSectionExact( idStr &out, int tabs ) {
1381 	int		depth;
1382 	bool	doTabs;
1383 	bool	skipWhite;
1384 
1385 	out.Empty();
1386 
1387 	if ( !idLexer::ExpectTokenString( "{" ) ) {
1388 		return out.c_str( );
1389 	}
1390 
1391 	out = "{";
1392 	depth = 1;
1393 	skipWhite = false;
1394 	doTabs = tabs >= 0;
1395 
1396 	while( depth && *idLexer::script_p ) {
1397 		char c = *(idLexer::script_p++);
1398 
1399 		switch ( c ) {
1400 			case '\t':
1401 			case ' ': {
1402 				if ( skipWhite ) {
1403 					continue;
1404 				}
1405 				break;
1406 			}
1407 			case '\n': {
1408 				if ( doTabs ) {
1409 					skipWhite = true;
1410 					out += c;
1411 					continue;
1412 				}
1413 				break;
1414 			}
1415 			case '{': {
1416 				depth++;
1417 				tabs++;
1418 				break;
1419 			}
1420 			case '}': {
1421 				depth--;
1422 				tabs--;
1423 				break;
1424 			}
1425 		}
1426 
1427 		if ( skipWhite ) {
1428 			int i = tabs;
1429 			if ( c == '{' ) {
1430 				i--;
1431 			}
1432 			skipWhite = false;
1433 			for ( ; i > 0; i-- ) {
1434 				out += '\t';
1435 			}
1436 		}
1437 		out += c;
1438 	}
1439 	return out.c_str();
1440 }
1441 
1442 /*
1443 =================
1444 idLexer::ParseBracedSection
1445 
1446 The next token should be an open brace.
1447 Parses until a matching close brace is found.
1448 Internal brace depths are properly skipped.
1449 =================
1450 */
1451 const char *idLexer::ParseBracedSection( idStr &out ) {
1452 	idToken token;
1453 	int i, depth;
1454 
1455 	out.Empty();
1456 	if ( !idLexer::ExpectTokenString( "{" ) ) {
1457 		return out.c_str();
1458 	}
1459 	out = "{";
1460 	depth = 1;
1461 	do {
1462 		if ( !idLexer::ReadToken( &token ) ) {
1463 			Error( "missing closing brace" );
1464 			return out.c_str();
1465 		}
1466 
1467 		// if the token is on a new line
1468 		for ( i = 0; i < token.linesCrossed; i++ ) {
1469 			out += "\r\n";
1470 		}
1471 
1472 		if ( token.type == TT_PUNCTUATION ) {
1473 			if ( token[0] == '{' ) {
1474 				depth++;
1475 			}
1476 			else if ( token[0] == '}' ) {
1477 				depth--;
1478 			}
1479 		}
1480 
1481 		if ( token.type == TT_STRING ) {
1482 			out += "\"" + token + "\"";
1483 		}
1484 		else {
1485 			out += token;
1486 		}
1487 		out += " ";
1488 	} while( depth );
1489 
1490 	return out.c_str();
1491 }
1492 
1493 /*
1494 =================
1495 idLexer::ParseRestOfLine
1496 
1497   parse the rest of the line
1498 =================
1499 */
1500 const char *idLexer::ParseRestOfLine( idStr &out ) {
1501 	idToken token;
1502 
1503 	out.Empty();
1504 	while(idLexer::ReadToken( &token )) {
1505 		if ( token.linesCrossed ) {
1506 			idLexer::script_p = lastScript_p;
1507 			idLexer::line = lastline;
1508 			break;
1509 		}
1510 		if ( out.Length() ) {
1511 			out += " ";
1512 		}
1513 		out += token;
1514 	}
1515 	return out.c_str();
1516 }
1517 
1518 /*
1519 ================
1520 idLexer::GetLastWhiteSpace
1521 ================
1522 */
1523 int idLexer::GetLastWhiteSpace( idStr &whiteSpace ) const {
1524 	whiteSpace.Clear();
1525 	for ( const char *p = whiteSpaceStart_p; p < whiteSpaceEnd_p; p++ ) {
1526 		whiteSpace.Append( *p );
1527 	}
1528 	return whiteSpace.Length();
1529 }
1530 
1531 /*
1532 ================
1533 idLexer::GetLastWhiteSpaceStart
1534 ================
1535 */
1536 int idLexer::GetLastWhiteSpaceStart( void ) const {
1537 	return whiteSpaceStart_p - buffer;
1538 }
1539 
1540 /*
1541 ================
1542 idLexer::GetLastWhiteSpaceEnd
1543 ================
1544 */
1545 int idLexer::GetLastWhiteSpaceEnd( void ) const {
1546 	return whiteSpaceEnd_p - buffer;
1547 }
1548 
1549 /*
1550 ================
1551 idLexer::Reset
1552 ================
1553 */
1554 void idLexer::Reset( void ) {
1555 	// pointer in script buffer
1556 	idLexer::script_p = idLexer::buffer;
1557 	// pointer in script buffer before reading token
1558 	idLexer::lastScript_p = idLexer::buffer;
1559 	// begin of white space
1560 	idLexer::whiteSpaceStart_p = NULL;
1561 	// end of white space
1562 	idLexer::whiteSpaceEnd_p = NULL;
1563 	// set if there's a token available in idLexer::token
1564 	idLexer::tokenavailable = 0;
1565 
1566 	idLexer::line = 1;
1567 	idLexer::lastline = 1;
1568 	// clear the saved token
1569 	idLexer::token = "";
1570 }
1571 
1572 /*
1573 ================
1574 idLexer::EndOfFile
1575 ================
1576 */
1577 int idLexer::EndOfFile( void ) {
1578 	return idLexer::script_p >= idLexer::end_p;
1579 }
1580 
1581 /*
1582 ================
1583 idLexer::NumLinesCrossed
1584 ================
1585 */
1586 int idLexer::NumLinesCrossed( void ) {
1587 	return idLexer::line - idLexer::lastline;
1588 }
1589 
1590 /*
1591 ================
1592 idLexer::LoadFile
1593 ================
1594 */
1595 int idLexer::LoadFile( const char *filename, bool OSPath ) {
1596 	idFile *fp;
1597 	idStr pathname;
1598 	int length;
1599 	char *buf;
1600 
1601 	if ( idLexer::loaded ) {
1602 		idLib::common->Error("idLexer::LoadFile: another script already loaded");
1603 		return false;
1604 	}
1605 
1606 	if ( !OSPath && ( baseFolder[0] != '\0' ) ) {
1607 		pathname = va( "%s/%s", baseFolder, filename );
1608 	} else {
1609 		pathname = filename;
1610 	}
1611 	if ( OSPath ) {
1612 		fp = idLib::fileSystem->OpenExplicitFileRead( pathname );
1613 	} else {
1614 		fp = idLib::fileSystem->OpenFileRead( pathname );
1615 	}
1616 	if ( !fp ) {
1617 		return false;
1618 	}
1619 	length = fp->Length();
1620 	buf = (char *) Mem_Alloc( length + 1 );
1621 	buf[length] = '\0';
1622 	fp->Read( buf, length );
1623 	idLexer::fileTime = fp->Timestamp();
1624 	idLexer::filename = fp->GetFullPath();
1625 	idLib::fileSystem->CloseFile( fp );
1626 
1627 	idLexer::buffer = buf;
1628 	idLexer::length = length;
1629 	// pointer in script buffer
1630 	idLexer::script_p = idLexer::buffer;
1631 	// pointer in script buffer before reading token
1632 	idLexer::lastScript_p = idLexer::buffer;
1633 	// pointer to end of script buffer
1634 	idLexer::end_p = &(idLexer::buffer[length]);
1635 
1636 	idLexer::tokenavailable = 0;
1637 	idLexer::line = 1;
1638 	idLexer::lastline = 1;
1639 	idLexer::allocated = true;
1640 	idLexer::loaded = true;
1641 
1642 	return true;
1643 }
1644 
1645 /*
1646 ================
1647 idLexer::LoadMemory
1648 ================
1649 */
1650 int idLexer::LoadMemory( const char *ptr, int length, const char *name, int startLine ) {
1651 	if ( idLexer::loaded ) {
1652 		idLib::common->Error("idLexer::LoadMemory: another script already loaded");
1653 		return false;
1654 	}
1655 	idLexer::filename = name;
1656 	idLexer::buffer = ptr;
1657 	idLexer::fileTime = 0;
1658 	idLexer::length = length;
1659 	// pointer in script buffer
1660 	idLexer::script_p = idLexer::buffer;
1661 	// pointer in script buffer before reading token
1662 	idLexer::lastScript_p = idLexer::buffer;
1663 	// pointer to end of script buffer
1664 	idLexer::end_p = &(idLexer::buffer[length]);
1665 
1666 	idLexer::tokenavailable = 0;
1667 	idLexer::line = startLine;
1668 	idLexer::lastline = startLine;
1669 	idLexer::allocated = false;
1670 	idLexer::loaded = true;
1671 
1672 	return true;
1673 }
1674 
1675 /*
1676 ================
1677 idLexer::FreeSource
1678 ================
1679 */
1680 void idLexer::FreeSource( void ) {
1681 #ifdef PUNCTABLE
1682 	if ( idLexer::punctuationtable && idLexer::punctuationtable != default_punctuationtable ) {
1683 		Mem_Free( (void *) idLexer::punctuationtable );
1684 		idLexer::punctuationtable = NULL;
1685 	}
1686 	if ( idLexer::nextpunctuation && idLexer::nextpunctuation != default_nextpunctuation ) {
1687 		Mem_Free( (void *) idLexer::nextpunctuation );
1688 		idLexer::nextpunctuation = NULL;
1689 	}
1690 #endif //PUNCTABLE
1691 	if ( idLexer::allocated ) {
1692 		Mem_Free( (void *) idLexer::buffer );
1693 		idLexer::buffer = NULL;
1694 		idLexer::allocated = false;
1695 	}
1696 	idLexer::tokenavailable = 0;
1697 	idLexer::token = "";
1698 	idLexer::loaded = false;
1699 }
1700 
1701 /*
1702 ================
1703 idLexer::idLexer
1704 ================
1705 */
1706 idLexer::idLexer( void ) {
1707 	idLexer::loaded = false;
1708 	idLexer::filename = "";
1709 	idLexer::flags = 0;
1710 	idLexer::SetPunctuations( NULL );
1711 	idLexer::allocated = false;
1712 	idLexer::fileTime = 0;
1713 	idLexer::length = 0;
1714 	idLexer::line = 0;
1715 	idLexer::lastline = 0;
1716 	idLexer::tokenavailable = 0;
1717 	idLexer::token = "";
1718 	idLexer::next = NULL;
1719 	idLexer::hadError = false;
1720 }
1721 
1722 /*
1723 ================
1724 idLexer::idLexer
1725 ================
1726 */
1727 idLexer::idLexer( int flags ) {
1728 	idLexer::loaded = false;
1729 	idLexer::filename = "";
1730 	idLexer::flags = flags;
1731 	idLexer::SetPunctuations( NULL );
1732 	idLexer::allocated = false;
1733 	idLexer::fileTime = 0;
1734 	idLexer::length = 0;
1735 	idLexer::line = 0;
1736 	idLexer::lastline = 0;
1737 	idLexer::tokenavailable = 0;
1738 	idLexer::token = "";
1739 	idLexer::next = NULL;
1740 	idLexer::hadError = false;
1741 }
1742 
1743 /*
1744 ================
1745 idLexer::idLexer
1746 ================
1747 */
1748 idLexer::idLexer( const char *filename, int flags, bool OSPath ) {
1749 	idLexer::loaded = false;
1750 	idLexer::flags = flags;
1751 	idLexer::SetPunctuations( NULL );
1752 	idLexer::allocated = false;
1753 	idLexer::token = "";
1754 	idLexer::next = NULL;
1755 	idLexer::hadError = false;
1756 	idLexer::LoadFile( filename, OSPath );
1757 }
1758 
1759 /*
1760 ================
1761 idLexer::idLexer
1762 ================
1763 */
1764 idLexer::idLexer( const char *ptr, int length, const char *name, int flags ) {
1765 	idLexer::loaded = false;
1766 	idLexer::flags = flags;
1767 	idLexer::SetPunctuations( NULL );
1768 	idLexer::allocated = false;
1769 	idLexer::token = "";
1770 	idLexer::next = NULL;
1771 	idLexer::hadError = false;
1772 	idLexer::LoadMemory( ptr, length, name );
1773 }
1774 
1775 /*
1776 ================
1777 idLexer::~idLexer
1778 ================
1779 */
1780 idLexer::~idLexer( void ) {
1781 	idLexer::FreeSource();
1782 }
1783 
1784 /*
1785 ================
1786 idLexer::SetBaseFolder
1787 ================
1788 */
1789 void idLexer::SetBaseFolder( const char *path ) {
1790 	idStr::Copynz( baseFolder, path, sizeof( baseFolder ) );
1791 }
1792 
1793 /*
1794 ================
1795 idLexer::HadError
1796 ================
1797 */
1798 bool idLexer::HadError( void ) const {
1799 	return hadError;
1800 }
1801