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 //succesfully 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 succesfully
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 //succesfully 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 strcpy(string, string+1);
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 strcpy(string, string+1);
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 sign = -1.0;
1160 PS_ExpectTokenType(script, TT_NUMBER, 0, &token);
1161 } //end if
1162 else if (token.type != TT_NUMBER)
1163 {
1164 ScriptError(script, "expected float value, found %s\n", token.string);
1165 } //end else if
1166 return sign * token.floatvalue;
1167 } //end of the function ReadSignedFloat
1168 //============================================================================
1169 //
1170 // Parameter: -
1171 // Returns: -
1172 // Changes Globals: -
1173 //============================================================================
1174 signed long int ReadSignedInt(script_t *script)
1175 {
1176 token_t token;
1177 signed long int sign = 1;
1178
1179 PS_ExpectAnyToken(script, &token);
1180 if (!strcmp(token.string, "-"))
1181 {
1182 sign = -1;
1183 PS_ExpectTokenType(script, TT_NUMBER, TT_INTEGER, &token);
1184 } //end if
1185 else if (token.type != TT_NUMBER || token.subtype == TT_FLOAT)
1186 {
1187 ScriptError(script, "expected integer value, found %s\n", token.string);
1188 } //end else if
1189 return sign * token.intvalue;
1190 } //end of the function ReadSignedInt
1191 //============================================================================
1192 //
1193 // Parameter: -
1194 // Returns: -
1195 // Changes Globals: -
1196 //============================================================================
1197 void SetScriptFlags(script_t *script, int flags)
1198 {
1199 script->flags = flags;
1200 } //end of the function SetScriptFlags
1201 //============================================================================
1202 //
1203 // Parameter: -
1204 // Returns: -
1205 // Changes Globals: -
1206 //============================================================================
1207 int GetScriptFlags(script_t *script)
1208 {
1209 return script->flags;
1210 } //end of the function GetScriptFlags
1211 //============================================================================
1212 //
1213 // Parameter: -
1214 // Returns: -
1215 // Changes Globals: -
1216 //============================================================================
1217 void ResetScript(script_t *script)
1218 {
1219 //pointer in script buffer
1220 script->script_p = script->buffer;
1221 //pointer in script buffer before reading token
1222 script->lastscript_p = script->buffer;
1223 //begin of white space
1224 script->whitespace_p = NULL;
1225 //end of white space
1226 script->endwhitespace_p = NULL;
1227 //set if there's a token available in script->token
1228 script->tokenavailable = 0;
1229 //
1230 script->line = 1;
1231 script->lastline = 1;
1232 //clear the saved token
1233 Com_Memset(&script->token, 0, sizeof(token_t));
1234 } //end of the function ResetScript
1235 //============================================================================
1236 // returns true if at the end of the script
1237 //
1238 // Parameter: -
1239 // Returns: -
1240 // Changes Globals: -
1241 //============================================================================
1242 int EndOfScript(script_t *script)
1243 {
1244 return script->script_p >= script->end_p;
1245 } //end of the function EndOfScript
1246 //============================================================================
1247 //
1248 // Parameter: -
1249 // Returns: -
1250 // Changes Globals: -
1251 //============================================================================
1252 int NumLinesCrossed(script_t *script)
1253 {
1254 return script->line - script->lastline;
1255 } //end of the function NumLinesCrossed
1256 //============================================================================
1257 //
1258 // Parameter: -
1259 // Returns: -
1260 // Changes Globals: -
1261 //============================================================================
1262 int ScriptSkipTo(script_t *script, char *value)
1263 {
1264 int len;
1265 char firstchar;
1266
1267 firstchar = *value;
1268 len = strlen(value);
1269 do
1270 {
1271 if (!PS_ReadWhiteSpace(script)) return 0;
1272 if (*script->script_p == firstchar)
1273 {
1274 if (!strncmp(script->script_p, value, len))
1275 {
1276 return 1;
1277 } //end if
1278 } //end if
1279 script->script_p++;
1280 } while(1);
1281 } //end of the function ScriptSkipTo
1282 #ifndef BOTLIB
1283 //============================================================================
1284 //
1285 // Parameter: -
1286 // Returns: -
1287 // Changes Globals: -
1288 //============================================================================
1289 int FileLength(FILE *fp)
1290 {
1291 int pos;
1292 int end;
1293
1294 pos = ftell(fp);
1295 fseek(fp, 0, SEEK_END);
1296 end = ftell(fp);
1297 fseek(fp, pos, SEEK_SET);
1298
1299 return end;
1300 } //end of the function FileLength
1301 #endif
1302 //============================================================================
1303 //
1304 // Parameter: -
1305 // Returns: -
1306 // Changes Globals: -
1307 //============================================================================
1308 script_t *LoadScriptFile(const char *filename)
1309 {
1310 #ifdef BOTLIB
1311 fileHandle_t fp;
1312 char pathname[MAX_QPATH];
1313 #else
1314 FILE *fp;
1315 #endif
1316 int length;
1317 void *buffer;
1318 script_t *script;
1319
1320 #ifdef BOTLIB
1321 if (strlen(basefolder))
1322 Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
1323 else
1324 Com_sprintf(pathname, sizeof(pathname), "%s", filename);
1325 length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
1326 if (!fp) return NULL;
1327 #else
1328 fp = fopen(filename, "rb");
1329 if (!fp) return NULL;
1330
1331 length = FileLength(fp);
1332 #endif
1333
1334 buffer = GetClearedMemory(sizeof(script_t) + length + 1);
1335 script = (script_t *) buffer;
1336 Com_Memset(script, 0, sizeof(script_t));
1337 strcpy(script->filename, filename);
1338 script->buffer = (char *) buffer + sizeof(script_t);
1339 script->buffer[length] = 0;
1340 script->length = length;
1341 //pointer in script buffer
1342 script->script_p = script->buffer;
1343 //pointer in script buffer before reading token
1344 script->lastscript_p = script->buffer;
1345 //pointer to end of script buffer
1346 script->end_p = &script->buffer[length];
1347 //set if there's a token available in script->token
1348 script->tokenavailable = 0;
1349 //
1350 script->line = 1;
1351 script->lastline = 1;
1352 //
1353 SetScriptPunctuations(script, NULL);
1354 //
1355 #ifdef BOTLIB
1356 botimport.FS_Read(script->buffer, length, fp);
1357 botimport.FS_FCloseFile(fp);
1358 #else
1359 if (fread(script->buffer, length, 1, fp) != 1)
1360 {
1361 FreeMemory(buffer);
1362 script = NULL;
1363 } //end if
1364 fclose(fp);
1365 #endif
1366
1367 return script;
1368 } //end of the function LoadScriptFile
1369 //============================================================================
1370 //
1371 // Parameter: -
1372 // Returns: -
1373 // Changes Globals: -
1374 //============================================================================
1375 script_t *LoadScriptMemory(char *ptr, int length, char *name)
1376 {
1377 void *buffer;
1378 script_t *script;
1379
1380 buffer = GetClearedMemory(sizeof(script_t) + length + 1);
1381 script = (script_t *) buffer;
1382 Com_Memset(script, 0, sizeof(script_t));
1383 strcpy(script->filename, name);
1384 script->buffer = (char *) buffer + sizeof(script_t);
1385 script->buffer[length] = 0;
1386 script->length = length;
1387 //pointer in script buffer
1388 script->script_p = script->buffer;
1389 //pointer in script buffer before reading token
1390 script->lastscript_p = script->buffer;
1391 //pointer to end of script buffer
1392 script->end_p = &script->buffer[length];
1393 //set if there's a token available in script->token
1394 script->tokenavailable = 0;
1395 //
1396 script->line = 1;
1397 script->lastline = 1;
1398 //
1399 SetScriptPunctuations(script, NULL);
1400 //
1401 Com_Memcpy(script->buffer, ptr, length);
1402 //
1403 return script;
1404 } //end of the function LoadScriptMemory
1405 //============================================================================
1406 //
1407 // Parameter: -
1408 // Returns: -
1409 // Changes Globals: -
1410 //============================================================================
1411 void FreeScript(script_t *script)
1412 {
1413 #ifdef PUNCTABLE
1414 if (script->punctuationtable) FreeMemory(script->punctuationtable);
1415 #endif //PUNCTABLE
1416 FreeMemory(script);
1417 } //end of the function FreeScript
1418 //============================================================================
1419 //
1420 // Parameter: -
1421 // Returns: -
1422 // Changes Globals: -
1423 //============================================================================
1424 void PS_SetBaseFolder(char *path)
1425 {
1426 #ifdef BSPC
1427 sprintf(basefolder, path);
1428 #else
1429 Com_sprintf(basefolder, sizeof(basefolder), "%s", path);
1430 #endif
1431 } //end of the function PS_SetBaseFolder
1432