1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 
5 This file is part of Quake III Arena source code.
6 
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11 
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, 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 Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 
23 /*****************************************************************************
24  * name:		l_script.c
25  *
26  * desc:		lexicographical parser
27  *
28  * $Archive: /MissionPack/code/botlib/l_script.c $
29  *
30  *****************************************************************************/
31 
32 //#define SCREWUP
33 //#define BOTLIB
34 //#define MEQCC
35 //#define BSPC
36 
37 #ifdef SCREWUP
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <limits.h>
41 #include <string.h>
42 #include <stdarg.h>
43 #include "l_memory.h"
44 #include "l_script.h"
45 
46 typedef enum {qfalse, qtrue}	qboolean;
47 
48 #endif //SCREWUP
49 
50 #ifdef BOTLIB
51 //include files for usage in the bot library
52 #include "../qcommon/q_shared.h"
53 #include "botlib.h"
54 #include "be_interface.h"
55 #include "l_script.h"
56 #include "l_memory.h"
57 #include "l_log.h"
58 #include "l_libvar.h"
59 #endif //BOTLIB
60 
61 #ifdef MEQCC
62 //include files for usage in MrElusive's QuakeC Compiler
63 #include "qcc.h"
64 #include "l_script.h"
65 #include "l_memory.h"
66 #include "l_log.h"
67 
68 #define qtrue	true
69 #define qfalse	false
70 #endif //MEQCC
71 
72 #ifdef BSPC
73 //include files for usage in the BSP Converter
74 #include "../bspc/qbsp.h"
75 #include "../bspc/l_log.h"
76 #include "../bspc/l_mem.h"
77 
78 #define qtrue	true
79 #define qfalse	false
80 #endif //BSPC
81 
82 
83 #define PUNCTABLE
84 
85 //longer punctuations first
86 punctuation_t default_punctuations[] =
87 {
88 	//binary operators
89 	{">>=",P_RSHIFT_ASSIGN, NULL},
90 	{"<<=",P_LSHIFT_ASSIGN, NULL},
91 	//
92 	{"...",P_PARMS, NULL},
93 	//define merge operator
94 	{"##",P_PRECOMPMERGE, NULL},
95 	//logic operators
96 	{"&&",P_LOGIC_AND, NULL},
97 	{"||",P_LOGIC_OR, NULL},
98 	{">=",P_LOGIC_GEQ, NULL},
99 	{"<=",P_LOGIC_LEQ, NULL},
100 	{"==",P_LOGIC_EQ, NULL},
101 	{"!=",P_LOGIC_UNEQ, NULL},
102 	//arithmatic operators
103 	{"*=",P_MUL_ASSIGN, NULL},
104 	{"/=",P_DIV_ASSIGN, NULL},
105 	{"%=",P_MOD_ASSIGN, NULL},
106 	{"+=",P_ADD_ASSIGN, NULL},
107 	{"-=",P_SUB_ASSIGN, NULL},
108 	{"++",P_INC, NULL},
109 	{"--",P_DEC, NULL},
110 	//binary operators
111 	{"&=",P_BIN_AND_ASSIGN, NULL},
112 	{"|=",P_BIN_OR_ASSIGN, NULL},
113 	{"^=",P_BIN_XOR_ASSIGN, NULL},
114 	{">>",P_RSHIFT, NULL},
115 	{"<<",P_LSHIFT, NULL},
116 	//reference operators
117 	{"->",P_POINTERREF, NULL},
118 	//C++
119 	{"::",P_CPP1, NULL},
120 	{".*",P_CPP2, NULL},
121 	//arithmatic operators
122 	{"*",P_MUL, NULL},
123 	{"/",P_DIV, NULL},
124 	{"%",P_MOD, NULL},
125 	{"+",P_ADD, NULL},
126 	{"-",P_SUB, NULL},
127 	{"=",P_ASSIGN, NULL},
128 	//binary operators
129 	{"&",P_BIN_AND, NULL},
130 	{"|",P_BIN_OR, NULL},
131 	{"^",P_BIN_XOR, NULL},
132 	{"~",P_BIN_NOT, NULL},
133 	//logic operators
134 	{"!",P_LOGIC_NOT, NULL},
135 	{">",P_LOGIC_GREATER, NULL},
136 	{"<",P_LOGIC_LESS, NULL},
137 	//reference operator
138 	{".",P_REF, NULL},
139 	//seperators
140 	{",",P_COMMA, NULL},
141 	{";",P_SEMICOLON, NULL},
142 	//label indication
143 	{":",P_COLON, NULL},
144 	//if statement
145 	{"?",P_QUESTIONMARK, NULL},
146 	//embracements
147 	{"(",P_PARENTHESESOPEN, NULL},
148 	{")",P_PARENTHESESCLOSE, NULL},
149 	{"{",P_BRACEOPEN, NULL},
150 	{"}",P_BRACECLOSE, NULL},
151 	{"[",P_SQBRACKETOPEN, NULL},
152 	{"]",P_SQBRACKETCLOSE, NULL},
153 	//
154 	{"\\",P_BACKSLASH, NULL},
155 	//precompiler operator
156 	{"#",P_PRECOMP, NULL},
157 #ifdef DOLLAR
158 	{"$",P_DOLLAR, NULL},
159 #endif //DOLLAR
160 	{NULL, 0}
161 };
162 
163 #ifdef BSPC
164 char basefolder[MAX_PATH];
165 #else
166 char basefolder[MAX_QPATH];
167 #endif
168 
169 //===========================================================================
170 //
171 // Parameter:				-
172 // Returns:					-
173 // Changes Globals:		-
174 //===========================================================================
PS_CreatePunctuationTable(script_t * script,punctuation_t * punctuations)175 void PS_CreatePunctuationTable(script_t *script, punctuation_t *punctuations)
176 {
177 	int i;
178 	punctuation_t *p, *lastp, *newp;
179 
180 	//get memory for the table
181 	if (!script->punctuationtable) script->punctuationtable = (punctuation_t **)
182 												GetMemory(256 * sizeof(punctuation_t *));
183 	Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *));
184 	//add the punctuations in the list to the punctuation table
185 	for (i = 0; punctuations[i].p; i++)
186 	{
187 		newp = &punctuations[i];
188 		lastp = NULL;
189 		//sort the punctuations in this table entry on length (longer punctuations first)
190 		for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next)
191 		{
192 			if (strlen(p->p) < strlen(newp->p))
193 			{
194 				newp->next = p;
195 				if (lastp) lastp->next = newp;
196 				else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
197 				break;
198 			} //end if
199 			lastp = p;
200 		} //end for
201 		if (!p)
202 		{
203 			newp->next = NULL;
204 			if (lastp) lastp->next = newp;
205 			else script->punctuationtable[(unsigned int) newp->p[0]] = newp;
206 		} //end if
207 	} //end for
208 } //end of the function PS_CreatePunctuationTable
209 //===========================================================================
210 //
211 // Parameter:				-
212 // Returns:					-
213 // Changes Globals:		-
214 //===========================================================================
PunctuationFromNum(script_t * script,int num)215 char *PunctuationFromNum(script_t *script, int num)
216 {
217 	int i;
218 
219 	for (i = 0; script->punctuations[i].p; i++)
220 	{
221 		if (script->punctuations[i].n == num) return script->punctuations[i].p;
222 	} //end for
223 	return "unkown punctuation";
224 } //end of the function PunctuationFromNum
225 //===========================================================================
226 //
227 // Parameter:				-
228 // Returns:					-
229 // Changes Globals:		-
230 //===========================================================================
ScriptError(script_t * script,char * str,...)231 void QDECL ScriptError(script_t *script, char *str, ...)
232 {
233 	char text[1024];
234 	va_list ap;
235 
236 	if (script->flags & SCFL_NOERRORS) return;
237 
238 	va_start(ap, str);
239 	Q_vsnprintf(text, sizeof(text), str, ap);
240 	va_end(ap);
241 #ifdef BOTLIB
242 	botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text);
243 #endif //BOTLIB
244 #ifdef MEQCC
245 	printf("error: file %s, line %d: %s\n", script->filename, script->line, text);
246 #endif //MEQCC
247 #ifdef BSPC
248 	Log_Print("error: file %s, line %d: %s\n", script->filename, script->line, text);
249 #endif //BSPC
250 } //end of the function ScriptError
251 //===========================================================================
252 //
253 // Parameter:				-
254 // Returns:					-
255 // Changes Globals:		-
256 //===========================================================================
ScriptWarning(script_t * script,char * str,...)257 void QDECL ScriptWarning(script_t *script, char *str, ...)
258 {
259 	char text[1024];
260 	va_list ap;
261 
262 	if (script->flags & SCFL_NOWARNINGS) return;
263 
264 	va_start(ap, str);
265 	Q_vsnprintf(text, sizeof(text), str, ap);
266 	va_end(ap);
267 #ifdef BOTLIB
268 	botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text);
269 #endif //BOTLIB
270 #ifdef MEQCC
271 	printf("warning: file %s, line %d: %s\n", script->filename, script->line, text);
272 #endif //MEQCC
273 #ifdef BSPC
274 	Log_Print("warning: file %s, line %d: %s\n", script->filename, script->line, text);
275 #endif //BSPC
276 } //end of the function ScriptWarning
277 //===========================================================================
278 //
279 // Parameter:				-
280 // Returns:					-
281 // Changes Globals:		-
282 //===========================================================================
SetScriptPunctuations(script_t * script,punctuation_t * p)283 void SetScriptPunctuations(script_t *script, punctuation_t *p)
284 {
285 #ifdef PUNCTABLE
286 	if (p) PS_CreatePunctuationTable(script, p);
287 	else  PS_CreatePunctuationTable(script, default_punctuations);
288 #endif //PUNCTABLE
289 	if (p) script->punctuations = p;
290 	else script->punctuations = default_punctuations;
291 } //end of the function SetScriptPunctuations
292 //============================================================================
293 // Reads spaces, tabs, C-like comments etc.
294 // When a newline character is found the scripts line counter is increased.
295 //
296 // Parameter:				-
297 // Returns:					-
298 // Changes Globals:		-
299 //============================================================================
PS_ReadWhiteSpace(script_t * script)300 int PS_ReadWhiteSpace(script_t *script)
301 {
302 	while(1)
303 	{
304 		//skip white space
305 		while(*script->script_p <= ' ')
306 		{
307 			if (!*script->script_p) return 0;
308 			if (*script->script_p == '\n') script->line++;
309 			script->script_p++;
310 		} //end while
311 		//skip comments
312 		if (*script->script_p == '/')
313 		{
314 			//comments //
315 			if (*(script->script_p+1) == '/')
316 			{
317 				script->script_p++;
318 				do
319 				{
320 					script->script_p++;
321 					if (!*script->script_p) return 0;
322 				} //end do
323 				while(*script->script_p != '\n');
324 				script->line++;
325 				script->script_p++;
326 				if (!*script->script_p) return 0;
327 				continue;
328 			} //end if
329 			//comments /* */
330 			else if (*(script->script_p+1) == '*')
331 			{
332 				script->script_p++;
333 				do
334 				{
335 					script->script_p++;
336 					if (!*script->script_p) return 0;
337 					if (*script->script_p == '\n') script->line++;
338 				} //end do
339 				while(!(*script->script_p == '*' && *(script->script_p+1) == '/'));
340 				script->script_p++;
341 				if (!*script->script_p) return 0;
342 				script->script_p++;
343 				if (!*script->script_p) return 0;
344 				continue;
345 			} //end if
346 		} //end if
347 		break;
348 	} //end while
349 	return 1;
350 } //end of the function PS_ReadWhiteSpace
351 //============================================================================
352 // Reads an escape character.
353 //
354 // Parameter:				script		: script to read from
355 //								ch				: place to store the read escape character
356 // Returns:					-
357 // Changes Globals:		-
358 //============================================================================
PS_ReadEscapeCharacter(script_t * script,char * ch)359 int PS_ReadEscapeCharacter(script_t *script, char *ch)
360 {
361 	int c, val, i;
362 
363 	//step over the leading '\\'
364 	script->script_p++;
365 	//determine the escape character
366 	switch(*script->script_p)
367 	{
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 			script->script_p++;
382 			for (i = 0, val = 0; ; i++, script->script_p++)
383 			{
384 				c = *script->script_p;
385 				if (c >= '0' && c <= '9') c = c - '0';
386 				else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10;
387 				else if (c >= 'a' && c <= 'z') c = c - 'a' + 10;
388 				else break;
389 				val = (val << 4) + c;
390 			} //end for
391 			script->script_p--;
392 			if (val > 0xFF)
393 			{
394 				ScriptWarning(script, "too large value in escape character");
395 				val = 0xFF;
396 			} //end if
397 			c = val;
398 			break;
399 		} //end case
400 		default: //NOTE: decimal ASCII code, NOT octal
401 		{
402 			if (*script->script_p < '0' || *script->script_p > '9') ScriptError(script, "unknown escape char");
403 			for (i = 0, val = 0; ; i++, script->script_p++)
404 			{
405 				c = *script->script_p;
406 				if (c >= '0' && c <= '9') c = c - '0';
407 				else break;
408 				val = val * 10 + c;
409 			} //end for
410 			script->script_p--;
411 			if (val > 0xFF)
412 			{
413 				ScriptWarning(script, "too large value in escape character");
414 				val = 0xFF;
415 			} //end if
416 			c = val;
417 			break;
418 		} //end default
419 	} //end switch
420 	//step over the escape character or the last digit of the number
421 	script->script_p++;
422 	//store the escape character
423 	*ch = c;
424 	//successfully read escape character
425 	return 1;
426 } //end of the function PS_ReadEscapeCharacter
427 //============================================================================
428 // Reads C-like string. Escape characters are interpretted.
429 // Quotes are included with the string.
430 // Reads two strings with a white space between them as one string.
431 //
432 // Parameter:				script		: script to read from
433 //								token			: buffer to store the string
434 // Returns:					qtrue when a string was read successfully
435 // Changes Globals:		-
436 //============================================================================
PS_ReadString(script_t * script,token_t * token,int quote)437 int PS_ReadString(script_t *script, token_t *token, int quote)
438 {
439 	int len, tmpline;
440 	char *tmpscript_p;
441 
442 	if (quote == '\"') token->type = TT_STRING;
443 	else token->type = TT_LITERAL;
444 
445 	len = 0;
446 	//leading quote
447 	token->string[len++] = *script->script_p++;
448 	//
449 	while(1)
450 	{
451 		//minus 2 because trailing double quote and zero have to be appended
452 		if (len >= MAX_TOKEN - 2)
453 		{
454 			ScriptError(script, "string longer than MAX_TOKEN = %d", MAX_TOKEN);
455 			return 0;
456 		} //end if
457 		//if there is an escape character and
458 		//if escape characters inside a string are allowed
459 		if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS))
460 		{
461 			if (!PS_ReadEscapeCharacter(script, &token->string[len]))
462 			{
463 				token->string[len] = 0;
464 				return 0;
465 			} //end if
466 			len++;
467 		} //end if
468 		//if a trailing quote
469 		else if (*script->script_p == quote)
470 		{
471 			//step over the double quote
472 			script->script_p++;
473 			//if white spaces in a string are not allowed
474 			if (script->flags & SCFL_NOSTRINGWHITESPACES) break;
475 			//
476 			tmpscript_p = script->script_p;
477 			tmpline = script->line;
478 			//read unusefull stuff between possible two following strings
479 			if (!PS_ReadWhiteSpace(script))
480 			{
481 				script->script_p = tmpscript_p;
482 				script->line = tmpline;
483 				break;
484 			} //end if
485 			//if there's no leading double qoute
486 			if (*script->script_p != quote)
487 			{
488 				script->script_p = tmpscript_p;
489 				script->line = tmpline;
490 				break;
491 			} //end if
492 			//step over the new leading double quote
493 			script->script_p++;
494 		} //end if
495 		else
496 		{
497 			if (*script->script_p == '\0')
498 			{
499 				token->string[len] = 0;
500 				ScriptError(script, "missing trailing quote");
501 				return 0;
502 			} //end if
503 	      if (*script->script_p == '\n')
504 			{
505 				token->string[len] = 0;
506 				ScriptError(script, "newline inside string %s", token->string);
507 				return 0;
508 			} //end if
509 			token->string[len++] = *script->script_p++;
510 		} //end else
511 	} //end while
512 	//trailing quote
513 	token->string[len++] = quote;
514 	//end string with a zero
515 	token->string[len] = '\0';
516 	//the sub type is the length of the string
517 	token->subtype = len;
518 	return 1;
519 } //end of the function PS_ReadString
520 //============================================================================
521 //
522 // Parameter:				-
523 // Returns:					-
524 // Changes Globals:		-
525 //============================================================================
PS_ReadName(script_t * script,token_t * token)526 int PS_ReadName(script_t *script, token_t *token)
527 {
528 	int len = 0;
529 	char c;
530 
531 	token->type = TT_NAME;
532 	do
533 	{
534 		token->string[len++] = *script->script_p++;
535 		if (len >= MAX_TOKEN)
536 		{
537 			ScriptError(script, "name longer than MAX_TOKEN = %d", MAX_TOKEN);
538 			return 0;
539 		} //end if
540 		c = *script->script_p;
541    } while ((c >= 'a' && c <= 'z') ||
542 				(c >= 'A' && c <= 'Z') ||
543 				(c >= '0' && c <= '9') ||
544 				c == '_');
545 	token->string[len] = '\0';
546 	//the sub type is the length of the name
547 	token->subtype = len;
548 	return 1;
549 } //end of the function PS_ReadName
550 //============================================================================
551 //
552 // Parameter:				-
553 // Returns:					-
554 // Changes Globals:		-
555 //============================================================================
NumberValue(char * string,int subtype,unsigned long int * intvalue,float * floatvalue)556 void NumberValue(char *string, int subtype, unsigned long int *intvalue,
557 															float *floatvalue)
558 {
559 	unsigned long int dotfound = 0;
560 
561 	*intvalue = 0;
562 	*floatvalue = 0;
563 	//floating point number
564 	if (subtype & TT_FLOAT)
565 	{
566 		while(*string)
567 		{
568 			if (*string == '.')
569 			{
570 				if (dotfound) return;
571 				dotfound = 10;
572 				string++;
573 			} //end if
574 			if (dotfound)
575 			{
576 				*floatvalue = *floatvalue + (float) (*string - '0') /
577 																	(float) dotfound;
578 				dotfound *= 10;
579 			} //end if
580 			else
581 			{
582 				*floatvalue = *floatvalue * 10.0 + (float) (*string - '0');
583 			} //end else
584 			string++;
585 		} //end while
586 		*intvalue = (unsigned long) *floatvalue;
587 	} //end if
588 	else if (subtype & TT_DECIMAL)
589 	{
590 		while(*string) *intvalue = *intvalue * 10 + (*string++ - '0');
591 		*floatvalue = *intvalue;
592 	} //end else if
593 	else if (subtype & TT_HEX)
594 	{
595 		//step over the leading 0x or 0X
596 		string += 2;
597 		while(*string)
598 		{
599 			*intvalue <<= 4;
600 			if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10;
601 			else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10;
602 			else *intvalue += *string - '0';
603 			string++;
604 		} //end while
605 		*floatvalue = *intvalue;
606 	} //end else if
607 	else if (subtype & TT_OCTAL)
608 	{
609 		//step over the first zero
610 		string += 1;
611 		while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0');
612 		*floatvalue = *intvalue;
613 	} //end else if
614 	else if (subtype & TT_BINARY)
615 	{
616 		//step over the leading 0b or 0B
617 		string += 2;
618 		while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0');
619 		*floatvalue = *intvalue;
620 	} //end else if
621 } //end of the function NumberValue
622 //============================================================================
623 //
624 // Parameter:				-
625 // Returns:					-
626 // Changes Globals:		-
627 //============================================================================
PS_ReadNumber(script_t * script,token_t * token)628 int PS_ReadNumber(script_t *script, token_t *token)
629 {
630 	int len = 0, i;
631 	int octal, dot;
632 	char c;
633 //	unsigned long int intvalue = 0;
634 //	double floatvalue = 0;
635 
636 	token->type = TT_NUMBER;
637 	//check for a hexadecimal number
638 	if (*script->script_p == '0' &&
639 		(*(script->script_p + 1) == 'x' ||
640 		*(script->script_p + 1) == 'X'))
641 	{
642 		token->string[len++] = *script->script_p++;
643 		token->string[len++] = *script->script_p++;
644 		c = *script->script_p;
645 		//hexadecimal
646 		while((c >= '0' && c <= '9') ||
647 					(c >= 'a' && c <= 'f') ||
648 					(c >= 'A' && c <= 'A'))
649 		{
650 			token->string[len++] = *script->script_p++;
651 			if (len >= MAX_TOKEN)
652 			{
653 				ScriptError(script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN);
654 				return 0;
655 			} //end if
656 			c = *script->script_p;
657 		} //end while
658 		token->subtype |= TT_HEX;
659 	} //end if
660 #ifdef BINARYNUMBERS
661 	//check for a binary number
662 	else if (*script->script_p == '0' &&
663 		(*(script->script_p + 1) == 'b' ||
664 		*(script->script_p + 1) == 'B'))
665 	{
666 		token->string[len++] = *script->script_p++;
667 		token->string[len++] = *script->script_p++;
668 		c = *script->script_p;
669 		//binary
670 		while(c == '0' || c == '1')
671 		{
672 			token->string[len++] = *script->script_p++;
673 			if (len >= MAX_TOKEN)
674 			{
675 				ScriptError(script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN);
676 				return 0;
677 			} //end if
678 			c = *script->script_p;
679 		} //end while
680 		token->subtype |= TT_BINARY;
681 	} //end if
682 #endif //BINARYNUMBERS
683 	else //decimal or octal integer or floating point number
684 	{
685 		octal = qfalse;
686 		dot = qfalse;
687 		if (*script->script_p == '0') octal = qtrue;
688 		while(1)
689 		{
690 			c = *script->script_p;
691 			if (c == '.') dot = qtrue;
692 			else if (c == '8' || c == '9') octal = qfalse;
693 			else if (c < '0' || c > '9') break;
694 			token->string[len++] = *script->script_p++;
695 			if (len >= MAX_TOKEN - 1)
696 			{
697 				ScriptError(script, "number longer than MAX_TOKEN = %d", MAX_TOKEN);
698 				return 0;
699 			} //end if
700 		} //end while
701 		if (octal) token->subtype |= TT_OCTAL;
702 		else token->subtype |= TT_DECIMAL;
703 		if (dot) token->subtype |= TT_FLOAT;
704 	} //end else
705 	for (i = 0; i < 2; i++)
706 	{
707 		c = *script->script_p;
708 		//check for a LONG number
709 		if ( (c == 'l' || c == 'L')
710 		     && !(token->subtype & TT_LONG))
711 		{
712 			script->script_p++;
713 			token->subtype |= TT_LONG;
714 		} //end if
715 		//check for an UNSIGNED number
716 		else if ( (c == 'u' || c == 'U')
717 			  && !(token->subtype & (TT_UNSIGNED | TT_FLOAT)))
718 		{
719 			script->script_p++;
720 			token->subtype |= TT_UNSIGNED;
721 		} //end if
722 	} //end for
723 	token->string[len] = '\0';
724 #ifdef NUMBERVALUE
725 	NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue);
726 #endif //NUMBERVALUE
727 	if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER;
728 	return 1;
729 } //end of the function PS_ReadNumber
730 //============================================================================
731 //
732 // Parameter:				-
733 // Returns:					-
734 // Changes Globals:		-
735 //============================================================================
PS_ReadLiteral(script_t * script,token_t * token)736 int PS_ReadLiteral(script_t *script, token_t *token)
737 {
738 	token->type = TT_LITERAL;
739 	//first quote
740 	token->string[0] = *script->script_p++;
741 	//check for end of file
742 	if (!*script->script_p)
743 	{
744 		ScriptError(script, "end of file before trailing \'");
745 		return 0;
746 	} //end if
747 	//if it is an escape character
748 	if (*script->script_p == '\\')
749 	{
750 		if (!PS_ReadEscapeCharacter(script, &token->string[1])) return 0;
751 	} //end if
752 	else
753 	{
754 		token->string[1] = *script->script_p++;
755 	} //end else
756 	//check for trailing quote
757 	if (*script->script_p != '\'')
758 	{
759 		ScriptWarning(script, "too many characters in literal, ignored");
760 		while(*script->script_p &&
761 				*script->script_p != '\'' &&
762 				*script->script_p != '\n')
763 		{
764 			script->script_p++;
765 		} //end while
766 		if (*script->script_p == '\'') script->script_p++;
767 	} //end if
768 	//store the trailing quote
769 	token->string[2] = *script->script_p++;
770 	//store trailing zero to end the string
771 	token->string[3] = '\0';
772 	//the sub type is the integer literal value
773 	token->subtype = token->string[1];
774 	//
775 	return 1;
776 } //end of the function PS_ReadLiteral
777 //============================================================================
778 //
779 // Parameter:				-
780 // Returns:					-
781 // Changes Globals:		-
782 //============================================================================
PS_ReadPunctuation(script_t * script,token_t * token)783 int PS_ReadPunctuation(script_t *script, token_t *token)
784 {
785 	int len;
786 	char *p;
787 	punctuation_t *punc;
788 
789 #ifdef PUNCTABLE
790 	for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next)
791 	{
792 #else
793 	int i;
794 
795 	for (i = 0; script->punctuations[i].p; i++)
796 	{
797 		punc = &script->punctuations[i];
798 #endif //PUNCTABLE
799 		p = punc->p;
800 		len = strlen(p);
801 		//if the script contains at least as much characters as the punctuation
802 		if (script->script_p + len <= script->end_p)
803 		{
804 			//if the script contains the punctuation
805 			if (!strncmp(script->script_p, p, len))
806 			{
807 				strncpy(token->string, p, MAX_TOKEN);
808 				script->script_p += len;
809 				token->type = TT_PUNCTUATION;
810 				//sub type is the number of the punctuation
811 				token->subtype = punc->n;
812 				return 1;
813 			} //end if
814 		} //end if
815 	} //end for
816 	return 0;
817 } //end of the function PS_ReadPunctuation
818 //============================================================================
819 //
820 // Parameter:				-
821 // Returns:					-
822 // Changes Globals:		-
823 //============================================================================
824 int PS_ReadPrimitive(script_t *script, token_t *token)
825 {
826 	int len;
827 
828 	len = 0;
829 	while(*script->script_p > ' ' && *script->script_p != ';')
830 	{
831 		if (len >= MAX_TOKEN)
832 		{
833 			ScriptError(script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN);
834 			return 0;
835 		} //end if
836 		token->string[len++] = *script->script_p++;
837 	} //end while
838 	token->string[len] = 0;
839 	//copy the token into the script structure
840 	Com_Memcpy(&script->token, token, sizeof(token_t));
841 	//primitive reading successfull
842 	return 1;
843 } //end of the function PS_ReadPrimitive
844 //============================================================================
845 //
846 // Parameter:				-
847 // Returns:					-
848 // Changes Globals:		-
849 //============================================================================
850 int PS_ReadToken(script_t *script, token_t *token)
851 {
852 	//if there is a token available (from UnreadToken)
853 	if (script->tokenavailable)
854 	{
855 		script->tokenavailable = 0;
856 		Com_Memcpy(token, &script->token, sizeof(token_t));
857 		return 1;
858 	} //end if
859 	//save script pointer
860 	script->lastscript_p = script->script_p;
861 	//save line counter
862 	script->lastline = script->line;
863 	//clear the token stuff
864 	Com_Memset(token, 0, sizeof(token_t));
865 	//start of the white space
866 	script->whitespace_p = script->script_p;
867 	token->whitespace_p = script->script_p;
868 	//read unusefull stuff
869 	if (!PS_ReadWhiteSpace(script)) return 0;
870 	//end of the white space
871 	script->endwhitespace_p = script->script_p;
872 	token->endwhitespace_p = script->script_p;
873 	//line the token is on
874 	token->line = script->line;
875 	//number of lines crossed before token
876 	token->linescrossed = script->line - script->lastline;
877 	//if there is a leading double quote
878 	if (*script->script_p == '\"')
879 	{
880 		if (!PS_ReadString(script, token, '\"')) return 0;
881 	} //end if
882 	//if an literal
883 	else if (*script->script_p == '\'')
884 	{
885 		//if (!PS_ReadLiteral(script, token)) return 0;
886 		if (!PS_ReadString(script, token, '\'')) return 0;
887 	} //end if
888 	//if there is a number
889 	else if ((*script->script_p >= '0' && *script->script_p <= '9') ||
890 				(*script->script_p == '.' &&
891 				(*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9')))
892 	{
893 		if (!PS_ReadNumber(script, token)) return 0;
894 	} //end if
895 	//if this is a primitive script
896 	else if (script->flags & SCFL_PRIMITIVE)
897 	{
898 		return PS_ReadPrimitive(script, token);
899 	} //end else if
900 	//if there is a name
901 	else if ((*script->script_p >= 'a' && *script->script_p <= 'z') ||
902 		(*script->script_p >= 'A' && *script->script_p <= 'Z') ||
903 		*script->script_p == '_')
904 	{
905 		if (!PS_ReadName(script, token)) return 0;
906 	} //end if
907 	//check for punctuations
908 	else if (!PS_ReadPunctuation(script, token))
909 	{
910 		ScriptError(script, "can't read token");
911 		return 0;
912 	} //end if
913 	//copy the token into the script structure
914 	Com_Memcpy(&script->token, token, sizeof(token_t));
915 	//successfully read a token
916 	return 1;
917 } //end of the function PS_ReadToken
918 //============================================================================
919 //
920 // Parameter:				-
921 // Returns:					-
922 // Changes Globals:		-
923 //============================================================================
924 int PS_ExpectTokenString(script_t *script, char *string)
925 {
926 	token_t token;
927 
928 	if (!PS_ReadToken(script, &token))
929 	{
930 		ScriptError(script, "couldn't find expected %s", string);
931 		return 0;
932 	} //end if
933 
934 	if (strcmp(token.string, string))
935 	{
936 		ScriptError(script, "expected %s, found %s", string, token.string);
937 		return 0;
938 	} //end if
939 	return 1;
940 } //end of the function PS_ExpectToken
941 //============================================================================
942 //
943 // Parameter:				-
944 // Returns:					-
945 // Changes Globals:		-
946 //============================================================================
947 int PS_ExpectTokenType(script_t *script, int type, int subtype, token_t *token)
948 {
949 	char str[MAX_TOKEN];
950 
951 	if (!PS_ReadToken(script, token))
952 	{
953 		ScriptError(script, "couldn't read expected token");
954 		return 0;
955 	} //end if
956 
957 	if (token->type != type)
958 	{
959 		if (type == TT_STRING) strcpy(str, "string");
960 		if (type == TT_LITERAL) strcpy(str, "literal");
961 		if (type == TT_NUMBER) strcpy(str, "number");
962 		if (type == TT_NAME) strcpy(str, "name");
963 		if (type == TT_PUNCTUATION) strcpy(str, "punctuation");
964 		ScriptError(script, "expected a %s, found %s", str, token->string);
965 		return 0;
966 	} //end if
967 	if (token->type == TT_NUMBER)
968 	{
969 		if ((token->subtype & subtype) != subtype)
970 		{
971 			if (subtype & TT_DECIMAL) strcpy(str, "decimal");
972 			if (subtype & TT_HEX) strcpy(str, "hex");
973 			if (subtype & TT_OCTAL) strcpy(str, "octal");
974 			if (subtype & TT_BINARY) strcpy(str, "binary");
975 			if (subtype & TT_LONG) strcat(str, " long");
976 			if (subtype & TT_UNSIGNED) strcat(str, " unsigned");
977 			if (subtype & TT_FLOAT) strcat(str, " float");
978 			if (subtype & TT_INTEGER) strcat(str, " integer");
979 			ScriptError(script, "expected %s, found %s", str, token->string);
980 			return 0;
981 		} //end if
982 	} //end if
983 	else if (token->type == TT_PUNCTUATION)
984 	{
985 		if (subtype < 0)
986 		{
987 			ScriptError(script, "BUG: wrong punctuation subtype");
988 			return 0;
989 		} //end if
990 		if (token->subtype != subtype)
991 		{
992 			ScriptError(script, "expected %s, found %s",
993 							script->punctuations[subtype], token->string);
994 			return 0;
995 		} //end if
996 	} //end else if
997 	return 1;
998 } //end of the function PS_ExpectTokenType
999 //============================================================================
1000 //
1001 // Parameter:				-
1002 // Returns:					-
1003 // Changes Globals:		-
1004 //============================================================================
1005 int PS_ExpectAnyToken(script_t *script, token_t *token)
1006 {
1007 	if (!PS_ReadToken(script, token))
1008 	{
1009 		ScriptError(script, "couldn't read expected token");
1010 		return 0;
1011 	} //end if
1012 	else
1013 	{
1014 		return 1;
1015 	} //end else
1016 } //end of the function PS_ExpectAnyToken
1017 //============================================================================
1018 //
1019 // Parameter:				-
1020 // Returns:					-
1021 // Changes Globals:		-
1022 //============================================================================
1023 int PS_CheckTokenString(script_t *script, char *string)
1024 {
1025 	token_t tok;
1026 
1027 	if (!PS_ReadToken(script, &tok)) return 0;
1028 	//if the token is available
1029 	if (!strcmp(tok.string, string)) return 1;
1030 	//token not available
1031 	script->script_p = script->lastscript_p;
1032 	return 0;
1033 } //end of the function PS_CheckTokenString
1034 //============================================================================
1035 //
1036 // Parameter:				-
1037 // Returns:					-
1038 // Changes Globals:		-
1039 //============================================================================
1040 int PS_CheckTokenType(script_t *script, int type, int subtype, token_t *token)
1041 {
1042 	token_t tok;
1043 
1044 	if (!PS_ReadToken(script, &tok)) return 0;
1045 	//if the type matches
1046 	if (tok.type == type &&
1047 			(tok.subtype & subtype) == subtype)
1048 	{
1049 		Com_Memcpy(token, &tok, sizeof(token_t));
1050 		return 1;
1051 	} //end if
1052 	//token is not available
1053 	script->script_p = script->lastscript_p;
1054 	return 0;
1055 } //end of the function PS_CheckTokenType
1056 //============================================================================
1057 //
1058 // Parameter:				-
1059 // Returns:					-
1060 // Changes Globals:		-
1061 //============================================================================
1062 int PS_SkipUntilString(script_t *script, char *string)
1063 {
1064 	token_t token;
1065 
1066 	while(PS_ReadToken(script, &token))
1067 	{
1068 		if (!strcmp(token.string, string)) return 1;
1069 	} //end while
1070 	return 0;
1071 } //end of the function PS_SkipUntilString
1072 //============================================================================
1073 //
1074 // Parameter:				-
1075 // Returns:					-
1076 // Changes Globals:		-
1077 //============================================================================
1078 void PS_UnreadLastToken(script_t *script)
1079 {
1080 	script->tokenavailable = 1;
1081 } //end of the function UnreadLastToken
1082 //============================================================================
1083 //
1084 // Parameter:				-
1085 // Returns:					-
1086 // Changes Globals:		-
1087 //============================================================================
1088 void PS_UnreadToken(script_t *script, token_t *token)
1089 {
1090 	Com_Memcpy(&script->token, token, sizeof(token_t));
1091 	script->tokenavailable = 1;
1092 } //end of the function UnreadToken
1093 //============================================================================
1094 // returns the next character of the read white space, returns NULL if none
1095 //
1096 // Parameter:				-
1097 // Returns:					-
1098 // Changes Globals:		-
1099 //============================================================================
1100 char PS_NextWhiteSpaceChar(script_t *script)
1101 {
1102 	if (script->whitespace_p != script->endwhitespace_p)
1103 	{
1104 		return *script->whitespace_p++;
1105 	} //end if
1106 	else
1107 	{
1108 		return 0;
1109 	} //end else
1110 } //end of the function PS_NextWhiteSpaceChar
1111 //============================================================================
1112 //
1113 // Parameter:				-
1114 // Returns:					-
1115 // Changes Globals:		-
1116 //============================================================================
1117 void StripDoubleQuotes(char *string)
1118 {
1119 	if (*string == '\"')
1120 	{
1121 		memmove(string, string+1, strlen(string));
1122 	} //end if
1123 	if (string[strlen(string)-1] == '\"')
1124 	{
1125 		string[strlen(string)-1] = '\0';
1126 	} //end if
1127 } //end of the function StripDoubleQuotes
1128 //============================================================================
1129 //
1130 // Parameter:				-
1131 // Returns:					-
1132 // Changes Globals:		-
1133 //============================================================================
1134 void StripSingleQuotes(char *string)
1135 {
1136 	if (*string == '\'')
1137 	{
1138 		memmove(string, string+1, strlen(string));
1139 	} //end if
1140 	if (string[strlen(string)-1] == '\'')
1141 	{
1142 		string[strlen(string)-1] = '\0';
1143 	} //end if
1144 } //end of the function StripSingleQuotes
1145 //============================================================================
1146 //
1147 // Parameter:				-
1148 // Returns:					-
1149 // Changes Globals:		-
1150 //============================================================================
1151 float ReadSignedFloat(script_t *script)
1152 {
1153 	token_t token;
1154 	float sign = 1.0;
1155 
1156 	PS_ExpectAnyToken(script, &token);
1157 	if (!strcmp(token.string, "-"))
1158 	{
1159 		if(!PS_ExpectAnyToken(script, &token))
1160 		{
1161 			ScriptError(script, "Missing float value\n", token.string);
1162 			return 0;
1163 		}
1164 
1165 		sign = -1.0;
1166 	}
1167 
1168 	if (token.type != TT_NUMBER)
1169 	{
1170 		ScriptError(script, "expected float value, found %s\n", token.string);
1171 		return 0;
1172 	}
1173 
1174 	return sign * token.floatvalue;
1175 } //end of the function ReadSignedFloat
1176 //============================================================================
1177 //
1178 // Parameter:				-
1179 // Returns:					-
1180 // Changes Globals:		-
1181 //============================================================================
1182 signed long int ReadSignedInt(script_t *script)
1183 {
1184 	token_t token;
1185 	signed long int sign = 1;
1186 
1187 	PS_ExpectAnyToken(script, &token);
1188 	if (!strcmp(token.string, "-"))
1189 	{
1190 		if(!PS_ExpectAnyToken(script, &token))
1191 		{
1192 			ScriptError(script, "Missing integer value\n", token.string);
1193 			return 0;
1194 		}
1195 
1196 		sign = -1;
1197 	}
1198 
1199 	if (token.type != TT_NUMBER || token.subtype == TT_FLOAT)
1200 	{
1201 		ScriptError(script, "expected integer value, found %s\n", token.string);
1202 		return 0;
1203 	}
1204 
1205 	return sign * token.intvalue;
1206 } //end of the function ReadSignedInt
1207 //============================================================================
1208 //
1209 // Parameter:				-
1210 // Returns:					-
1211 // Changes Globals:		-
1212 //============================================================================
1213 void SetScriptFlags(script_t *script, int flags)
1214 {
1215 	script->flags = flags;
1216 } //end of the function SetScriptFlags
1217 //============================================================================
1218 //
1219 // Parameter:				-
1220 // Returns:					-
1221 // Changes Globals:		-
1222 //============================================================================
1223 int GetScriptFlags(script_t *script)
1224 {
1225 	return script->flags;
1226 } //end of the function GetScriptFlags
1227 //============================================================================
1228 //
1229 // Parameter:				-
1230 // Returns:					-
1231 // Changes Globals:		-
1232 //============================================================================
1233 void ResetScript(script_t *script)
1234 {
1235 	//pointer in script buffer
1236 	script->script_p = script->buffer;
1237 	//pointer in script buffer before reading token
1238 	script->lastscript_p = script->buffer;
1239 	//begin of white space
1240 	script->whitespace_p = NULL;
1241 	//end of white space
1242 	script->endwhitespace_p = NULL;
1243 	//set if there's a token available in script->token
1244 	script->tokenavailable = 0;
1245 	//
1246 	script->line = 1;
1247 	script->lastline = 1;
1248 	//clear the saved token
1249 	Com_Memset(&script->token, 0, sizeof(token_t));
1250 } //end of the function ResetScript
1251 //============================================================================
1252 // returns true if at the end of the script
1253 //
1254 // Parameter:				-
1255 // Returns:					-
1256 // Changes Globals:		-
1257 //============================================================================
1258 int EndOfScript(script_t *script)
1259 {
1260 	return script->script_p >= script->end_p;
1261 } //end of the function EndOfScript
1262 //============================================================================
1263 //
1264 // Parameter:				-
1265 // Returns:					-
1266 // Changes Globals:		-
1267 //============================================================================
1268 int NumLinesCrossed(script_t *script)
1269 {
1270 	return script->line - script->lastline;
1271 } //end of the function NumLinesCrossed
1272 //============================================================================
1273 //
1274 // Parameter:				-
1275 // Returns:					-
1276 // Changes Globals:		-
1277 //============================================================================
1278 int ScriptSkipTo(script_t *script, char *value)
1279 {
1280 	int len;
1281 	char firstchar;
1282 
1283 	firstchar = *value;
1284 	len = strlen(value);
1285 	do
1286 	{
1287 		if (!PS_ReadWhiteSpace(script)) return 0;
1288 		if (*script->script_p == firstchar)
1289 		{
1290 			if (!strncmp(script->script_p, value, len))
1291 			{
1292 				return 1;
1293 			} //end if
1294 		} //end if
1295 		script->script_p++;
1296 	} while(1);
1297 } //end of the function ScriptSkipTo
1298 #ifndef BOTLIB
1299 //============================================================================
1300 //
1301 // Parameter:				-
1302 // Returns:					-
1303 // Changes Globals:		-
1304 //============================================================================
1305 int FileLength(FILE *fp)
1306 {
1307 	int pos;
1308 	int end;
1309 
1310 	pos = ftell(fp);
1311 	fseek(fp, 0, SEEK_END);
1312 	end = ftell(fp);
1313 	fseek(fp, pos, SEEK_SET);
1314 
1315 	return end;
1316 } //end of the function FileLength
1317 #endif
1318 //============================================================================
1319 //
1320 // Parameter:				-
1321 // Returns:					-
1322 // Changes Globals:		-
1323 //============================================================================
1324 script_t *LoadScriptFile(const char *filename)
1325 {
1326 #ifdef BOTLIB
1327 	fileHandle_t fp;
1328 	char pathname[MAX_QPATH];
1329 #else
1330 	FILE *fp;
1331 #endif
1332 	int length;
1333 	void *buffer;
1334 	script_t *script;
1335 
1336 #ifdef BOTLIB
1337 	if (strlen(basefolder))
1338 		Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
1339 	else
1340 		Com_sprintf(pathname, sizeof(pathname), "%s", filename);
1341 	length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
1342 	if (!fp) return NULL;
1343 #else
1344 	fp = fopen(filename, "rb");
1345 	if (!fp) return NULL;
1346 
1347 	length = FileLength(fp);
1348 #endif
1349 
1350 	buffer = GetClearedMemory(sizeof(script_t) + length + 1);
1351 	script = (script_t *) buffer;
1352 	Com_Memset(script, 0, sizeof(script_t));
1353 	strcpy(script->filename, filename);
1354 	script->buffer = (char *) buffer + sizeof(script_t);
1355 	script->buffer[length] = 0;
1356 	script->length = length;
1357 	//pointer in script buffer
1358 	script->script_p = script->buffer;
1359 	//pointer in script buffer before reading token
1360 	script->lastscript_p = script->buffer;
1361 	//pointer to end of script buffer
1362 	script->end_p = &script->buffer[length];
1363 	//set if there's a token available in script->token
1364 	script->tokenavailable = 0;
1365 	//
1366 	script->line = 1;
1367 	script->lastline = 1;
1368 	//
1369 	SetScriptPunctuations(script, NULL);
1370 	//
1371 #ifdef BOTLIB
1372 	botimport.FS_Read(script->buffer, length, fp);
1373 	botimport.FS_FCloseFile(fp);
1374 #else
1375 	if (fread(script->buffer, length, 1, fp) != 1)
1376 	{
1377 		FreeMemory(buffer);
1378 		script = NULL;
1379 	} //end if
1380 	fclose(fp);
1381 #endif
1382 
1383 	return script;
1384 } //end of the function LoadScriptFile
1385 //============================================================================
1386 //
1387 // Parameter:			-
1388 // Returns:				-
1389 // Changes Globals:		-
1390 //============================================================================
1391 script_t *LoadScriptMemory(char *ptr, int length, char *name)
1392 {
1393 	void *buffer;
1394 	script_t *script;
1395 
1396 	buffer = GetClearedMemory(sizeof(script_t) + length + 1);
1397 	script = (script_t *) buffer;
1398 	Com_Memset(script, 0, sizeof(script_t));
1399 	strcpy(script->filename, name);
1400 	script->buffer = (char *) buffer + sizeof(script_t);
1401 	script->buffer[length] = 0;
1402 	script->length = length;
1403 	//pointer in script buffer
1404 	script->script_p = script->buffer;
1405 	//pointer in script buffer before reading token
1406 	script->lastscript_p = script->buffer;
1407 	//pointer to end of script buffer
1408 	script->end_p = &script->buffer[length];
1409 	//set if there's a token available in script->token
1410 	script->tokenavailable = 0;
1411 	//
1412 	script->line = 1;
1413 	script->lastline = 1;
1414 	//
1415 	SetScriptPunctuations(script, NULL);
1416 	//
1417 	Com_Memcpy(script->buffer, ptr, length);
1418 	//
1419 	return script;
1420 } //end of the function LoadScriptMemory
1421 //============================================================================
1422 //
1423 // Parameter:				-
1424 // Returns:					-
1425 // Changes Globals:		-
1426 //============================================================================
1427 void FreeScript(script_t *script)
1428 {
1429 #ifdef PUNCTABLE
1430 	if (script->punctuationtable) FreeMemory(script->punctuationtable);
1431 #endif //PUNCTABLE
1432 	FreeMemory(script);
1433 } //end of the function FreeScript
1434 //============================================================================
1435 //
1436 // Parameter:				-
1437 // Returns:					-
1438 // Changes Globals:		-
1439 //============================================================================
1440 void PS_SetBaseFolder(char *path)
1441 {
1442 #ifdef BSPC
1443 	sprintf(basefolder, path);
1444 #else
1445 	Com_sprintf(basefolder, sizeof(basefolder), "%s", path);
1446 #endif
1447 } //end of the function PS_SetBaseFolder
1448