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