]> icculus.org git repositories - taylor/freespace2.git/blob - src/parse/parselo.cpp
added copyright header
[taylor/freespace2.git] / src / parse / parselo.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Source$
11  * $Revision$
12  * $Author$
13  * $Date$
14  *
15  * low level parse routines common to all types of parsers
16  *
17  * $Log$
18  * Revision 1.5  2002/06/09 04:41:25  relnev
19  * added copyright header
20  *
21  * Revision 1.4  2002/05/28 06:45:38  theoddone33
22  * Cleanup some stuff
23  *
24  * Revision 1.3  2002/05/28 06:28:20  theoddone33
25  * Filesystem mods, actually reads some data files now
26  *
27  * Revision 1.2  2002/05/07 03:16:48  theoddone33
28  * The Great Newline Fix
29  *
30  * Revision 1.1.1.1  2002/05/03 03:28:10  root
31  * Initial import.
32  *
33  * 
34  * 12    8/10/99 6:54p Dave
35  * Mad optimizations. Added paging to the nebula effect.
36  * 
37  * 11    6/02/99 2:22p Andsager
38  * Clean up warning.
39  * 
40  * 10    5/20/99 11:25a Andsager
41  * Added error checking for parsing table files.
42  * 
43  * 9     2/23/99 11:18a Andsager
44  * Localize launcher using strings.tbl
45  * 
46  * 8     2/03/99 6:06p Dave
47  * Groundwork for FS2 PXO usertracker support.  Gametracker support next.
48  * 
49  * 7     11/05/98 4:18p Dave
50  * First run nebula support. Beefed up localization a bit. Removed all
51  * conditional compiles for foreign versions. Modified mission file
52  * format.
53  * 
54  * 6     10/29/98 12:49p Dave
55  * Intermediate checkin for Fred hash table stuff. 
56  * 
57  * 5     10/28/98 11:30a Dave
58  * Temporary checkin
59  * 
60  * 4     10/22/98 6:14p Dave
61  * Optimized some #includes in Anim folder. Put in the beginnings of
62  * parse/localization support for externalized strings and tstrings.tbl
63  * 
64  * 3     10/14/98 1:15p Andsager
65  * Fix fred
66  * 
67  * 2     10/07/98 10:53a Dave
68  * Initial checkin.
69  * 
70  * 1     10/07/98 10:50a Dave
71  * 
72  * 98    9/21/98 2:37p Adam
73  * fixed font translation bug
74  * 
75  * 97    7/29/98 9:39a Hoffoss
76  * Fixed bug with stuff_and_malloc_string
77  * 
78  * 96    6/22/98 11:18a Hoffoss
79  * Fixed bug where mission notes field gets an extra linefeed with each
80  * save.
81  * 
82  * 95    6/19/98 3:53p Lawrance
83  * account for some new foreign chars
84  * 
85  * 94    6/12/98 7:37p Hoffoss
86  * Made ß translate to ss, since we don't have this character in our font.
87  * 
88  * 93    6/12/98 4:52p Hoffoss
89  * Added support for special characters in in forgeign languages.
90  * 
91  * 92    5/21/98 3:26p Dave
92  * Fixed bug in new split string function.  Newline as last character did
93  * strange things.
94  * 
95  * 91    5/21/98 3:01p Dave
96  * Make sure split_str handles 0 length strings correctly.
97  * 
98  * 90    5/21/98 2:14a Hoffoss
99  * Rewrote split_str() to solve a problem and it was too hard for me to
100  * figure out (oow, my head hurts still!)
101  * 
102  * $NoKeywords: $
103 */
104
105 #include        <stdio.h>
106 #include <stdlib.h>
107 #include <string.h>
108 #include <assert.h>
109 #include <stdarg.h>
110 #include <setjmp.h>
111
112 #include "pstypes.h"
113 #include "parselo.h"
114 #include "sexp.h"
115 #include "cfile.h"
116 #include "missionparse.h"
117 #include "ctype.h"
118 #include "encrypt.h"
119 #include "localize.h"
120 #include "fhash.h"
121
122 #define ERROR_LENGTH    64
123 #define RS_MAX_TRIES    5
124
125 char            Current_filename[128];
126 char            Error_str[ERROR_LENGTH];
127 int             my_errno;
128 int             Warning_count, Error_count;
129 int             fred_parse_flag = 0;
130 int             Token_found_flag;
131 jmp_buf parse_abort;
132
133 char    Mission_text[MISSION_TEXT_SIZE];
134 char    Mission_text_raw[MISSION_TEXT_SIZE];
135 char    *Mp;
136 char    *token_found;
137
138 //      Return true if this character is white space, else false.
139 int is_white_space(char ch)
140 {
141         return ((ch == ' ') || (ch == '\t') || (ch == EOLN));
142 }
143
144 // Returns true if this character is gray space, else false (gray space is white space except for EOLN).
145 int is_gray_space(char ch)
146 {
147         return ((ch == ' ') || (ch == '\t'));
148 }
149
150 //      Advance global Mp (mission pointer) past all current white space.
151 //      Leaves Mp pointing at first non white space character.
152 void ignore_white_space()
153 {
154         while ((*Mp != EOF_CHAR) && is_white_space(*Mp))
155                 Mp++;
156 }
157
158 void ignore_gray_space()
159 {
160         while ((*Mp != EOF_CHAR) && is_gray_space(*Mp))
161                 Mp++;
162 }
163
164 //      Truncate *str, eliminating all trailing white space.
165 //      Eg: "abc   "   becomes "abc"
166 //               "abc abc " becomes "abc abc"
167 //               "abc \t"   becomes "abc"
168 void drop_trailing_white_space(char *str)
169 {
170         int     i;
171
172         i = strlen(str) - 1;
173
174         while ((i >= 0) && is_white_space(str[i]))
175                 i--;
176
177         str[i+1] = 0;
178 }
179
180 //      Eliminate any leading whitespace in str
181 void drop_leading_white_space(char *str)
182 {
183         int len, i;
184
185         len = strlen(str);
186         i = 0;
187
188         while ((i < len) && is_white_space(str[i]))
189                 i++;
190
191         memmove(str, str+i, len-i);
192         str[len-i] = 0;
193 }
194
195 // eliminates all leading and trailing white space from a string.  Returns pointer passed in.
196 char *drop_white_space(char *str)
197 {
198         int s, e;
199
200         s = 0;
201         while (str[s] && is_white_space(str[s]))
202                 s++;
203
204         e = strlen(str) - 1;
205         while (e > s) {
206                 if (!is_white_space(str[e]))
207                         break;
208
209                 e--;
210         }
211
212         if (e > s)
213                 memmove(str, str + s, e - s + 1);
214
215         str[e - s + 1] = 0;
216         return str;
217 }
218
219 //      Advances Mp past current token.
220 void skip_token()
221 {
222         ignore_white_space();
223
224         while ((*Mp != EOF_CHAR) && !is_white_space(*Mp))
225                 Mp++;
226 }
227
228 //      Display a diagnostic message if Verbose is set.
229 //      (Verbose is set if -v command line switch is present.)
230 void diag_printf(char *format, ...)
231 {
232         char    buffer[8192];
233         va_list args;
234
235         va_start(args, format);
236         vsprintf(buffer, format, args);
237         va_end(args);
238
239         nprintf(("Parse", "%s", buffer));
240 }
241
242 //      Grab and return (a pointer to) a bunch of tokens, terminating at
243 // ERROR_LENGTH chars, or end of line.
244 char *next_tokens()
245 {
246         int     count = 0;
247         char    *pstr = Mp;
248         char    ch;
249
250         while (((ch = *pstr++) != EOLN) && (ch != EOF_CHAR) && (count < ERROR_LENGTH-1))
251                 Error_str[count++] = ch;
252
253         Error_str[count] = 0;
254         return Error_str;
255 }
256
257 //      Return the line number given by the current mission pointer, ie Mp.
258 //      A very slow function (scans all processed text), but who cares how long
259 //      an error reporting function takes?
260 int get_line_num()
261 {
262         int     count = 1;
263         int     incomment = 0;
264         int     multiline = 0;
265         char    *stoploc;
266         char    *p;
267
268         p = Mission_text_raw;
269         stoploc = (Mp - Mission_text) + p;
270
271         while (p < stoploc)
272         {
273                 if (*p == EOF_CHAR)
274                         Assert(0);
275
276                 if ( !incomment && (*p == COMMENT_CHAR) )
277                         incomment = 1;
278
279                 if ( !incomment && (*p == '/') && (*(p+1) == '*') ) {
280                         multiline = 1;
281                         incomment = 1;
282                 }
283
284                 if ( incomment )
285                         stoploc++;
286
287                 if ( multiline && (*(p-1) == '*') && (*p == '/') ) {
288                         multiline = 0;
289                         incomment = 0;
290                 }
291
292                 if (*p++ == EOLN) {
293                         if ( !multiline && incomment )
294                                 incomment = 0;
295                         count++;
296                 }
297         }
298
299         return count;
300 }
301
302 //      Call this function to display an error message.
303 //      error_level == 0 means this is just a warning.
304 //      !0 means it's an error message.
305 //      Prints line number and other useful information.
306 void error_display(int error_level, char *format, ...)
307 {
308         char    buffer[1024];
309         char    error_text[128];
310         va_list args;
311
312         if (error_level == 0) {
313                 strcpy(error_text, "Warning");
314                 Warning_count++;
315         } else {
316                 strcpy(error_text, "Error");
317                 Error_count++;
318         }
319
320         nprintf((error_text, "%s(%i):%s: ", Current_filename, get_line_num(), error_text));
321
322         va_start(args, format);
323         vsprintf(buffer, format, args);
324         va_end(args);
325         Assert(strlen(buffer) < 1024);
326
327         nprintf((error_text, "%s", buffer));
328         Warning(LOCATION, "%s(%i):\n%s: %s", Current_filename, get_line_num(), error_text, buffer);
329 }
330
331 //      Advance Mp to the next eoln character.
332 void advance_to_eoln(char *more_terminators)
333 {
334         char    terminators[128];
335
336         Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
337
338         terminators[0] = EOLN;
339         terminators[1] = (char)EOF_CHAR;
340         if (more_terminators != NULL)
341                 strcpy(&terminators[2], more_terminators);
342         else
343                 terminators[2] = 0;
344
345         while (strchr(terminators, *Mp) == NULL)
346                 Mp++;
347 }
348
349 // Advance Mp to the next white space (ignoring white space inside of " marks)
350 void advance_to_next_white()
351 {
352         int in_quotes = 0;
353
354         while ((*Mp != EOLN) && (*Mp != EOF_CHAR)) {
355                 if (*Mp == '\"')
356                         in_quotes = !in_quotes;
357
358                 if (!in_quotes && is_white_space(*Mp))
359                         break;
360                 Mp++;
361         }
362 }
363
364 // Search for specified string, skipping everything up to that point.  Returns 1 if found,
365 // 0 if string wasn't found (and hit end of file), or -1 if not found, but end of checking
366 // block was reached.
367 int skip_to_string(char *pstr, char *end)
368 {
369         int len, len2 = 0;
370
371         ignore_white_space();
372         len = strlen(pstr);
373         if (end)
374                 len2 = strlen(end);
375
376         while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, len)) {
377                 if (end && *Mp == '#')
378                         return 0;
379
380                 if (end && !strnicmp(pstr, Mp, len2))
381                         return -1;
382
383                 advance_to_eoln(NULL);
384                 ignore_white_space();
385         }
386
387         if (!Mp || (*Mp == EOF_CHAR))
388                 return 0;
389
390         Mp += strlen(pstr);
391         return 1;
392 }
393
394 // Advance to start of either pstr1 or pstr2.  Return 0 is successful, otherwise return !0
395 int skip_to_start_of_strings(char *pstr1, char *pstr2)
396 {
397         int len1, len2;
398
399         ignore_white_space();
400         len1 = strlen(pstr1);
401         len2 = strlen(pstr2);
402
403         while ( (*Mp != EOF_CHAR) && strnicmp(pstr1, Mp, len1) && strnicmp(pstr2, Mp, len2) ) {
404                 advance_to_eoln(NULL);
405                 ignore_white_space();
406         }
407
408         if (!Mp || (*Mp == EOF_CHAR))
409                 return 0;
410
411         return 1;
412 }
413
414 // Find a required string.
415 // If not found, display an error message, but try up to RS_MAX_TRIES times
416 // to find the string.  (This is the groundwork for ignoring non-understood
417 // lines.
418 //      If unable to find the required string after RS_MAX_TRIES tries, then
419 //      abort using longjmp to parse_abort.
420 int required_string(char *pstr)
421 {
422         int     count = 0;
423
424         ignore_white_space();
425
426         while (strnicmp(pstr, Mp, strlen(pstr)) && (count < RS_MAX_TRIES)) {
427                 error_display(1, "Required token = [%s], found [%.32s].\n", pstr, next_tokens());
428                 advance_to_eoln(NULL);
429                 ignore_white_space();
430                 count++;
431         }
432
433         if (count == RS_MAX_TRIES) {
434                 nprintf(("Error", "Error: Unable to find required token [%s]\n", pstr));
435                 Warning(LOCATION, "Error: Unable to find required token [%s]\n", pstr);
436                 longjmp(parse_abort, 1);
437         }
438
439         Mp += strlen(pstr);
440         diag_printf("Found required string [%s]\n", token_found = pstr);
441         return 1;
442 }
443
444 // similar to optional_string, but just checks if next token is a match.
445 // It doesn't advance Mp.
446 //
447 int check_for_string(char *pstr)
448 {
449         ignore_white_space();
450
451         if (!strnicmp(pstr, Mp, strlen(pstr)))
452                 return 1;
453
454         return 0;
455 }
456
457 // like check for string, but doesn't skip past any whitespace
458 int check_for_string_raw(char *pstr)
459 {
460         if (!strnicmp(pstr, Mp, strlen(pstr))){
461                 return 1;
462         }
463
464         return 0;
465 }
466
467 // Find an optional string.
468 //      If found, return 1, else return 0.
469 //      If found, point past string, else don't update pointer.
470 int optional_string(char *pstr)
471 {
472         ignore_white_space();
473
474         if (!strnicmp(pstr, Mp, strlen(pstr))) {
475                 Mp += strlen(pstr);
476                 return 1;
477         }
478
479         return 0;
480 }
481
482 int required_string_fred(char *pstr, char *end)
483 {
484         char *backup = Mp;;
485
486         token_found = pstr;
487         if (fred_parse_flag)
488                 return 0;
489
490         ignore_white_space();
491         while (*Mp != EOF_CHAR && strnicmp(pstr, Mp, strlen(pstr))) {
492                 if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end)))) {
493                         Mp = NULL;
494                         break;
495                 }
496
497                 advance_to_eoln(NULL);
498                 ignore_white_space();
499         }
500
501         if (!Mp || (*Mp == EOF_CHAR)) {
502                 diag_printf("Required string [%s] not found\n", pstr);
503                 Mp = backup;
504                 Token_found_flag = 0;
505                 return 0;
506         }
507
508         Mp += strlen(pstr);
509         diag_printf("Found required string [%s]\n", pstr);
510         Token_found_flag = 1;
511         return 1;
512 }
513
514 // attempt to find token in buffer.  It might not exist, however, in which case we don't need
515 // to do anything.  If it is found, then we advance the pointer to just after the token.  To
516 // further complicate things, we should only search to a certain point, since we don't want
517 // a token that belongs to another section which might match the token we want.  Thus, we
518 // also pass in an ending token, which marks the point we should stop looking at.
519 int optional_string_fred(char *pstr, char *end, char *end2)
520 {
521         char *mp_save = Mp;
522
523         token_found = pstr;
524         if (fred_parse_flag)
525                 return 0;
526
527         ignore_white_space();
528         while ((*Mp != EOF_CHAR) && strnicmp(pstr, Mp, strlen(pstr))) {
529                 if ((*Mp == '#') || (end && !strnicmp(end, Mp, strlen(end))) ||
530                         (end2 && !strnicmp(end2, Mp, strlen(end2)))) {
531                         Mp = NULL;
532                         break;
533                 }
534
535                 advance_to_eoln(NULL);
536                 ignore_white_space();
537         }
538
539         if (!Mp || (*Mp == EOF_CHAR)) {
540                 diag_printf("Optional string [%s] not found\n", pstr);
541                 Mp = mp_save;
542                 Token_found_flag = 0;
543                 return 0;
544         }
545
546         Mp += strlen(pstr);
547         diag_printf("Found optional string [%s]\n", pstr);
548         Token_found_flag = 1;
549         return 1;
550 }
551
552 //      Return 0 or 1 for str1 match, str2 match.  Return -1 if neither matches.
553 //      Does not update Mp if token found.  If not found, advances, trying to
554 //      find the string.  Doesn't advance past the found string.
555 int required_string_either(char *str1, char *str2)
556 {
557         int     count = 0;
558
559         ignore_white_space();
560
561         while (count < RS_MAX_TRIES) {
562                 if (strnicmp(str1, Mp, strlen(str1)) == 0) {
563                         // Mp += strlen(str1);
564                         diag_printf("Found required string [%s]\n", token_found = str1);
565                         return 0;
566                 } else if (strnicmp(str2, Mp, strlen(str2)) == 0) {
567                         // Mp += strlen(str2);
568                         diag_printf("Found required string [%s]\n", token_found = str2);
569                         return 1;
570                 }
571
572                 error_display(1, "Required token = [%s] or [%s], found [%.32s].\n", str1, str2, next_tokens());
573
574                 advance_to_eoln(NULL);
575                 ignore_white_space();
576                 count++;
577         }
578
579         if (count == RS_MAX_TRIES) {
580                 nprintf(("Error", "Error: Unable to find either required token [%s] or [%s]\n", str1, str2));
581                 Warning(LOCATION, "Error: Unable to find either required token [%s] or [%s]\n", str1, str2);
582                 longjmp(parse_abort, 2);
583         }
584
585         return -1;
586         // exit (1);
587 }
588
589 //      Return 0 or 1 for str1 match, str2 match.  Return -1 if neither matches.
590 //      Does not update Mp if token found.  If not found, advances, trying to
591 //      find the string.  Doesn't advance past the found string.
592 int required_string_3(char *str1, char *str2, char *str3)
593 {
594         int     count = 0;
595
596         ignore_white_space();
597
598         while (count < RS_MAX_TRIES) {
599                 if (strnicmp(str1, Mp, strlen(str1)) == 0) {
600                         // Mp += strlen(str1);
601                         diag_printf("Found required string [%s]\n", token_found = str1);
602                         return 0;
603                 } else if (strnicmp(str2, Mp, strlen(str2)) == 0) {
604                         // Mp += strlen(str2);
605                         diag_printf("Found required string [%s]\n", token_found = str2);
606                         return 1;
607                 } else if (strnicmp(str3, Mp, strlen(str3)) == 0) {
608                         diag_printf("Found required string [%s]\n", token_found = str3);
609                         return 2;
610                 }
611
612                 error_display(1, "Required token = [%s], [%s] or [%s], found [%.32s].\n", str1, str2, str3, next_tokens());
613
614                 advance_to_eoln(NULL);
615                 ignore_white_space();
616                 count++;
617         }
618
619         return -1;
620         // exit (1);
621 }
622
623 int required_string_either_fred(char *str1, char *str2)
624 {
625         ignore_white_space();
626
627         while (*Mp != EOF_CHAR) {
628                 if (!strnicmp(str1, Mp, strlen(str1))) {
629                         // Mp += strlen(str1);
630                         diag_printf("Found required string [%s]\n", token_found = str1);
631                         return fred_parse_flag = 0;
632                 
633                 } else if (!strnicmp(str2, Mp, strlen(str2))) {
634                         // Mp += strlen(str2);
635                         diag_printf("Found required string [%s]\n", token_found = str2);
636                         return fred_parse_flag = 1;
637                 }
638
639                 advance_to_eoln(NULL);
640                 ignore_white_space();
641         }
642
643         if (*Mp == EOF_CHAR)
644                 diag_printf("Unable to find either required token [%s] or [%s]\n", str1, str2);
645
646         return -1;
647         // exit (1);
648 }
649
650 //      Copy characters from instr to outstr until eoln is found, or until max
651 //      characters have been copied (including terminator).
652 void copy_to_eoln(char *outstr, char *more_terminators, char *instr, int max)
653 {
654         int     count = 0;
655         char    ch;
656         char    terminators[128];
657
658         Assert((more_terminators == NULL) || (strlen(more_terminators) < 125));
659
660         terminators[0] = EOLN;
661         terminators[1] = (char)EOF_CHAR;
662         if (more_terminators != NULL)
663                 strcpy(&terminators[2], more_terminators);
664         else
665                 terminators[2] = 0;
666
667         while (((ch = *instr++) != 0) && (strchr(terminators, ch) == NULL)  && (count < max)) {
668                 *outstr++ = ch;
669                 count++;
670         }
671
672         if (count == max)
673                 error_display(0, "Token too long: [%s].  Length = %i.  Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
674
675         *outstr = 0;
676 }
677
678 //      Copy characters from instr to outstr until next white space is found, or until max
679 //      characters have been copied (including terminator).
680 void copy_to_next_white(char *outstr, char *instr, int max)
681 {
682         int     count = 0;
683         int     in_quotes = 0;
684         char    ch;
685
686         while (((ch = *instr++)>0) && (ch != EOLN) && (ch != EOF_CHAR) && (count < max)) {
687                 if ( ch == '\"' ) {
688                         in_quotes = !in_quotes;
689                         continue;
690                 }
691                 if ( !in_quotes && is_white_space(ch) ) // not in quotes, white space terminates string
692                         break;
693                 *outstr++ = ch;
694                 count++;
695         }
696
697         if (count == max)
698                 error_display(0, "Token too long: [%s].  Length = %i.  Max is %i.\n", next_tokens(), strlen(next_tokens()), max);
699
700         *outstr = 0;
701 }
702
703 //      Copy text until a certain string is matched.
704 //      For example, this is used to copy mission notes, scanning until $END NOTES:
705 // is found.
706 void copy_text_until(char *outstr, char *instr, char *endstr, int max_chars)
707 {
708         char *foundstr;
709
710         foundstr = strstr(instr, endstr);
711
712         if (foundstr == NULL) {
713                 nprintf(("Error", "Error.  Looking for [%s], but never found it.\n", endstr));
714                 longjmp(parse_abort, 3);
715         }
716
717         if (foundstr - instr + strlen(endstr) < (uint) max_chars) {
718                 strncpy(outstr, instr, foundstr - instr);
719                 outstr[foundstr - instr] = 0;
720
721         } else {
722                 nprintf(("Error", "Error.  Too much text (%i chars, %i allowed) before %s\n",
723                         foundstr - instr - strlen(endstr), max_chars, endstr));
724
725                 longjmp(parse_abort, 4);
726         }
727
728         diag_printf("Here's the partial wad of text:\n%.30s", outstr);
729 }
730
731 // stuffs a string into a buffer.  Can get a string between " marks and stops
732 // when whitespace in encounted -- not to end of line
733 void stuff_string_white(char *pstr)
734 {
735         ignore_white_space();
736         copy_to_next_white(pstr, Mp, NAME_LENGTH-1);
737         advance_to_next_white();
738 }
739
740 //      Stuff a string into a string buffer.
741 //      Supports various FreeSpace primitive types.  If 'len' is supplied, it will override
742 // the default string length if using the F_NAME case.
743 void stuff_string(char *pstr, int type, char *terminators, int len)
744 {       
745         char read_str[2048] = "";
746         int read_len = 2048;    
747         int final_len = len;
748         int tag_id;
749
750         switch (type) {
751                 case F_NAME:
752                         if (!len){
753                                 final_len = NAME_LENGTH;
754                         }
755                         ignore_gray_space();
756                         copy_to_eoln(read_str, terminators, Mp, read_len);
757                         drop_trailing_white_space(read_str);
758                         advance_to_eoln(terminators);
759                         break;
760
761                 case F_DATE:
762                         ignore_gray_space();                    
763                         final_len = DATE_LENGTH;
764                         copy_to_eoln(read_str, terminators, Mp, read_len);
765                         drop_trailing_white_space(read_str);
766                         advance_to_eoln(terminators);
767                         break;
768
769                 case F_NOTES:
770                         ignore_white_space();
771                         final_len = NOTES_LENGTH;
772                         copy_text_until(read_str, Mp, "$End Notes:", read_len);
773                         Mp += strlen(read_str);
774                         required_string("$End Notes:");
775                         break;
776
777                 case F_FILESPEC:
778                         ignore_gray_space();
779                         final_len = FILESPEC_LENGTH;
780                         copy_to_eoln(read_str, terminators, Mp, read_len);
781                         drop_trailing_white_space(read_str);
782                         advance_to_eoln(terminators);
783                         break;
784                 // F_MULTITEXTOLD keeping for backwards compatability with old missions
785                 // can be deleted once all missions are using new briefing format
786
787                 case F_MULTITEXTOLD:            
788                         ignore_white_space();
789                         final_len = NOTES_LENGTH;
790                         copy_text_until(read_str, Mp, "$End Briefing Text:", read_len);
791                         Mp += strlen(read_str);
792                         required_string("$End Briefing Text:");
793                         break;
794
795                 case F_MULTITEXT:               
796                         if (!len){
797                                 final_len = MULTITEXT_LENGTH;
798                         }
799                         ignore_white_space();
800                         copy_text_until(read_str, Mp, "$end_multi_text", read_len);
801                         Mp += strlen(read_str);
802                         drop_trailing_white_space(read_str);
803                         required_string("$end_multi_text");
804                         break;
805
806                 case F_PATHNAME:
807                         ignore_gray_space();
808                         final_len = PATHNAME_LENGTH;
809                         copy_to_eoln(read_str, terminators, Mp, read_len);
810                         drop_trailing_white_space(read_str);
811                         advance_to_eoln(terminators);
812                         break;
813
814                 case F_MESSAGE:
815                         ignore_gray_space();
816                         final_len = MESSAGE_LENGTH;
817                         copy_to_eoln(read_str, terminators, Mp, read_len);
818                         drop_trailing_white_space(read_str);
819                         advance_to_eoln(terminators);
820                         break;          
821
822                 default:
823                         Assert(0);
824         }
825
826         // now we want to do any final localization
827         lcl_ext_localize(read_str, pstr, final_len, &tag_id);
828
829         // if the hash localized text hash table is active and we have a valid external string - hash it
830         if(fhash_active() && (tag_id > -2)){
831                 fhash_add_str(pstr, tag_id);
832         }
833
834         diag_printf("Stuffed string = [%.30s]\n", pstr);
835 }
836
837 // stuff a string, but only until the end of a line. don't ignore leading whitespace. close analog of fgets()/cfgets()
838 void stuff_string_line(char *pstr, int len)
839 {
840         char read_str[2048] = "";
841         int read_len = 2048;    
842         int final_len = len;
843         int tag_id;
844         
845         // read in a line
846         final_len = len;
847         copy_to_eoln(read_str, "\n", Mp, read_len);
848         drop_trailing_white_space(read_str);
849         advance_to_eoln("");
850         Mp++;
851                         
852         // now we want to do any final localization
853         lcl_ext_localize(read_str, pstr, final_len, &tag_id);
854
855         // if the hash localized text hash table is active and we have a valid external string - hash it
856         if(fhash_active() && (tag_id > -2)){
857                 fhash_add_str(pstr, tag_id);
858         }
859
860         diag_printf("Stuffed string = [%.30s]\n", pstr);
861 }
862
863 // 1K on the stack? seems to work...
864 // JH: 1k isn't enough!  Command briefs can be 16k max, so changed this.
865 #define MAX_TMP_STRING_LENGTH 16384
866
867 // Exactly the same as stuff string only Malloc's the buffer. 
868 //      Supports various FreeSpace primitive types.  If 'len' is supplied, it will override
869 // the default string length if using the F_NAME case.
870 char *stuff_and_malloc_string( int type, char *terminators, int len)
871 {
872         int l;
873
874         char tmp_result[MAX_TMP_STRING_LENGTH];
875
876         stuff_string(tmp_result, type, terminators, len);
877         drop_white_space(tmp_result);
878
879         l = strlen(tmp_result);
880         Assert(l < MAX_TMP_STRING_LENGTH);              // Get John!!
881         if (l < 1)
882                 return NULL;
883
884         return strdup(tmp_result);
885 }
886
887 // After reading a multitext string, you can call this function to convert any newlines into
888 // spaces, so it's a one paragraph string (I.e. as in MS-Word).
889 //
890 void compact_multitext_string(char *str)
891 {
892         unsigned int i;
893
894         for (i=0; i<strlen(str); i++)
895                 if (str[i] == '\n')
896                         str[i] = ' ';
897 }
898
899 #define BUF_SIZE 2048
900
901 // Strip comments from a line of input.
902 int strip_comments_fred(char *readp, int in_comment)
903 {       
904         int     ch;
905         char    *writep = readp;
906
907         while ((ch = *readp) != COMMENT_CHAR) {
908                 if (*readp == 0) {
909                         *writep = 0;
910                         return in_comment;
911                 }
912
913                 if (!in_comment) {                              
914                         *writep = (char)ch;
915                         writep++;
916                 }
917                 
918                 readp++;                
919         }
920
921         *writep = EOLN;
922         writep[1] = 0;
923         
924         return in_comment;      
925 }
926
927
928 // Strip comments from a line of input.
929 int strip_comments(char *readp, int in_comment)
930 {       
931         int     ch;
932         char    *writep = readp;
933
934         while ((ch = *readp) != COMMENT_CHAR) {
935                 if (*readp == 0) {
936                         *writep = 0;
937                         return in_comment;
938                 }
939
940                 if (!in_comment) {
941                         // time to do some special foreign character conversion                 
942                         switch (ch) {
943                                 case -4:
944                                         ch = 129;
945                                         break;
946
947                                 case -28:
948                                         ch = 132;
949                                         break;
950
951                                 case -10:
952                                         ch = 148;
953                                         break;
954
955                                 case -23:
956                                         ch = 130;
957                                         break;
958
959                                 case -30:
960                                         ch = 131;
961                                         break;
962
963                                 case -25:
964                                         ch = 135;
965                                         break;
966
967                                 case -21:
968                                         ch = 137;
969                                         break;
970
971                                 case -24:
972                                         ch = 138;
973                                         break;
974
975                                 case -17:
976                                         ch = 139;
977                                         break;
978
979                                 case -18:
980                                         ch = 140;
981                                         break;
982
983                                 case -60:
984                                         ch = 142;
985                                         break;
986
987                                 case -55:
988                                         ch = 144;
989                                         break;
990
991                                 case -12:
992                                         ch = 147;
993                                         break;
994
995                                 case -14:
996                                         ch = 149;
997                                         break;
998
999                                 case -5:
1000                                         ch = 150;
1001                                         break;
1002
1003                                 case -7:
1004                                         ch = 151;
1005                                         break;
1006
1007                                 case -42:
1008                                         ch = 153;
1009                                         break;
1010
1011                                 case -36:
1012                                         ch = 154;
1013                                         break;
1014
1015                                 case -31:
1016                                         ch = 160;
1017                                         break;
1018
1019                                 case -19:
1020                                         ch = 161;
1021                                         break;
1022
1023                                 case -13:
1024                                         ch = 162;
1025                                         break;
1026
1027                                 case -6:
1028                                         ch = 163;
1029                                         break;
1030
1031                                 case -32:
1032                                         ch = 133;
1033                                         break;
1034
1035                                 case -22:
1036                                         ch = 136;
1037                                         break;
1038
1039                                 case -20:
1040                                         ch = 141;
1041                                         break;
1042                         }                       
1043
1044                         *writep = (char)ch;
1045                         writep++;
1046                 }
1047                 
1048                 readp++;        
1049         }
1050
1051         *writep = EOLN;
1052         writep[1] = 0;
1053         
1054         return in_comment;      
1055 }
1056
1057 #if 0
1058 void strip_all_comments( char *readp, char *writep )
1059 {
1060         int     ch;
1061         //char  *writep = readp;
1062
1063         while ( *readp != EOF_CHAR ) {
1064                 ch = *readp;
1065                 if ( ch == COMMENT_CHAR ) {
1066                         while ( *readp != EOLN )
1067                                 readp++;
1068
1069                         *writep = EOLN;
1070                         writep++;
1071                         // get to next character after EOLN
1072                         readp++;
1073                 } else if ( (ch == '/') && (readp[1] == '*')) {                 // Start of multi-line comment
1074                         int done;
1075                         
1076                         done = 0;
1077                         while ( !done ) {
1078                                 while ( *readp != '*' )
1079                                         readp++;
1080                                 if ( readp[1] == '/' ) {
1081                                         readp += 2;
1082                                         done = 1;
1083                                 } else {
1084                                         readp++;
1085                                 }
1086                         }
1087                 } else {
1088                         *writep = (char)ch;
1089                         *writep++;
1090                         readp++;
1091                 }
1092         }
1093
1094         *writep = (char)EOF_CHAR;
1095 }
1096 #endif
1097
1098 int parse_get_line(char *lineout, int max_line_len, char *start, int max_size, char *cur)
1099 {
1100         char * t = lineout;
1101         int i, num_chars_read=0;
1102         char c;
1103
1104
1105         for ( i = 0; i < max_line_len-1; i++ ) {
1106                 do {
1107                         if ( (cur - start) >= max_size ) {
1108                                 *lineout = 0;
1109                                 if ( lineout > t ) {            
1110                                         return num_chars_read;
1111                                 } else {
1112                                         return 0;
1113                                 }
1114                         }
1115                         c = *cur++;
1116                         num_chars_read++;
1117                 } while ( c == 13 );
1118
1119                 *lineout++ = c;
1120                 if ( c=='\n' ) break;
1121         }
1122
1123         *lineout++ = 0;
1124         return  num_chars_read;
1125 }
1126
1127 //      Read mission text, stripping comments.
1128 //      When a comment is found, it is removed.  If an entire line
1129 //      consisted of a comment, a blank line is left in the input file.
1130 void read_file_text(char *filename, int mode)
1131 {
1132         CFILE   *mf;
1133         char    outbuf[BUF_SIZE], *str;
1134         char    *mp = Mission_text;
1135         char    *mp2 = Mission_text_raw;
1136         int     file_is_encrypted = 0, in_comment = 0;
1137
1138         if (!filename)
1139                 longjmp(parse_abort, 10);
1140
1141         strcpy(Current_filename, filename);
1142         mf = cfopen(filename, "rb", CFILE_NORMAL, mode);
1143         if (mf == NULL) {
1144                 nprintf(("Error", "Wokka!  Error opening mission.txt!\n"));
1145                 longjmp(parse_abort, 5);
1146         }
1147
1148         // read the entire file in
1149         int file_len = cfilelength(mf);
1150
1151         // read first 10 bytes to determine if file is encrypted
1152         cfread(Mission_text_raw, min(file_len, 10), 1, mf);
1153         file_is_encrypted = is_encrpyted(Mission_text_raw);
1154         cfseek(mf, 0, CF_SEEK_SET);
1155
1156         if ( file_is_encrypted ) {
1157                 int     unscrambled_len;
1158                 char    *scrambled_text;
1159                 scrambled_text = (char*)malloc(file_len+1);
1160                 Assert(scrambled_text);
1161                 cfread(scrambled_text, file_len, 1, mf);
1162                 // unscramble text
1163                 unencrypt(scrambled_text, file_len, Mission_text_raw, &unscrambled_len);
1164                 file_len = unscrambled_len;
1165                 free(scrambled_text);
1166         } else {
1167                 cfread(Mission_text_raw, file_len, 1, mf);
1168         }
1169
1170         cfclose(mf);
1171
1172         //      If you hit this assert, it is probably telling you the obvious.  The file
1173         //      you are trying to read is truly too large.  Look at *filename to see the file name.
1174         Assert(file_len < MISSION_TEXT_SIZE);
1175
1176         // strip comments from raw text, reading into Mission_text
1177
1178         int num_chars_read = 0;
1179
1180         mp2 = Mission_text_raw;
1181         while ( (num_chars_read = parse_get_line(outbuf, BUF_SIZE, Mission_text_raw, file_len, mp2)) != 0 ) {
1182                 mp2 += num_chars_read;
1183
1184                 if(Fred_running){
1185                         in_comment = strip_comments_fred(outbuf, in_comment);
1186                 } else {
1187                         in_comment = strip_comments(outbuf, in_comment);
1188                 }
1189                 str = outbuf;
1190                 while (*str) {
1191                         if (*str == -33) {
1192                                 *mp++ = 's';
1193                                 *mp++ = 's';
1194                                 str++;
1195
1196                         } else
1197                                 *mp++ = *str++;
1198                 }
1199
1200 //              strcpy(mp, outbuf);
1201 //              mp += strlen(outbuf);
1202         }
1203         
1204         *mp = *mp2 = (char)EOF_CHAR;
1205 /*
1206         while (cfgets(outbuf, BUF_SIZE, mf) != NULL) {
1207                 if (strlen(outbuf) >= BUF_SIZE-1)
1208                         error_display(0, "Input string too long.  Max is %i characters.\n%.256s\n", BUF_SIZE, outbuf);
1209
1210                 //      If you hit this assert, it is probably telling you the obvious.  The file
1211                 //      you are trying to read is truly too large.  Look at *filename to see the file name.
1212                 Assert(mp2 - Mission_text_raw + strlen(outbuf) < MISSION_TEXT_SIZE);
1213                 strcpy(mp2, outbuf);
1214                 mp2 += strlen(outbuf);
1215
1216                 in_comment = strip_comments(outbuf, in_comment);
1217                 strcpy(mp, outbuf);
1218                 mp += strlen(outbuf);
1219         }
1220         
1221         *mp = *mp2 = (char)EOF_CHAR;
1222 */
1223
1224 }
1225
1226 void debug_show_mission_text()
1227 {
1228         char    *mp = Mission_text;
1229         char    ch;
1230
1231         while ((ch = *mp++) != EOF_CHAR)
1232                 printf("%c", ch);
1233 }
1234
1235 float atof2()
1236 {
1237         char    ch;
1238
1239         my_errno = 0;
1240         ignore_white_space();
1241
1242         ch = *Mp;
1243
1244         if ((ch != '.') && (ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1245                 error_display(1, "Expecting float, found [%.32s].\n", next_tokens());
1246                 my_errno = 1;
1247                 return 0.0f;
1248         } else
1249                 return (float)atof(Mp);
1250
1251 }
1252
1253 int atoi2()
1254 {
1255         char    ch;
1256
1257         my_errno = 0;
1258
1259         ignore_white_space();
1260
1261         ch = *Mp;
1262
1263         if ((ch != '-') && (ch != '+') && ((ch < '0') || (ch > '9'))) {
1264                 error_display(1, "Expecting int, found [%.32s].\n", next_tokens());
1265                 my_errno = 1;
1266                 return 0;
1267         } else
1268                 return atoi(Mp);
1269
1270 }
1271
1272
1273 //      Stuff a floating point value pointed at by Mp.
1274 //      Advances past float characters.
1275 void stuff_float(float *f)
1276 {
1277         *f = atof2();
1278
1279         if (my_errno)
1280                 skip_token();
1281         else
1282                 Mp += strspn(Mp, "+-0123456789.");
1283
1284         if (*Mp ==',')
1285                 Mp++;
1286
1287         diag_printf("Stuffed float: %f\n", *f);
1288 }
1289
1290 //      Stuff an integer value pointed at by Mp.
1291 //      Advances past integer characters.
1292 void stuff_int(int *i)
1293 {
1294         *i = atoi2();
1295
1296         if (my_errno)
1297                 skip_token();
1298         else
1299                 Mp += strspn(Mp, "+-0123456789");
1300
1301         if (*Mp ==',')
1302                 Mp++;
1303
1304         diag_printf("Stuffed int: %i\n", *i);
1305 }
1306
1307
1308 // Stuffs a boolean value pointed at by Mp.  
1309 // YES/NO (supporting 1/0 now as well)
1310 //
1311 void stuff_boolean(int *i)
1312 {
1313         char token[512];
1314         ignore_white_space();
1315         copy_to_eoln(token, NULL, Mp, 512);
1316         drop_trailing_white_space(token);
1317         advance_to_eoln(NULL);
1318
1319         if ( isdigit(token[0]) ) {                                      // using 1/0 instead of YES/NO
1320                 int int_value;
1321                 int_value = atoi(token);
1322                 if ( int_value )
1323                         *i = 1;
1324                 else
1325                         *i = 0;
1326         }
1327         else {
1328                 if ( !stricmp(token, "yes") ) {
1329                         *i = 1;
1330                 }
1331                 else if ( !stricmp(token, "no") ) {
1332                         *i = 0;
1333                 }
1334                 else
1335                         Assert(0);      // can't happen
1336         }
1337 }
1338
1339
1340 //      Stuff an integer value pointed at by Mp.
1341 //      Advances past integer characters.
1342 void stuff_byte(ubyte *i)
1343 {
1344         int     temp;
1345
1346         temp = atoi2();
1347
1348         *i = (ubyte)temp;
1349
1350         if (my_errno)
1351                 skip_token();
1352         else
1353                 Mp += strspn(Mp, "+-0123456789");
1354
1355         if (*Mp == ',')
1356                 Mp++;
1357
1358         diag_printf("Stuffed byte: %i\n", *i);
1359 }
1360
1361 // Stuffs a list of strings
1362 int stuff_string_list(char slp[][NAME_LENGTH], int max_strings)
1363 {
1364         int count = 0;
1365         ignore_white_space();
1366
1367         if ( *Mp != '(' ) {
1368                 error_display(1, "Reading string list.  Found [%c].  Expecting '('.\n", *Mp);
1369                 longjmp(parse_abort, 100);
1370         }
1371
1372         Mp++;
1373
1374         ignore_white_space();
1375
1376         while (*Mp != ')') {
1377                 assert ( count < max_strings );
1378                 Assert ( *Mp == '\"' );                                 // should always be enclosed in quotes
1379
1380                 get_string( slp[count++] );
1381                 ignore_white_space();
1382         }
1383
1384         Mp++;
1385
1386         return count;
1387 }
1388
1389 //      Stuffs an integer list.
1390 //      This is of the form ( i* )
1391 //        where i is an integer.
1392 // For example, (1) () (1 2 3) ( 1 ) are legal integer lists.
1393 int stuff_int_list(int *ilp, int max_ints, int lookup_type)
1394 {
1395         int     count = 0, ok_flag = 1, dummy;
1396         ignore_white_space();
1397
1398         if (*Mp != '(') {
1399                 error_display(1, "Reading integer list.  Found [%c].  Expecting '('.\n", *Mp);
1400                 longjmp(parse_abort, 6);
1401         }
1402
1403         Mp++;
1404         ignore_white_space();
1405
1406         while (*Mp != ')') {
1407                 Assert(count < max_ints);
1408                 if (*Mp == '"') {
1409                         int num = 0;
1410                         char str[128];
1411
1412                         get_string(str);
1413                         switch (lookup_type) {
1414                                 case SHIP_TYPE:
1415                                         num = ship_name_lookup(str);    // returns index of Ship[] entry with name
1416                                         break;
1417
1418                                 case SHIP_INFO_TYPE:
1419                                         ok_flag = 1;
1420                                         num = ship_info_lookup(str);    // returns index of Ship_info[] entry with name
1421                                         if (num < 0)
1422                                                 ok_flag = 0;
1423                                         break;
1424
1425                                 case WEAPON_POOL_TYPE:
1426                                         ok_flag = 1;
1427                                         num = weapon_info_lookup(str);
1428                                         if (num < 0)
1429                                                 ok_flag = 0;
1430                                         break;
1431
1432                                 case WEAPON_LIST_TYPE:
1433                                         num = weapon_info_lookup(str);
1434                                         if (num < 0)
1435                                                 num = -2;
1436                                         break;
1437
1438                                 case RAW_INTEGER_TYPE:
1439                                         num = atoi(str);
1440                                         break;
1441
1442                                 default:
1443                                         Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1444                                         break;
1445                         }
1446
1447                         if (ok_flag) {
1448                                 if (num == -1) {
1449                                         Error(LOCATION, "Unable to find string \"%s\" in stuff_int_list\n\nMany possible sources for this error.  Get a programmer!\n", str);
1450                                 } else if (num == -2) {
1451                                         if (strlen(str) > 0) {
1452                                                 Warning(LOCATION, "Unable to find WEAPON_LIST_TYPE string \"%s\" in stuff_int_list\n\nMany possible sources for this error.  Get a programmer!\n", str);
1453                                         }
1454                                 }
1455
1456                                 if (num < 0)  // other negatives used to bypass the above error trap, but should be -1
1457                                         num = -1;
1458
1459                                 ilp[count++] = num;
1460                         }
1461
1462                 } else {
1463                         if (ok_flag)
1464                                 stuff_int(&ilp[count++]);
1465                         else
1466                                 stuff_int(&dummy);
1467                 }
1468                 
1469                 ignore_white_space();
1470         }
1471
1472         Mp++;
1473
1474         return count;
1475 }
1476
1477 //      Marks an integer list.
1478 //      This is of the form ( i* )
1479 //        where i is an integer.
1480 //      If a specified string is found in the lookup and its value is 7, then the 7th value
1481 //      in the array is set.
1482 void mark_int_list(int *ilp, int max_ints, int lookup_type)
1483 {
1484         ignore_white_space();
1485
1486         if (*Mp != '(') {
1487                 error_display(1, "Marking integer list.  Found [%c].  Expecting '('.\n", *Mp);
1488                 longjmp(parse_abort, 6);
1489         }
1490
1491         Mp++;
1492         ignore_white_space();
1493
1494         while (*Mp != ')') {
1495                 if (*Mp == '"') {
1496                         int num = 0;
1497                         char str[128];
1498
1499                         get_string(str);
1500                         switch(lookup_type) {
1501                                 case SHIP_TYPE:
1502                                         num = ship_name_lookup(str);    // returns index of Ship[] entry with name
1503                                         break;
1504                         
1505                                 case SHIP_INFO_TYPE:
1506                                         num = ship_info_lookup(str);    // returns index of Ship_info[] entry with name
1507                                         break;
1508         
1509                                 case WEAPON_LIST_TYPE:
1510                                         num = weapon_info_lookup(str);
1511                                         break;
1512
1513                                 default:
1514                                         Error(LOCATION,"Unknown lookup_type in stuff_int_list");
1515                                         break;
1516                         }
1517
1518                         if ( (num < 0) || (num >= max_ints) )
1519                                 Error(LOCATION, "Unable to find string \"%s\" in mark_int_list.\n", str);
1520
1521 //                      ilp[num] = 1;
1522                 
1523                 } else {
1524                         int     tval;
1525
1526                         stuff_int(&tval);
1527                         Assert((tval >= 0) && (tval < max_ints));
1528                         ilp[tval] = 1;
1529                 }
1530                 
1531                 ignore_white_space();
1532         }
1533
1534         Mp++;
1535
1536 }
1537
1538
1539 //      Stuff a vector, which is 3 floats.
1540 void stuff_vector(vector *vp)
1541 {
1542         stuff_float(&vp->x);
1543         stuff_float(&vp->y);
1544         stuff_float(&vp->z);
1545 }
1546
1547 void stuff_parenthesized_vector(vector *vp)
1548 {
1549         ignore_white_space();
1550
1551         if (*Mp != '(') {
1552                 error_display(1, "Reading parenthesized vector.  Found [%c].  Expecting '('.\n", *Mp);
1553                 longjmp(parse_abort, 11);
1554         } else {
1555                 Mp++;
1556                 stuff_vector(vp);
1557                 ignore_white_space();
1558                 if (*Mp != ')') {
1559                         error_display(1, "Reading parenthesized vector.  Found [%c].  Expecting ')'.\n", *Mp);
1560                         longjmp(parse_abort, 12);
1561                 }
1562                 Mp++;
1563         }
1564
1565 }
1566
1567 //      Stuffs vector list.
1568 //      This is of the form ( (vector)* )
1569 //        where vector is a vector
1570 // For example, ( (1 2 3) (2 3 4) (2 3 5) )
1571 //               is a vector list of three vectors.
1572 int stuff_vector_list(vector *vlp, int max_vecs)
1573 {
1574         int     count = 0;
1575
1576         ignore_white_space();
1577
1578         if (*Mp != '(') {
1579                 error_display(1, "Reading integer list.  Found [%c].  Expecting '('.\n", *Mp);
1580                 longjmp(parse_abort, 6);
1581         }
1582
1583         Mp++;
1584
1585         ignore_white_space();
1586
1587         while (*Mp != ')') {
1588                 Assert(count < max_vecs);
1589                 stuff_parenthesized_vector(&vlp[count++]);
1590                 
1591                 ignore_white_space();
1592         }
1593
1594         Mp++;
1595
1596         return count;
1597 }
1598
1599
1600 //      Stuff a matrix, which is 3 vectors.
1601 void stuff_matrix(matrix *mp)
1602 {
1603         stuff_vector(&mp->rvec);
1604         stuff_vector(&mp->uvec);
1605         stuff_vector(&mp->fvec);
1606 }
1607
1608
1609 //      Given a string, find it in a string array.
1610 //      *descrtiption is only used for diagnostics in case it can't be found.
1611 //      *str1 is the string to be found.
1612 //      *strlist is the list of strings to search.
1613 //      max is the number of entries in *strlist to scan.
1614 int string_lookup(char *str1, char *strlist[], int max, char *description, int say_errors)
1615 {
1616         int     i;
1617
1618         for (i=0; i<max; i++) {
1619                 Assert(strlen(strlist[i]) != 0);
1620
1621                 if (!stricmp(str1, strlist[i]))
1622                         return i;
1623         }
1624
1625         if (say_errors)
1626                 error_display(0, "Unable to find [%s] in %s list.\n", str1, description);
1627
1628         return -1;
1629 }
1630
1631 //      Find a required string (*id), then stuff the text of type f_type that
1632 // follows it at *addr.  *strlist[] contains the strings it should try to
1633 // match.
1634 void find_and_stuff(char *id, int *addr, int f_type, char *strlist[], int max, char *description)
1635 {
1636         char    token[128];
1637
1638         required_string(id);
1639         stuff_string(token, f_type, NULL);
1640         *addr = string_lookup(token, strlist, max, description, 1);
1641 }
1642
1643 //      Mp points at a string.
1644 //      Find the string in the list of strings *strlist[].
1645 // Returns the index of the match, -1 if none.
1646 int match_and_stuff(int f_type, char *strlist[], int max, char *description)
1647 {
1648         char    token[128];
1649
1650         stuff_string(token, f_type, NULL);
1651         return string_lookup(token, strlist, max, description, 0);
1652 }
1653
1654 void find_and_stuff_or_add(char *id, int *addr, int f_type, char *strlist[], int *total,
1655         int max, char *description)
1656 {
1657         char    token[128];
1658
1659         *addr = -1;
1660         required_string(id);
1661         stuff_string(token, f_type, NULL);
1662         if (*total)
1663                 *addr = string_lookup(token, strlist, *total, description, 0);
1664
1665         if (*addr == -1)  // not in list, so lets try and add it.
1666         {
1667                 Assert(*total < max);
1668                 strcpy(strlist[*total], token);
1669                 *addr = (*total)++;
1670         }
1671 }
1672
1673 // Initialize a parse process.
1674 void init_parse()
1675 {
1676         int i;
1677
1678         Mp = Mission_text;
1679
1680         Warning_count = 0;
1681         Error_count = 0;
1682         for (i=0; i<MAX_CARGO; i++)
1683                 Cargo_names[i] = Cargo_names_buf[i]; // make a pointer array for compatibility
1684
1685         Total_goal_ship_names = 0;
1686         init_sexp();
1687 }
1688
1689 void reset_parse()
1690 {
1691         Mp = Mission_text;
1692
1693         Warning_count = 0;
1694         Error_count = 0;
1695 }
1696
1697 // Display number of warnings and errors at the end of a parse.
1698 void display_parse_diagnostics()
1699 {
1700         nprintf(("Parse", "\nParse complete.\n"));
1701         nprintf(("Parse", "%i errors.  %i warnings.\n", Error_count, Warning_count));
1702 }
1703
1704 // Splits a string into 2 lines if the string is wider than max_pixel_w pixels.  A null
1705 // terminator is placed where required to make the first line <= max_pixel_w.  The remaining
1706 // text is returned (leading whitespace removed).  If the line doesn't need to be split,
1707 // NULL is returned.
1708 char *split_str_once(char *src, int max_pixel_w)
1709 {
1710         char *brk = NULL;
1711         int i, w, len, last_was_white = 0;
1712
1713         Assert(src);
1714         Assert(max_pixel_w > 0);
1715         
1716         gr_get_string_size(&w, NULL, src);
1717         if ( w <= max_pixel_w )
1718                 return NULL;  // string doesn't require a cut
1719
1720         len = strlen(src);
1721         for (i=0; i<len; i++) {
1722                 gr_get_string_size(&w, NULL, src, i);
1723                 if ( w > max_pixel_w )
1724                         break;
1725
1726                 if (src[i] == '\n') {  // reached natural end of line
1727                         src[i] = 0;
1728                         return src + i + 1;
1729                 }
1730
1731                 if (is_white_space(src[i])) {
1732                         if (!last_was_white)
1733                                 brk = src + i;
1734
1735                         last_was_white = 1;
1736
1737                 } else {
1738                         last_was_white = 0;
1739                 }
1740         }
1741
1742         if (!brk) {
1743                 brk = src + i;
1744         }
1745
1746         *brk = 0;
1747         src = brk + 1;
1748         while (is_white_space(*src))
1749                 src++;
1750
1751         if (!*src)
1752                 return NULL;  // end of the string anyway
1753                 
1754         if (*src == '\n')
1755                 src++;
1756
1757         return src;
1758 }
1759
1760 #define SPLIT_STR_BUFFER_SIZE   512
1761
1762 // --------------------------------------------------------------------------------------
1763 // split_str() 
1764 //
1765 // A general function that will split a string into several lines.  Lines are allowed up
1766 // to max_pixel_w pixels.  Breaks are found in white space.
1767 //
1768 // Supports \n's in the strings!
1769 //
1770 // parameters:          src                     =>              source string to be broken up
1771 //                                              max_pixel_w     =>              max width of line in pixels
1772 //                                              n_chars         =>              output array that will hold number of characters in each line
1773 //                                              p_str                   =>              output array of pointers to start of lines within src
1774 //                                              max_lines       =>              limit of number of lines to break src up into
1775 //                                              ignore_char     =>              OPTIONAL parameter (default val -1).  Ignore words starting with this character
1776 //                                                                                              This is useful when you want to ignore embedded control information that starts
1777 //                                                                                              with a specific character, like $ or #
1778 //
1779 //      returns:                        number of lines src is broken into
1780 //                                              -1 is returned when an error occurs
1781 //
1782 int split_str(char *src, int max_pixel_w, int *n_chars, char **p_str, int max_lines, char ignore_char)
1783 {
1784         char buffer[SPLIT_STR_BUFFER_SIZE];
1785         char *breakpoint = NULL;
1786         int sw, new_line = 1, line_num = 0, last_was_white = 0;
1787         int ignore_until_whitespace, buf_index;
1788         
1789         // check our assumptions..
1790         Assert(src != NULL);
1791         Assert(n_chars != NULL);
1792         Assert(p_str != NULL);
1793         Assert(max_lines > 0);
1794         Assert(max_pixel_w > 0);
1795         
1796         memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1797         buf_index = 0;
1798         ignore_until_whitespace = 0;
1799
1800         // get rid of any leading whitespace
1801         while (is_white_space(*src))
1802                 src++;
1803
1804         new_line = 1;
1805         p_str[0] = NULL;
1806
1807         // iterate through chars in line, keeping track of most recent "white space" location that can be used
1808         // as a line splitting point if necessary
1809         for (; *src; src++) {
1810                 if (line_num >= max_lines)
1811                         return line_num;  // time to bail out
1812
1813                 // starting a new line of text, init stuff for that
1814                 if (new_line) {
1815                         p_str[line_num] = NULL;
1816                         if (is_gray_space(*src))
1817                                 continue;
1818
1819                         p_str[line_num] = src;
1820                         breakpoint = NULL;
1821                         new_line = 0;
1822                 }
1823
1824                 // maybe skip leading whitespace
1825                 if (ignore_until_whitespace) {
1826                         if ( is_white_space(*src) )
1827                                 ignore_until_whitespace = 0;
1828
1829                         continue;
1830                 }
1831
1832                 // if we have a newline, split the line here
1833                 if (*src == '\n') {
1834                         n_chars[line_num] = src - p_str[line_num];  // track length of line
1835                         line_num++;
1836                         p_str[line_num] = NULL;
1837                         new_line = 1;
1838
1839                         memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1840                         buf_index = 0;
1841                         continue;
1842                 }
1843
1844                 if (*src == ignore_char) {
1845                         ignore_until_whitespace = 1;
1846                         continue;
1847                 }
1848
1849                 if (is_gray_space(*src)) {
1850                         if (!last_was_white)  // track at first whitespace in a series of whitespace
1851                                 breakpoint = src;
1852
1853                         last_was_white = 1;
1854
1855                 } else {
1856                         // indicate next time around that this wasn't a whitespace character
1857                         last_was_white = 0;
1858                 }
1859
1860                 // throw it in our buffer
1861                 buffer[buf_index] = *src;
1862                 buf_index++;
1863                 buffer[buf_index] = 0;  // null terminate it
1864         
1865                 gr_get_string_size(&sw, NULL, buffer);
1866                 if (sw >= max_pixel_w) {
1867                         char *end;
1868
1869                         if (breakpoint) {
1870                                 end = src = breakpoint;
1871
1872                         } else {
1873                                 end = src;  // force a split here since to whitespace
1874                                 src--;  // reuse this character in next line
1875                         }
1876
1877                         n_chars[line_num] = end - p_str[line_num];  // track length of line
1878                         Assert(n_chars[line_num]);
1879                         line_num++;
1880                         p_str[line_num] = NULL;
1881                         new_line = 1;
1882
1883                         memset(buffer, 0, SPLIT_STR_BUFFER_SIZE);
1884                         buf_index = 0;
1885                         continue;
1886                 }
1887         }       // end for
1888
1889         if (p_str[line_num]) {
1890                 n_chars[line_num] = src - p_str[line_num];  // track length of line
1891                 Assert(n_chars[line_num]);
1892                 line_num++;
1893         }
1894
1895         return line_num;
1896 }
1897