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