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