1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //
21 // parse.c
22 // FIXME TODO:
23 // - currentCol is broken in certain cases
24 //
25
26 #include "common.h"
27
28 /*
29 ==============================================================================
30
31 SESSION CREATION
32
33 ==============================================================================
34 */
35
36 static parse_t ps_sessionList[MAX_PARSE_SESSIONS];
37 static byte ps_numSessions;
38
39 static char ps_scratchToken[MAX_PS_TOKCHARS]; // Used for temporary storage during data-type/post parsing
40 static const char *ps_dataTypeStr[] = {
41 "char",
42 "bool",
43 "byte",
44 "double",
45 "float",
46 "int",
47 "uint"
48 };
49
50 /*
51 ============
52 PS_StartSession
53 ============
54 */
PS_StartSession(char * dataPtr,uint32 properties)55 parse_t *PS_StartSession (char *dataPtr, uint32 properties)
56 {
57 parse_t *ps = NULL;
58 int i;
59
60 // Make sure incoming data is valid
61 if (!dataPtr)
62 return NULL;
63
64 // Find an available session
65 for (i=0 ; i<ps_numSessions ; i++) {
66 if (!ps_sessionList[i].inUse) {
67 ps = &ps_sessionList[i];
68 break;
69 }
70 }
71
72 if (i == ps_numSessions) {
73 if (ps_numSessions+1 >= MAX_PARSE_SESSIONS)
74 Com_Error (ERR_FATAL, "PS_StartSession: MAX_PARSE_SESSIONS\n");
75
76 ps = &ps_sessionList[ps_numSessions++];
77 }
78
79 // Fill in the values
80 ps->currentCol = 1;
81 ps->currentLine = 1;
82 ps->currentToken[0] = '\0';
83
84 ps->dataPtr = dataPtr;
85 ps->dataPtrLast = dataPtr;
86 ps->inUse = qTrue;
87
88 ps->numErrors = 0;
89 ps->numWarnings = 0;
90
91 ps->properties = properties;
92 return ps;
93 }
94
95
96 /*
97 ============
98 PS_EndSession
99 ============
100 */
PS_EndSession(parse_t * ps)101 void PS_EndSession (parse_t *ps)
102 {
103 if (!ps)
104 return;
105
106 ps->currentCol = 1;
107 ps->currentLine = 1;
108 ps->currentToken[0] = '\0';
109
110 ps->dataPtr = NULL;
111 ps->dataPtrLast = NULL;
112 ps->inUse = qFalse;
113
114 ps->numErrors = 0;
115 ps->numWarnings = 0;
116
117 ps->properties = 0;
118 }
119
120 /*
121 ==============================================================================
122
123 ERROR HANDLING
124
125 ==============================================================================
126 */
127
128 /*
129 ============
130 PS_AddErrorCount
131 ============
132 */
PS_AddErrorCount(parse_t * ps,uint32 * errors,uint32 * warnings)133 void PS_AddErrorCount (parse_t *ps, uint32 *errors, uint32 *warnings)
134 {
135 if (ps) {
136 if (errors)
137 *errors += ps->numErrors;
138 if (warnings)
139 *warnings += ps->numWarnings;
140 }
141 }
142
143
144 /*
145 ============
146 PS_GetErrorCount
147 ============
148 */
PS_GetErrorCount(parse_t * ps,uint32 * errors,uint32 * warnings)149 void PS_GetErrorCount (parse_t *ps, uint32 *errors, uint32 *warnings)
150 {
151 if (ps) {
152 if (errors)
153 *errors = ps->numErrors;
154 if (warnings)
155 *warnings = ps->numWarnings;
156 }
157 }
158
159
160 /*
161 ============
162 PS_GetPosition
163 ============
164 */
PS_GetPosition(parse_t * ps,uint32 * line,uint32 * col)165 void PS_GetPosition (parse_t *ps, uint32 *line, uint32 *col)
166 {
167 if (ps) {
168 if (line)
169 *line = ps->currentLine;
170 if (col)
171 *col = ps->currentCol;
172 }
173 }
174
175
176 /*
177 ============
178 PS_PrintError
179 ============
180 */
PS_PrintError(parse_t * ps,char * fmt,...)181 static void PS_PrintError (parse_t *ps, char *fmt, ...)
182 {
183 va_list argptr;
184 char msg[MAX_COMPRINT];
185
186 // Evaluate args
187 va_start (argptr, fmt);
188 vsnprintf (msg, sizeof (msg), fmt, argptr);
189 va_end (argptr);
190
191 // Print
192 ps->numErrors++;
193 Com_ConPrint (PRNT_ERROR, msg);
194 }
195
196
197 /*
198 ============
199 PS_PrintWarning
200 ============
201 */
PS_PrintWarning(parse_t * ps,char * fmt,...)202 static void PS_PrintWarning (parse_t *ps, char *fmt, ...)
203 {
204 va_list argptr;
205 char msg[MAX_COMPRINT];
206
207 // Evaluate args
208 va_start (argptr, fmt);
209 vsnprintf (msg, sizeof (msg), fmt, argptr);
210 va_end (argptr);
211
212 // Print
213 ps->numWarnings++;
214 Com_ConPrint (PRNT_WARNING, msg);
215 }
216
217 /*
218 ==============================================================================
219
220 PARSING
221
222 ==============================================================================
223 */
224
225 /*
226 ============
227 PS_ParseToken
228
229 Sets data to NULL when a '\0' is hit, or when a broken comment is detected.
230 Returns qTrue only when a comment block was handled.
231 ============
232 */
PS_SkipComments(parse_t * ps,char ** data,uint32 flags)233 static qBool PS_SkipComments (parse_t *ps, char **data, uint32 flags)
234 {
235 char *p;
236
237 // See if any comment types are allowed
238 if (!(ps->properties & PSP_COMMENT_MASK))
239 return qFalse;
240
241 p = *data;
242
243 switch (*p) {
244 case '#':
245 // Skip "# comments"
246 if (ps->properties & PSP_COMMENT_POUND) {
247 while (*p != '\n') {
248 if (*p == '\0') {
249 *data = NULL;
250 return qTrue;
251 }
252
253 p++;
254 ps->currentCol++;
255 }
256
257 *data = p;
258 return qTrue;
259 }
260 break;
261
262 case '*':
263 // This shouldn't happen with proper commenting
264 if (p[1] == '/' && ps->properties & PSP_COMMENT_BLOCK) {
265 p += 2;
266 PS_PrintError (ps, "PARSE ERROR: end-comment '*/' with no opening\n");
267 *data = NULL;
268 return qTrue;
269 }
270 break;
271
272 case '/':
273 // Skip "// comments"
274 if (p[1] == '/' && ps->properties & PSP_COMMENT_LINE) {
275 while (*p != '\n') {
276 if (*p == '\0') {
277 *data = NULL;
278 return qTrue;
279 }
280
281 p++;
282 ps->currentCol++;
283 }
284
285 *data = p;
286 return qTrue;
287 }
288
289 // Skip "/* comments */"
290 if (p[1] == '*' && ps->properties & PSP_COMMENT_BLOCK) {
291 // Skip initial "/*"
292 p += 2;
293 ps->currentCol += 2;
294
295 // Skip everything until "*/"
296 while (*p && (*p != '*' || p[1] != '/')) {
297 if (*p == '\n') {
298 ps->currentCol = 0;
299 ps->currentLine++;
300 }
301 else {
302 ps->currentCol++;
303 }
304
305 p++;
306 }
307
308 // Skip the final "*/"
309 if (*p == '*' && p[1] == '/') {
310 p += 2;
311 ps->currentCol += 2;
312 *data = p;
313 return qTrue;
314 }
315
316 // Didn't find final "*/" (hit EOF)
317 PS_PrintError (ps, "PARSE ERROR: unclosed comment and hit EOF\n");
318 *data = NULL;
319 return qTrue;
320 }
321 break;
322 }
323
324 // No comment block handled
325 return qFalse;
326 }
327
328
329 /*
330 ============
331 PS_ConvertEscape
332 ============
333 */
PS_ConvertEscape(parse_t * ps,uint32 flags)334 static qBool PS_ConvertEscape (parse_t *ps, uint32 flags)
335 {
336 uint32 len, i;
337 char *source, *scratch;
338
339 // If it's blank then why even try?
340 len = strlen (ps->currentToken);
341 if (!len)
342 return qTrue;
343
344 // Convert escape characters
345 source = &ps->currentToken[0];
346 scratch = &ps_scratchToken[0];
347 for (i=0 ; i<len ; i++) {
348 if (source[0] != '\\') {
349 *scratch++ = *source++;
350 continue;
351 }
352
353 // Hit a '\'
354 switch (source[1]) {
355 case 'n':
356 if (flags & PSF_CONVERT_NEWLINE) {
357 *scratch++ = '\n';
358 source += 2;
359 continue;
360 }
361 break;
362
363 default:
364 PS_PrintWarning (ps, "PARSE WARNING: unknown escape character '%c%c', ignoring\n", source[0], source[1]);
365 *scratch++ = *source++;
366 *scratch++ = *source++;
367 break;
368 }
369 }
370 *scratch = '\0';
371
372 // Copy scratch back to the current token
373 Q_strncpyz (ps->currentToken, ps_scratchToken, sizeof (ps->currentToken));
374 return qTrue;
375 }
376
377
378 /*
379 ============
380 PS_ParseToken
381 - Skip whitespace and skip comments. If a comment gets skipped start over at
382 skip whitespace to make sure we're right up at the tail of the next token.
383 - If it's a quoted string, copy the string into the currentToken until an
384 end-quote is hit.
385 - If it's not a quoted string, store off characters into currentToken until
386 a quotation, comment, or blank space is hit.
387 ============
388 */
PS_ParseToken(parse_t * ps,uint32 flags,char ** target)389 qBool PS_ParseToken (parse_t *ps, uint32 flags, char **target)
390 {
391 int c, len;
392 char *data;
393
394 if (!ps)
395 return qFalse;
396
397 // Check if the incoming data offset is valid (see if we hit EOF last the last run)
398 data = ps->dataPtr;
399 if (!data) {
400 PS_PrintError (ps, "PARSE ERROR: called PS_ParseToken and already hit EOF\n");
401 return qFalse;
402 }
403 ps->dataPtrLast = ps->dataPtr;
404
405 // Clear the current token
406 ps->currentToken[0] = '\0';
407 len = 0;
408
409 for ( ; ; ) {
410 // Skip whitespace
411 while ((c = *data) <= ' ') {
412 switch (c) {
413 case '\0':
414 ps->dataPtr = NULL;
415 return qFalse;
416
417 case '\n':
418 if (!(flags & PSF_ALLOW_NEWLINES)) {
419 ps->dataPtr = data;
420 if (!ps->currentToken[0])
421 return qFalse;
422
423 *target = ps->currentToken;
424 return qTrue;
425 }
426
427 ps->currentCol = 0;
428 ps->currentLine++;
429 break;
430
431 default:
432 ps->currentCol++;
433 break;
434 }
435
436 data++;
437 }
438
439 // Skip comments
440 if (PS_SkipComments (ps, &data, flags)) {
441 if (!data) {
442 ps->dataPtr = NULL;
443 return qFalse;
444 }
445 }
446 else {
447 // No comment, don't skip anymore whitespace
448 break;
449 }
450 }
451
452 // Handle quoted strings specially
453 // FIXME: PSP_QUOTES_TOKENED
454 if (c == '\"') {
455 ps->currentCol++;
456 data++;
457
458 for ( ; ; ) {
459 c = *data++;
460 switch (c) {
461 case '\0':
462 ps->currentCol++;
463 ps->dataPtr = data;
464 PS_PrintError (ps, "PARSE ERROR: hit EOF while inside quotation\n");
465 return qFalse;
466
467 case '\"':
468 ps->currentCol++;
469 ps->dataPtr = data;
470 ps->currentToken[len] = '\0';
471
472 // Empty token
473 if (!ps->currentToken[0])
474 return qFalse;
475
476 // Lower-case if desired
477 if (flags & PSF_TO_LOWER)
478 Q_strlwr (ps->currentToken);
479 if (flags & PSF_CONVERT_NEWLINE) {
480 if (!PS_ConvertEscape (ps, flags))
481 return qFalse;
482 }
483
484 *target = ps->currentToken;
485 return qTrue;
486
487 case '\n':
488 if (!(flags & PSF_ALLOW_NEWLINES)) {
489 ps->dataPtr = data;
490 if (!ps->currentToken[0])
491 return qFalse;
492
493 *target = ps->currentToken;
494 return qTrue;
495 }
496
497 ps->currentCol = 0;
498 ps->currentLine++;
499 break;
500
501 default:
502 ps->currentCol++;
503 break;
504 }
505
506 if (len < MAX_PS_TOKCHARS)
507 ps->currentToken[len++] = c;
508 }
509 }
510
511 // Parse a regular word
512 for ( ; ; ) {
513 if (c <= ' ' || c == '\"') // FIXME: PSP_QUOTES_TOKENED
514 break; // Stop at spaces and quotation marks
515
516 // Stop at opening comments
517 if (ps->properties & PSP_COMMENT_MASK) {
518 if (c == '#' && ps->properties & PSP_COMMENT_POUND)
519 break;
520 if (c == '/') {
521 if (data[1] == '/' && ps->properties & PSP_COMMENT_LINE)
522 break;
523 if (data[1] == '*' && ps->properties & PSP_COMMENT_BLOCK)
524 break;
525 }
526 if (c == '*' && data[1] == '/' && ps->properties & PSP_COMMENT_BLOCK) {
527 ps->dataPtr = data;
528 PS_PrintError (ps, "PARSE ERROR: end-comment '*/' with no opening\n");
529 return qFalse;
530 }
531 }
532
533 // Store character
534 if (len < MAX_PS_TOKCHARS)
535 ps->currentToken[len++] = c;
536
537 ps->currentCol++;
538 c = *++data;
539 }
540
541 // Check length
542 if (len >= MAX_PS_TOKCHARS-1) {
543 PS_PrintError (ps, "PARSE ERROR: token too long!\n");
544 ps->dataPtr = data;
545 return qFalse;
546 }
547
548 // Done
549 ps->currentToken[len] = '\0';
550 ps->dataPtr = data;
551
552 // Empty token
553 if (!ps->currentToken[0])
554 return qFalse;
555
556 // Lower-case if desired
557 if (flags & PSF_TO_LOWER)
558 Q_strlwr (ps->currentToken);
559 if (flags & PSF_CONVERT_NEWLINE) {
560 if (!PS_ConvertEscape (ps, flags))
561 return qFalse;
562 }
563 *target = ps->currentToken;
564 return qTrue;
565 }
566
567
568 /*
569 ============
570 PS_UndoParse
571
572 Simply move back to the point parsing was at before the last token parse.
573 Has issues with datatype parsing and "1" "1" "1" etc token values.
574 ============
575 */
PS_UndoParse(parse_t * ps)576 void PS_UndoParse (parse_t *ps)
577 {
578 if (!ps)
579 return;
580
581 ps->dataPtr = ps->dataPtrLast;
582 }
583
584
585 /*
586 ============
587 PS_SkipLine
588
589 Unlike other parsing functions in this file, this does not route through
590 PS_ParseToken simply for the small performance gain.
591
592 Simply skip from the current position to the first new-line character.
593 ============
594 */
PS_SkipLine(parse_t * ps)595 void PS_SkipLine (parse_t *ps)
596 {
597 char *data;
598
599 if (!ps)
600 return;
601
602 // Check if the incoming data offset is valid (see if we hit EOF last the last run)
603 data = ps->dataPtr;
604 if (!data) {
605 PS_PrintError (ps, "PARSE ERROR: called PS_SkipLine and already hit EOF\n");
606 return;
607 }
608 ps->dataPtrLast = ps->dataPtr;
609
610 // Skip to the end of the line
611 while (*data && *data != '\n') {
612 data++;
613 ps->currentCol++;
614 }
615 ps->dataPtr = data;
616 }
617
618 /*
619 ==============================================================================
620
621 DATA-TYPE PARSING
622
623 ==============================================================================
624 */
625
626 /*
627 ============
628 PS_VerifyCharVec
629 ============
630 */
PS_VerifyCharVec(char * token,char * target)631 static qBool PS_VerifyCharVec (char *token, char *target)
632 {
633 uint32 len, i;
634 int temp;
635
636 len = strlen (token);
637 for (i=0 ; i<len ; i++) {
638 if (token[i] >= '0' || token[i] <= '9')
639 continue;
640 if (token[i] == '-' && i == 0)
641 continue;
642 break;
643 }
644 if (i != len)
645 return qFalse;
646
647 temp = atoi (token);
648 if (temp < -128 || temp > 127)
649 return qFalse;
650
651 *target = temp;
652 return qTrue;
653 }
654
655
656 /*
657 ============
658 PS_VerifyBooleanVec
659 ============
660 */
PS_VerifyBooleanVec(char * token,qBool * target)661 static qBool PS_VerifyBooleanVec (char *token, qBool *target)
662 {
663 if (!strcmp (token, "1") || !strcmp (token, "true")) {
664 *target = qTrue;
665 return qTrue;
666 }
667 if (!strcmp (token, "0") || !strcmp (token, "false")) {
668 *target = qFalse;
669 return qTrue;
670 }
671
672 return qFalse;
673 }
674
675
676 /*
677 ============
678 PS_VerifyByteVec
679 ============
680 */
PS_VerifyByteVec(char * token,byte * target)681 static qBool PS_VerifyByteVec (char *token, byte *target)
682 {
683 uint32 len, i;
684 int temp;
685
686 len = strlen (token);
687 for (i=0 ; i<len ; i++) {
688 if (token[i] >= '0' || token[i] <= '9')
689 continue;
690 if (token[i] == '-' && i == 0)
691 continue;
692 break;
693 }
694 if (i != len)
695 return qFalse;
696
697 temp = atoi (token);
698 if (temp < 0 || temp > 255)
699 return qFalse;
700
701 *target = temp;
702 return qTrue;
703 }
704
705
706 /*
707 ============
708 PS_VerifyDoubleVec
709 ============
710 */
PS_VerifyDoubleVec(char * token,double * target)711 static qBool PS_VerifyDoubleVec (char *token, double *target)
712 {
713 uint32 len, i;
714 qBool dot;
715
716 dot = qFalse;
717 len = strlen (token);
718 for (i=0 ; i<len ; i++) {
719 if (token[i] >= '0' || token[i] <= '9')
720 continue;
721 if (token[i] == '-' && i == 0)
722 continue;
723 if (token[i] == '.' && !dot) {
724 dot = qTrue;
725 continue;
726 }
727 break;
728 }
729 if (i != len)
730 return qFalse;
731
732 *target = atof (token);
733 return qTrue;
734 }
735
736
737 /*
738 ============
739 PS_VerifyFloatVec
740 ============
741 */
PS_VerifyFloatVec(char * token,float * target)742 static qBool PS_VerifyFloatVec (char *token, float *target)
743 {
744 uint32 len, i;
745 qBool dot;
746
747 dot = qFalse;
748 len = strlen (token);
749 for (i=0 ; i<len ; i++) {
750 if (token[i] >= '0' || token[i] <= '9')
751 continue;
752 if (token[i] == '.' && !dot) {
753 dot = qTrue;
754 continue;
755 }
756 if (i == 0) {
757 if (token[i] == '-')
758 continue;
759 }
760 else if (i == len-1 && (token[i] == 'f' || token[i] == 'F'))
761 continue;
762 break;
763 }
764 if (i != len)
765 return qFalse;
766
767 *target = (float)atof (token);
768 return qTrue;
769 }
770
771
772 /*
773 ============
774 PS_VerifyIntegerVec
775 ============
776 */
PS_VerifyIntegerVec(char * token,int * target)777 static qBool PS_VerifyIntegerVec (char *token, int *target)
778 {
779 uint32 len, i;
780
781 len = strlen (token);
782 for (i=0 ; i<len ; i++) {
783 if (token[i] >= '0' || token[i] <= '9')
784 continue;
785 if (token[i] == '-' && i == 0)
786 continue;
787 break;
788 }
789 if (i != len)
790 return qFalse;
791
792 *target = atoi (token);
793 return qTrue;
794 }
795
796
797 /*
798 ============
799 PS_VerifyUIntegerVec
800 ============
801 */
PS_VerifyUIntegerVec(char * token,uint32 * target)802 static qBool PS_VerifyUIntegerVec (char *token, uint32 *target)
803 {
804 uint32 len, i;
805
806 len = strlen (token);
807 for (i=0 ; i<len ; i++) {
808 if (token[i] >= '0' || token[i] <= '9')
809 continue;
810 break;
811 }
812 if (i != len)
813 return qFalse;
814
815 *target = atoi (token);
816 return qTrue;
817 }
818
819
820 /*
821 ============
822 PS_ParseDataVector
823 ============
824 */
PS_ParseDataVector(char * token,uint32 dataType,void * target)825 static qBool PS_ParseDataVector (char *token, uint32 dataType, void *target)
826 {
827 switch (dataType) {
828 case PSDT_CHAR:
829 return PS_VerifyCharVec (token, (char *)target);
830 case PSDT_BOOLEAN:
831 return PS_VerifyBooleanVec (token, (qBool *)target);
832 case PSDT_BYTE:
833 return PS_VerifyByteVec (token, (byte *)target);
834 case PSDT_DOUBLE:
835 return PS_VerifyDoubleVec (token, (double *)target);
836 case PSDT_FLOAT:
837 return PS_VerifyFloatVec (token, (float *)target);
838 case PSDT_INTEGER:
839 return PS_VerifyIntegerVec (token, (int *)target);
840 case PSDT_UINTEGER:
841 return PS_VerifyUIntegerVec (token, (uint32 *)target);
842 }
843
844 return qFalse;
845 }
846
847
848 /*
849 ============
850 PS_ParseDataType
851 ============
852 */
PS_ParseDataType(parse_t * ps,uint32 flags,uint32 dataType,void * target,uint32 numVecs)853 qBool PS_ParseDataType (parse_t *ps, uint32 flags, uint32 dataType, void *target, uint32 numVecs)
854 {
855 char *token, *data;
856 uint32 len, i;
857 int c;
858
859 // Parse the next token
860 if (!PS_ParseToken (ps, flags, &token))
861 return qFalse;
862
863 // Individual tokens
864 // FIXME: support commas and () [] {} brackets
865 if (!strchr (token, ' ') && !strchr (token, ',')) {
866 for (i=0 ; i<numVecs ; i++) {
867 if (i) {
868 // Parse the next token
869 if (!PS_ParseToken (ps, flags, &token)) {
870 PS_PrintError (ps, "PARSE ERROR: vector missing parameters!\n");
871 return qFalse;
872 }
873
874 // Storage position
875 switch (dataType) {
876 case PSDT_CHAR: target = (char *)target+1; break;
877 case PSDT_BOOLEAN: target = (qBool *)target+1; break;
878 case PSDT_BYTE: target = (byte *)target+1; break;
879 case PSDT_DOUBLE: target = (double *)target+1; break;
880 case PSDT_FLOAT: target = (float *)target+1; break;
881 case PSDT_INTEGER: target = (int *)target+1; break;
882 case PSDT_UINTEGER: target = (uint32 *)target+1; break;
883 }
884 }
885
886 // Check the data type
887 if (!PS_ParseDataVector (token, dataType, target)) {
888 PS_PrintError (ps, "PARSE ERROR: does not evaluate to desired data type!\n");
889 return qFalse;
890 }
891 }
892
893 return qTrue;
894 }
895
896 // Single token with all vectors
897 // FIXME: support () [] {} brackets
898 data = token;
899 for (i=0 ; i<numVecs ; i++) {
900 ps_scratchToken[0] = '\0';
901 len = 0;
902
903 // Skip white-space
904 for ( ; ; ) {
905 c = *data++;
906 if (c <= ' ' || c == ',')
907 continue;
908 if (c == '\0')
909 break;
910 break;
911 }
912
913 // Parse this token into a sub-token stored in ps_scratchToken
914 for ( ; ; ) {
915 if (c <= ' ' || c == ',') {
916 data--;
917 break; // Stop at white space and commas
918 }
919
920 ps_scratchToken[len++] = c;
921 c = *data++;
922 }
923 ps_scratchToken[len++] = '\0';
924
925 // Too few vecs
926 if (!ps_scratchToken[0]) {
927 PS_PrintError (ps, "PARSE ERROR: missing vector parameters!\n");
928 return qFalse;
929 }
930
931 // Check the data type and set the target
932 if (!PS_ParseDataVector (ps_scratchToken, dataType, target)) {
933 PS_PrintError (ps, "PARSE ERROR: '%s' does not evaluate to desired data type %s!\n", ps_scratchToken, ps_dataTypeStr[dataType]);
934 return qFalse;
935 }
936
937 // Next storage position
938 switch (dataType) {
939 case PSDT_CHAR: target = (char *)target+1; break;
940 case PSDT_BOOLEAN: target = (qBool *)target+1; break;
941 case PSDT_BYTE: target = (byte *)target+1; break;
942 case PSDT_DOUBLE: target = (double *)target+1; break;
943 case PSDT_FLOAT: target = (float *)target+1; break;
944 case PSDT_INTEGER: target = (int *)target+1; break;
945 case PSDT_UINTEGER: target = (uint32 *)target+1; break;
946 }
947 }
948
949 // Check for too much data
950 for ( ; ; ) {
951 c = *data++;
952 if (c == '\0')
953 break;
954 if (c > ' ') {
955 PS_PrintError (ps, "PARSE ERROR: too many vector parameters!\n");
956 return qFalse;
957 }
958 }
959
960 return qTrue;
961 }
962