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