Update to Inform v6.35
[inform.git] / src / inform.c
1 /* ------------------------------------------------------------------------- */
2 /*   "inform" :  The top level of Inform: switches, pathnames, filenaming    */
3 /*               conventions, ICL (Inform Command Line) files, main          */
4 /*                                                                           */
5 /*   Part of Inform 6.35                                                     */
6 /*   copyright (c) Graham Nelson 1993 - 2021                                 */
7 /*                                                                           */
8 /* Inform is free software: you can redistribute it and/or modify            */
9 /* it under the terms of the GNU General Public License as published by      */
10 /* the Free Software Foundation, either version 3 of the License, or         */
11 /* (at your option) any later version.                                       */
12 /*                                                                           */
13 /* Inform is distributed in the hope that it will be useful,                 */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
16 /* GNU General Public License for more details.                              */
17 /*                                                                           */
18 /* You should have received a copy of the GNU General Public License         */
19 /* along with Inform. If not, see https://gnu.org/licenses/                  *
20 /*                                                                           */
21 /* ------------------------------------------------------------------------- */
22
23 #define MAIN_INFORM_FILE
24 #include "header.h"
25
26 #define CMD_BUF_SIZE (256)
27
28 /* ------------------------------------------------------------------------- */
29 /*   Compiler progress                                                       */
30 /* ------------------------------------------------------------------------- */
31
32 static int no_compilations;
33
34 int endofpass_flag;      /* set to TRUE when an "end" directive is reached
35                             (the inputs routines insert one into the stream
36                             if necessary)                                    */
37
38 /* ------------------------------------------------------------------------- */
39 /*   Version control                                                         */
40 /* ------------------------------------------------------------------------- */
41
42 int version_number,      /* 3 to 8 (Z-code)                                  */
43     instruction_set_number,
44                          /* 3 to 6: versions 7 and 8 use instruction set of
45                             version 5                                        */
46     extend_memory_map;   /* extend using function- and string-offsets        */
47 int32 scale_factor,      /* packed address multiplier                        */
48     length_scale_factor; /* length-in-header multiplier                      */
49
50 int32 requested_glulx_version;
51
52 extern void select_version(int vn)
53 {   version_number = vn;
54     extend_memory_map = FALSE;
55     if ((version_number==6)||(version_number==7)) extend_memory_map = TRUE;
56
57     scale_factor = 4;
58     if (version_number==3) scale_factor = 2;
59     if (version_number==8) scale_factor = 8;
60
61     length_scale_factor = scale_factor;
62     if ((version_number==6)||(version_number==7)) length_scale_factor = 8;
63
64     instruction_set_number = version_number;
65     if ((version_number==7)||(version_number==8)) instruction_set_number = 5;
66 }
67
68 static int select_glulx_version(char *str)
69 {
70   /* Parse an "X.Y.Z" style version number, and store it for later use. */
71   char *cx = str;
72   int major=0, minor=0, patch=0;
73
74   while (isdigit(*cx))
75     major = major*10 + ((*cx++)-'0');
76   if (*cx == '.') {
77     cx++;
78     while (isdigit(*cx))
79       minor = minor*10 + ((*cx++)-'0');
80     if (*cx == '.') {
81       cx++;
82       while (isdigit(*cx))
83         patch = patch*10 + ((*cx++)-'0');
84     }
85   }
86
87   requested_glulx_version = ((major & 0x7FFF) << 16) 
88     + ((minor & 0xFF) << 8) 
89     + (patch & 0xFF);
90   return (cx - str);
91 }
92
93 /* ------------------------------------------------------------------------- */
94 /*   Target: variables which vary between the Z-machine and Glulx            */
95 /* ------------------------------------------------------------------------- */
96
97 int   WORDSIZE;            /* Size of a machine word: 2 or 4 */
98 int32 MAXINTWORD;          /* 0x7FFF or 0x7FFFFFFF */
99
100 /* The first property number which is an individual property. The
101    eight class-system i-props (create, recreate, ... print_to_array)
102    are numbered from INDIV_PROP_START to INDIV_PROP_START+7.
103 */
104 int INDIV_PROP_START;
105
106 /* The length of an object, as written in tables.c. It's easier to define
107    it here than to repeat the same expression all over the source code.
108    Not used in Z-code. 
109 */
110 int OBJECT_BYTE_LENGTH;
111 /* The total length of a dict entry, in bytes. Not used in Z-code. 
112 */
113 int DICT_ENTRY_BYTE_LENGTH;
114 /* The position in a dict entry that the flag values begin.
115    Not used in Z-code. 
116 */
117 int DICT_ENTRY_FLAG_POS;
118
119 static void select_target(int targ)
120 {
121   if (!targ) {
122     /* Z-machine */
123     WORDSIZE = 2;
124     MAXINTWORD = 0x7FFF;
125
126     if (INDIV_PROP_START != 64) {
127         INDIV_PROP_START = 64;
128         fatalerror("You cannot change INDIV_PROP_START in Z-code");
129     }
130     if (DICT_WORD_SIZE != 6) {
131       DICT_WORD_SIZE = 6;
132       fatalerror("You cannot change DICT_WORD_SIZE in Z-code");
133     }
134     if (DICT_CHAR_SIZE != 1) {
135       DICT_CHAR_SIZE = 1;
136       fatalerror("You cannot change DICT_CHAR_SIZE in Z-code");
137     }
138     if (NUM_ATTR_BYTES != 6) {
139       NUM_ATTR_BYTES = 6;
140       fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
141     }
142     if (MAX_LOCAL_VARIABLES != 16) {
143       MAX_LOCAL_VARIABLES = 16;
144       fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
145     }
146     if (MAX_GLOBAL_VARIABLES != 240) {
147       MAX_GLOBAL_VARIABLES = 240;
148       fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
149     }
150     if (MAX_VERBS > 255) {
151       MAX_VERBS = 255;
152       fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
153     }
154   }
155   else {
156     /* Glulx */
157     WORDSIZE = 4;
158     MAXINTWORD = 0x7FFFFFFF;
159     scale_factor = 0; /* It should never even get used in Glulx */
160
161     if (INDIV_PROP_START < 256) {
162         INDIV_PROP_START = 256;
163         warning_numbered("INDIV_PROP_START should be at least 256 in Glulx. Setting to", INDIV_PROP_START);
164     }
165
166     if (NUM_ATTR_BYTES % 4 != 3) {
167       NUM_ATTR_BYTES += (3 - (NUM_ATTR_BYTES % 4)); 
168       warning_numbered("NUM_ATTR_BYTES must be a multiple of four, plus three. Increasing to", NUM_ATTR_BYTES);
169     }
170
171     if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) {
172       DICT_CHAR_SIZE = 4;
173       warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE);
174     }
175   }
176
177   if (MAX_LOCAL_VARIABLES >= 120) {
178     MAX_LOCAL_VARIABLES = 119;
179     warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119");
180     /* This is because the keyword table in the lexer only has 120
181        entries. */
182   }
183   if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
184     DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
185     warning_numbered(
186       "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting", 
187       MAX_DICT_WORD_SIZE);
188     /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */
189   }
190   if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) {
191     NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES;
192     warning_numbered(
193       "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting",
194       MAX_NUM_ATTR_BYTES);
195     /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
196   }
197
198   if (MAX_ADJECTIVES > 255) {
199     MAX_ADJECTIVES = 255;
200     warning("MAX_ADJECTIVES cannot exceed 255; resetting to 255");
201     /* Only used under Grammar__Version 1, which is obsolete. */
202   }
203     
204   /* Set up a few more variables that depend on the above values */
205
206   if (!targ) {
207     /* Z-machine */
208     DICT_WORD_BYTES = DICT_WORD_SIZE;
209     /* The Z-code generator doesn't use the following variables, although 
210        it would be a little cleaner if it did. */
211     OBJECT_BYTE_LENGTH = 0;
212     DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9;
213     DICT_ENTRY_FLAG_POS = 0;
214   }
215   else {
216     /* Glulx */
217     OBJECT_BYTE_LENGTH = (1 + (NUM_ATTR_BYTES) + 6*4 + (GLULX_OBJECT_EXT_BYTES));
218     DICT_WORD_BYTES = DICT_WORD_SIZE*DICT_CHAR_SIZE;
219     if (DICT_CHAR_SIZE == 1) {
220       DICT_ENTRY_BYTE_LENGTH = (7+DICT_WORD_BYTES);
221       DICT_ENTRY_FLAG_POS = (1+DICT_WORD_BYTES);
222     }
223     else {
224       DICT_ENTRY_BYTE_LENGTH = (12+DICT_WORD_BYTES);
225       DICT_ENTRY_FLAG_POS = (4+DICT_WORD_BYTES);
226     }
227   }
228
229   if (!targ) {
230     /* Z-machine */
231     /* The Z-machine's 96 abbreviations are used for these two purposes.
232        Make sure they are set consistently. If exactly one has been
233        set non-default, set the other to match. */
234     if (MAX_DYNAMIC_STRINGS == 32 && MAX_ABBREVS != 64) {
235         MAX_DYNAMIC_STRINGS = 96 - MAX_ABBREVS;
236     }
237     if (MAX_ABBREVS == 64 && MAX_DYNAMIC_STRINGS != 32) {
238         MAX_ABBREVS = 96 - MAX_DYNAMIC_STRINGS;
239     }
240     if (MAX_ABBREVS + MAX_DYNAMIC_STRINGS != 96
241         || MAX_ABBREVS < 0
242         || MAX_DYNAMIC_STRINGS < 0) {
243       warning("MAX_ABBREVS plus MAX_DYNAMIC_STRINGS must be 96 in Z-code; resetting both");
244       MAX_DYNAMIC_STRINGS = 32;
245       MAX_ABBREVS = 64;
246     }
247   }
248   else {
249     if (MAX_DYNAMIC_STRINGS > 100) {
250       MAX_DYNAMIC_STRINGS = 100;
251       warning("MAX_DYNAMIC_STRINGS cannot exceed 100; resetting to 100");
252       /* This is because they are specified in text literals like "@00",
253          with two digits. */
254     }
255   }
256 }
257
258 /* ------------------------------------------------------------------------- */
259 /*   Tracery: output control variables                                       */
260 /* ------------------------------------------------------------------------- */
261
262 int asm_trace_level,     /* trace assembly: 0 for off, 1 for assembly
263                             only, 2 for full assembly tracing with hex dumps */
264     line_trace_level,    /* line tracing: 0 off, 1 on                        */
265     expr_trace_level,    /* expression tracing: 0 off, 1 full, 2 brief       */
266     linker_trace_level,  /* set by -y: 0 to 4 levels of tracing              */
267     tokens_trace_level;  /* lexer output tracing: 0 off, 1 on                */
268
269 /* ------------------------------------------------------------------------- */
270 /*   On/off switch variables (by default all FALSE); other switch settings   */
271 /* ------------------------------------------------------------------------- */
272
273 int bothpasses_switch,              /* -b */
274     concise_switch,                 /* -c */
275     economy_switch,                 /* -e */
276     frequencies_switch,             /* -f */
277     ignore_switches_switch,         /* -i */
278     listobjects_switch,             /* -j */
279     debugfile_switch,               /* -k */
280     listing_switch,                 /* -l */
281     memout_switch,                  /* -m */
282     printprops_switch,              /* -n */
283     offsets_switch,                 /* -o */
284     percentages_switch,             /* -p */
285     obsolete_switch,                /* -q */
286     transcript_switch,              /* -r */
287     statistics_switch,              /* -s */
288     optimise_switch,                /* -u */
289     version_set_switch,             /* -v */
290     nowarnings_switch,              /* -w */
291     hash_switch,                    /* -x */
292     memory_map_switch,              /* -z */
293     oddeven_packing_switch,         /* -B */
294     define_DEBUG_switch,            /* -D */
295     temporary_files_switch,         /* -F */
296     module_switch,                  /* -M */
297     runtime_error_checking_switch,  /* -S */
298     define_USE_MODULES_switch,      /* -U */
299     define_INFIX_switch;            /* -X */
300 #ifdef ARC_THROWBACK
301 int throwback_switch;               /* -T */
302 #endif
303 #ifdef ARCHIMEDES
304 int riscos_file_type_format;        /* set by -R */
305 #endif
306 int compression_switch;             /* set by -H */
307 int character_set_setting,          /* set by -C0 through -C9 */
308     character_set_unicode,          /* set by -Cu */
309     error_format,                   /* set by -E */
310     asm_trace_setting,              /* set by -a and -t: value of
311                                        asm_trace_level to use when tracing */
312     double_space_setting,           /* set by -d: 0, 1 or 2 */
313     trace_fns_setting,              /* set by -g: 0, 1 or 2 */
314     linker_trace_setting,           /* set by -y: ditto for linker_... */
315     store_the_text;                 /* when set, record game text to a chunk
316                                        of memory (used by both -r & -k) */
317 static int r_e_c_s_set;             /* has -S been explicitly set? */
318
319 int glulx_mode;                     /* -G */
320
321 static void reset_switch_settings(void)
322 {   asm_trace_setting=0;
323     linker_trace_level=0;
324     tokens_trace_level=0;
325
326     store_the_text = FALSE;
327
328     bothpasses_switch = FALSE;
329     concise_switch = FALSE;
330     double_space_setting = 0;
331     economy_switch = FALSE;
332     frequencies_switch = FALSE;
333     trace_fns_setting = 0;
334     ignore_switches_switch = FALSE;
335     listobjects_switch = FALSE;
336     debugfile_switch = FALSE;
337     listing_switch = FALSE;
338     memout_switch = FALSE;
339     printprops_switch = FALSE;
340     offsets_switch = FALSE;
341     percentages_switch = FALSE;
342     obsolete_switch = FALSE;
343     transcript_switch = FALSE;
344     statistics_switch = FALSE;
345     optimise_switch = FALSE;
346     version_set_switch = FALSE;
347     nowarnings_switch = FALSE;
348     hash_switch = FALSE;
349     memory_map_switch = FALSE;
350     oddeven_packing_switch = FALSE;
351     define_DEBUG_switch = FALSE;
352 #ifdef USE_TEMPORARY_FILES
353     temporary_files_switch = TRUE;
354 #else
355     temporary_files_switch = FALSE;
356 #endif
357     define_USE_MODULES_switch = FALSE;
358     module_switch = FALSE;
359 #ifdef ARC_THROWBACK
360     throwback_switch = FALSE;
361 #endif
362     runtime_error_checking_switch = TRUE;
363     r_e_c_s_set = FALSE;
364     define_INFIX_switch = FALSE;
365 #ifdef ARCHIMEDES
366     riscos_file_type_format = 0;
367 #endif
368     error_format=DEFAULT_ERROR_FORMAT;
369
370     character_set_setting = 1;         /* Default is ISO Latin-1 */
371     character_set_unicode = FALSE;
372
373     compression_switch = TRUE;
374     glulx_mode = FALSE;
375     requested_glulx_version = 0;
376 }
377
378 /* ------------------------------------------------------------------------- */
379 /*   Number of files given as command line parameters (0, 1 or 2)            */
380 /* ------------------------------------------------------------------------- */
381
382 static int cli_files_specified,
383            convert_filename_flag;
384
385 char Source_Name[PATHLEN];             /* Processed name of first input file */
386 char Code_Name[PATHLEN];               /* Processed name of output file      */
387
388 static char *cli_file1, *cli_file2;    /* Unprocessed (and unsafe to alter)  */
389
390 /* ========================================================================= */
391 /*   Data structure management routines                                      */
392 /* ------------------------------------------------------------------------- */
393
394 static void init_vars(void)
395 {
396     init_arrays_vars();
397     init_asm_vars();
398     init_bpatch_vars();
399     init_chars_vars();
400     init_directs_vars();
401     init_errors_vars();
402     init_expressc_vars();
403     init_expressp_vars();
404     init_files_vars();
405     init_lexer_vars();
406     init_linker_vars();
407     init_memory_vars();
408     init_objects_vars();
409     init_states_vars();
410     init_symbols_vars();
411     init_syntax_vars();
412     init_tables_vars();
413     init_text_vars();
414     init_veneer_vars();
415     init_verbs_vars();
416 }
417
418 static void begin_pass(void)
419 {
420     arrays_begin_pass();
421     asm_begin_pass();
422     bpatch_begin_pass();
423     chars_begin_pass();
424     directs_begin_pass();
425     errors_begin_pass();
426     expressc_begin_pass();
427     expressp_begin_pass();
428     files_begin_pass();
429
430     endofpass_flag = FALSE;
431     line_trace_level = 0; expr_trace_level = 0;
432     asm_trace_level = asm_trace_setting;
433     linker_trace_level = linker_trace_setting;
434     if (listing_switch) line_trace_level=1;
435
436     lexer_begin_pass();
437     linker_begin_pass();
438     memory_begin_pass();
439     objects_begin_pass();
440     states_begin_pass();
441     symbols_begin_pass();
442     syntax_begin_pass();
443     tables_begin_pass();
444     text_begin_pass();
445     veneer_begin_pass();
446     verbs_begin_pass();
447
448     if (!module_switch)
449     {
450         /*  Compile a Main__ routine (see "veneer.c")  */
451
452         compile_initial_routine();
453
454         /*  Make the four metaclasses: Class must be object number 1, so
455             it must come first  */
456
457         veneer_mode = TRUE;
458
459         make_class("Class");
460         make_class("Object");
461         make_class("Routine");
462         make_class("String");
463
464         veneer_mode = FALSE;
465     }
466 }
467
468 extern void allocate_arrays(void)
469 {
470     arrays_allocate_arrays();
471     asm_allocate_arrays();
472     bpatch_allocate_arrays();
473     chars_allocate_arrays();
474     directs_allocate_arrays();
475     errors_allocate_arrays();
476     expressc_allocate_arrays();
477     expressp_allocate_arrays();
478     files_allocate_arrays();
479
480     lexer_allocate_arrays();
481     linker_allocate_arrays();
482     memory_allocate_arrays();
483     objects_allocate_arrays();
484     states_allocate_arrays();
485     symbols_allocate_arrays();
486     syntax_allocate_arrays();
487     tables_allocate_arrays();
488     text_allocate_arrays();
489     veneer_allocate_arrays();
490     verbs_allocate_arrays();
491 }
492
493 extern void free_arrays(void)
494 {
495     /*  One array may survive this routine, all_the_text (used to hold
496         game text until the abbreviations optimiser begins work on it): this
497         array (if it was ever allocated) is freed at the top level.          */
498
499     arrays_free_arrays();
500     asm_free_arrays();
501     bpatch_free_arrays();
502     chars_free_arrays();
503     directs_free_arrays();
504     errors_free_arrays();
505     expressc_free_arrays();
506     expressp_free_arrays();
507     files_free_arrays();
508
509     lexer_free_arrays();
510     linker_free_arrays();
511     memory_free_arrays();
512     objects_free_arrays();
513     states_free_arrays();
514     symbols_free_arrays();
515     syntax_free_arrays();
516     tables_free_arrays();
517     text_free_arrays();
518     veneer_free_arrays();
519     verbs_free_arrays();
520 }
521
522 /* ------------------------------------------------------------------------- */
523 /*    Name translation code for filenames                                    */
524 /* ------------------------------------------------------------------------- */
525
526 static char Source_Path[PATHLEN];
527 static char Include_Path[PATHLEN];
528 static char Code_Path[PATHLEN];
529 static char Module_Path[PATHLEN];
530 static char Temporary_Path[PATHLEN];
531 static char current_source_path[PATHLEN];
532        char Debugging_Name[PATHLEN];
533        char Transcript_Name[PATHLEN];
534        char Language_Name[PATHLEN];
535        char Charset_Map[PATHLEN];
536 static char ICL_Path[PATHLEN];
537
538 /* Set one of the above Path buffers to the given location, or list of
539    locations. (A list is comma-separated, and only accepted for Source_Path,
540    Include_Path, ICL_Path, Module_Path.)
541 */
542 static void set_path_value(char *path, char *value)
543 {   int i, j;
544
545     for (i=0, j=0;;)
546     {
547         if (i >= PATHLEN-1) {
548             printf("A specified path is longer than %d characters.\n",
549                 PATHLEN-1);
550             exit(1);
551         }
552         if ((value[j] == FN_ALT) || (value[j] == 0))
553         {   if ((value[j] == FN_ALT)
554                 && (path != Source_Path) && (path != Include_Path)
555                 && (path != ICL_Path) && (path != Module_Path))
556             {   printf("The character '%c' is used to divide entries in a list \
557 of possible locations, and can only be used in the Include_Path, Source_Path, \
558 Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
559                 exit(1);
560             }
561             if ((path != Debugging_Name) && (path != Transcript_Name)
562                  && (path != Language_Name) && (path != Charset_Map)
563                  && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
564             path[i++] = value[j++];
565             if (value[j-1] == 0) return;
566         }
567         else path[i++] = value[j++];
568     }
569 }
570
571 /* Prepend the given location or list of locations to one of the above
572    Path buffers. This is only permitted for Source_Path, Include_Path, 
573    ICL_Path, Module_Path.
574
575    An empty field (in the comma-separated list) means the current
576    directory. If the Path buffer is entirely empty, we assume that
577    we want to search both value and the current directory, so
578    the result will be "value,".
579 */
580 static void prepend_path_value(char *path, char *value)
581 {
582     int i, j;
583     int oldlen = strlen(path);
584     int newlen;
585     char new_path[PATHLEN];
586
587     if ((path != Source_Path) && (path != Include_Path)
588         && (path != ICL_Path) && (path != Module_Path))
589     {   printf("The character '+' is used to add to a list \
590 of possible locations, and can only be used in the Include_Path, Source_Path, \
591 Module_Path or ICL_Path variables. Other paths are for output only.\n");
592         exit(1);
593     }
594
595     for (i=0, j=0;;)
596     {
597         if (i >= PATHLEN-1) {
598             printf("A specified path is longer than %d characters.\n",
599                 PATHLEN-1);
600             exit(1);
601         }
602         if ((value[j] == FN_ALT) || (value[j] == 0))
603         {   if ((path != Debugging_Name) && (path != Transcript_Name)
604                  && (path != Language_Name) && (path != Charset_Map)
605                  && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
606             new_path[i++] = value[j++];
607             if (value[j-1] == 0) {
608                 newlen = i-1;
609                 break;
610             }
611         }
612         else new_path[i++] = value[j++];
613     }
614
615     if (newlen+1+oldlen >= PATHLEN-1) {
616         printf("A specified path is longer than %d characters.\n",
617             PATHLEN-1);
618         exit(1);
619     }
620
621     i = newlen;
622     new_path[i++] = FN_ALT;
623     for (j=0; j<oldlen;)
624         new_path[i++] = path[j++];
625     new_path[i] = 0;
626     
627     strcpy(path, new_path);
628 }
629
630 static void set_default_paths(void)
631 {
632     set_path_value(Source_Path,     Source_Directory);
633     set_path_value(Include_Path,    Include_Directory);
634     set_path_value(Code_Path,       Code_Directory);
635     set_path_value(Module_Path,     Module_Directory);
636     set_path_value(ICL_Path,        ICL_Directory);
637     set_path_value(Temporary_Path,  Temporary_Directory);
638     set_path_value(Debugging_Name,  Debugging_File);
639     set_path_value(Transcript_Name, Transcript_File);
640     set_path_value(Language_Name,   Default_Language);
641     set_path_value(Charset_Map,     "");
642 }
643
644 /* Parse a path option which looks like "dir", "+dir", "pathname=dir",
645    or "+pathname=dir". If there is no "=", we assume "include_path=...".
646    If the option begins with a "+" the directory is prepended to the
647    existing path instead of replacing it.
648 */
649 static void set_path_command(char *command)
650 {   int i, j; char *path_to_set = NULL;
651     int prepend = 0;
652
653     if (command[0] == '+') {
654         prepend = 1;
655         command++;
656     }
657
658     for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
659
660     path_to_set=Include_Path; 
661
662     if (command[i] == '=') { 
663         char pathname[PATHLEN];
664         if (i>=PATHLEN) i=PATHLEN-1;
665         for (j=0;j<i;j++) {
666             char ch = command[j];
667             if (isupper(ch)) ch=tolower(ch);
668             pathname[j]=ch;
669         }
670         pathname[j]=0;
671         command = command+i+1;
672
673         path_to_set = NULL;
674         if (strcmp(pathname, "source_path")==0)  path_to_set=Source_Path;
675         if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
676         if (strcmp(pathname, "code_path")==0)    path_to_set=Code_Path;
677         if (strcmp(pathname, "module_path")==0)  path_to_set=Module_Path;
678         if (strcmp(pathname, "icl_path")==0)     path_to_set=ICL_Path;
679         if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
680         if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
681         if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
682         if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
683         if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
684
685         if (path_to_set == NULL)
686         {   printf("No such path setting as \"%s\"\n", pathname);
687             exit(1);
688         }
689     }
690
691     if (!prepend)
692         set_path_value(path_to_set, command);
693     else
694         prepend_path_value(path_to_set, command);
695 }
696
697 static int contains_separator(char *name)
698 {   int i;
699     for (i=0; name[i]!=0; i++)
700         if (name[i] == FN_SEP) return 1;
701     return 0;
702 }
703
704 static int write_translated_name(char *new_name, char *old_name,
705                                  char *prefix_path, int start_pos,
706                                  char *extension)
707 {   int x;
708     if (strlen(old_name)+strlen(extension) >= PATHLEN) {
709         printf("One of your filenames is longer than %d characters.\n", PATHLEN);
710         exit(1);
711     }
712     if (prefix_path == NULL)
713     {   sprintf(new_name,"%s%s", old_name, extension);
714         return 0;
715     }
716     strcpy(new_name, prefix_path + start_pos);
717     for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
718     if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
719     if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
720         printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
721         exit(1);
722     }
723     sprintf(new_name + x, "%s%s", old_name, extension);
724     return start_pos;
725 }
726
727 #ifdef FILE_EXTENSIONS
728 static char *check_extension(char *name, char *extension)
729 {   int i;
730
731     /* If a filename ends in '.', remove the dot and add no file extension: */
732     i = strlen(name)-1;
733     if (name[i] == '.') { name[i]=0; return ""; }
734
735     /* Remove the new extension if it's already got one: */
736
737     for (; (i>=0) && (name[i]!=FN_SEP); i--)
738         if (name[i] == '.') return "";
739     return extension;
740 }
741 #endif
742
743 /* ------------------------------------------------------------------------- */
744 /*    Three translation routines have to deal with path variables which may  */
745 /*    contain alternative locations separated by the FN_ALT character.       */
746 /*    These have the protocol:                                               */
747 /*                                                                           */
748 /*        int translate_*_filename(int last_value, ...)                      */
749 /*                                                                           */
750 /*    and should first be called with last_value equal to 0.  If the         */
751 /*    translated filename works, fine.  Otherwise, if the returned integer   */
752 /*    was zero, the caller knows that no filename works and can issue an     */
753 /*    error message.  If it was non-zero, the caller should pass it on as    */
754 /*    the last_value again.                                                  */
755 /*                                                                           */
756 /*    As implemented below, last_value is the position in the path variable  */
757 /*    string at which the next directory name to try begins.                 */
758 /* ------------------------------------------------------------------------- */
759
760 extern int translate_in_filename(int last_value,
761     char *new_name, char *old_name,
762     int same_directory_flag, int command_line_flag)
763 {   char *prefix_path = NULL;
764     char *extension;
765     int add_path_flag = 1;
766     int i;
767
768     if ((same_directory_flag==0)
769         && (contains_separator(old_name)==1)) add_path_flag=0;
770
771     if (add_path_flag==1)
772     {   if (command_line_flag == 0)
773         {   /* File is opened as a result of an Include directive */
774
775             if (same_directory_flag==1)
776                 prefix_path = current_source_path;
777             else
778                 if (Include_Path[0]!=0) prefix_path = Include_Path;
779         }
780         /* Main file being opened from the command line */
781
782         else if (Source_Path[0]!=0) prefix_path = Source_Path;
783     }
784
785 #ifdef FILE_EXTENSIONS
786     /* Which file extension is expected? */
787
788     if ((command_line_flag==1)||(same_directory_flag==1))
789         extension = Source_Extension;
790     else
791         extension = Include_Extension;
792
793     extension = check_extension(old_name, extension);
794 #else
795     extension = "";
796 #endif
797
798     last_value = write_translated_name(new_name, old_name,
799                      prefix_path, last_value, extension);
800
801     /* Set the "current source path" (for use of Include ">...") */
802
803     if (command_line_flag==1)
804     {   strcpy(current_source_path, new_name);
805         for (i=strlen(current_source_path)-1;
806              ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
807
808         if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir   */
809         else current_source_path[0] = 0;        /* Current file at root dir */
810     }
811
812     return last_value;
813 }
814
815 extern int translate_link_filename(int last_value,
816     char *new_name, char *old_name)
817 {   char *prefix_path = NULL;
818     char *extension;
819
820     if (contains_separator(old_name)==0)
821         if (Module_Path[0]!=0)
822             prefix_path = Module_Path;
823
824 #ifdef FILE_EXTENSIONS
825     extension = check_extension(old_name, Module_Extension);
826 #else
827     extension = "";
828 #endif
829
830     return write_translated_name(new_name, old_name,
831                prefix_path, last_value, extension);
832 }
833
834 static int translate_icl_filename(int last_value,
835     char *new_name, char *old_name)
836 {   char *prefix_path = NULL;
837     char *extension = "";
838
839     if (contains_separator(old_name)==0)
840         if (ICL_Path[0]!=0)
841             prefix_path = ICL_Path;
842
843 #ifdef FILE_EXTENSIONS
844     extension = check_extension(old_name, ICL_Extension);
845 #endif
846
847     return write_translated_name(new_name, old_name,
848                prefix_path, last_value, extension);
849 }
850
851 extern void translate_out_filename(char *new_name, char *old_name)
852 {   char *prefix_path;
853     char *extension = "";
854     int i;
855
856     /* If !convert_filename_flag, then the old_name is just the <file2>
857        parameter on the Inform command line, which we leave alone. */
858
859     if (!convert_filename_flag)
860     {   strcpy(new_name, old_name); return;
861     }
862
863     /* Remove any pathname or extension in <file1>. */
864
865     if (contains_separator(old_name)==1)
866     {   for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
867         if (old_name[i]==FN_SEP) i++;
868         old_name += i;
869     }
870 #ifdef FILE_EXTENSIONS
871     for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
872     if (old_name[i] == '.') old_name[i] = 0;
873 #endif
874
875     prefix_path = NULL;
876     if (module_switch)
877     {   extension = Module_Extension;
878         if (Module_Path[0]!=0) prefix_path = Module_Path;
879     }
880     else
881     {
882         if (!glulx_mode) {
883             switch(version_number)
884             {   case 3: extension = Code_Extension;   break;
885                 case 4: extension = V4Code_Extension; break;
886                 case 5: extension = V5Code_Extension; break;
887                 case 6: extension = V6Code_Extension; break;
888                 case 7: extension = V7Code_Extension; break;
889                 case 8: extension = V8Code_Extension; break;
890             }
891         }
892         else {
893             extension = GlulxCode_Extension;
894         }
895         if (Code_Path[0]!=0) prefix_path = Code_Path;
896     }
897
898 #ifdef FILE_EXTENSIONS
899     extension = check_extension(old_name, extension);
900 #endif
901
902     write_translated_name(new_name, old_name, prefix_path, 0, extension);
903 }
904
905 static char *name_or_unset(char *p)
906 {   if (p[0]==0) return "(unset)";
907     return p;
908 }
909
910 static void help_on_filenames(void)
911 {   char old_name[PATHLEN];
912     char new_name[PATHLEN];
913     int save_mm = module_switch, x;
914
915     module_switch = FALSE;
916
917     printf("Help information on filenames:\n\n");
918
919     printf(
920 "The command line can take one of two forms:\n\n\
921   inform [commands...] <file1>\n\
922   inform [commands...] <file1> <file2>\n\n\
923 Inform translates <file1> into a source file name (see below) for its input.\n\
924 <file2> is usually omitted: if so, the output filename is made from <file1>\n\
925 by cutting out the name part and translating that (see below).\n\
926 If <file2> is given, however, the output filename is set to just <file2>\n\
927 (not altered in any way).\n\n");
928
929     printf(
930 "Filenames given in the game source (with commands like Include \"name\" and\n\
931 Link \"name\") are also translated by the rules below.\n\n");
932
933     printf(
934 "Rules of translation:\n\n\
935 Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
936 (such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
937 1. If the name contains a '%c' character (so it's already a pathname), it\n\
938    isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
939
940     printf(
941 "   [Exception: when the name is given in an Include command using the >\n\
942    form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
943    of the file doing the inclusion");
944 #ifdef FILE_EXTENSIONS
945                           printf(" and a suitable file extension is added");
946 #endif
947     printf(".]\n\n");
948
949     printf(
950 "   Filenames must never contain double-quotation marks \".  To use filenames\n\
951    which contain spaces, write them in double-quotes: for instance,\n\n\
952    \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
953
954     printf(
955 "2. The file is looked for at a particular \"path\" (the filename of a\n\
956    directory), depending on what kind of file it is.\n\n\
957        File type              Name                Current setting\n\n\
958        Source code (in)       source_path         %s\n\
959        Include file (in)      include_path        %s\n\
960        Story file (out)       code_path           %s\n",
961    name_or_unset(Source_Path), name_or_unset(Include_Path),
962    name_or_unset(Code_Path));
963
964     printf(
965 "       Temporary file (out)   temporary_path      %s\n\
966        ICL command file (in)  icl_path            %s\n\
967        Module (in & out)      module_path         %s\n\n",
968    name_or_unset(Temporary_Path),
969    name_or_unset(ICL_Path), name_or_unset(Module_Path));
970
971     printf(
972 "   If the path is unset, then the current working directory is used (so\n\
973    the filename doesn't change): if, for instance, include_path is set to\n\
974    \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
975    \"backup%coldlib%cparser\".\n\n\
976    The paths can be set or unset on the Inform command line by, eg,\n\
977    \"inform +code_path=finished jigsaw\" or\n\
978    \"inform +include_path= balances\" (which unsets include_path).\n\n",
979         FN_SEP, FN_SEP, FN_SEP);
980
981     printf(
982 "   The four input path variables can be set to lists of alternative paths\n\
983    separated by '%c' characters: these alternatives are always tried in\n\
984    the order they are specified in, that is, left to right through the text\n\
985    in the path variable.\n\n",
986    FN_ALT);
987     printf(
988 "   If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
989    the path or paths are added to the existing list.\n\n");
990     printf(
991 "   (Modules are written to the first alternative in the module_path list;\n\
992    it is an error to give alternatives at all for purely output paths.)\n\n");
993
994 #ifdef FILE_EXTENSIONS
995     printf("3. The following file extensions are added:\n\n\
996       Source code:     %s\n\
997       Include files:   %s\n\
998       Story files:     %s (Version 3), %s (v4), %s (v5, the default),\n\
999                        %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
1000       Temporary files: .tmp\n\
1001       Modules:         %s\n\n",
1002       Source_Extension, Include_Extension,
1003       Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
1004       V7Code_Extension, V8Code_Extension, GlulxCode_Extension, 
1005       Module_Extension);
1006     printf("\
1007    except that any extension you give (on the command line or in a filename\n\
1008    used in a program) will override these.  If you give the null extension\n\
1009    \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
1010 #endif
1011
1012     printf("Names of four individual files can also be set using the same\n\
1013   + command notation (though they aren't really pathnames).  These are:\n\n\
1014       transcript_name  (text written by -r switch): now \"%s\"\n\
1015       debugging_name   (data written by -k switch): now \"%s\"\n\
1016       language_name    (library file defining natural language of game):\n\
1017                        now \"%s\"\n\
1018       charset_map      (file for character set mapping): now \"%s\"\n\n",
1019     Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
1020
1021     translate_in_filename(0, new_name, "rezrov", 0, 1);
1022     printf("Examples: 1. \"inform rezrov\"\n\
1023   the source code is read from \"%s\"\n",
1024         new_name);
1025     convert_filename_flag = TRUE;
1026     translate_out_filename(new_name, "rezrov");
1027     printf("  and a story file is compiled to \"%s\".\n\n", new_name);
1028
1029     translate_in_filename(0, new_name, "frotz", 0, 1);
1030     printf("2. \"inform -M frotz\"\n\
1031   the source code is read from \"%s\"\n",
1032         new_name);
1033     module_switch = TRUE;
1034     convert_filename_flag = TRUE;
1035     translate_out_filename(new_name, "frotz");
1036     printf("  and a module is compiled to \"%s\".\n\n", new_name);
1037
1038     module_switch = FALSE;
1039
1040     sprintf(old_name, "demos%cplugh", FN_SEP);
1041     printf("3. \"inform %s\"\n", old_name);
1042     translate_in_filename(0, new_name, old_name, 0, 1);
1043     printf("  the source code is read from \"%s\"\n", new_name);
1044     sprintf(old_name, "demos%cplugh", FN_SEP);
1045     convert_filename_flag = TRUE;
1046     translate_out_filename(new_name, old_name);
1047     printf("  and a story file is compiled to \"%s\".\n\n", new_name);
1048
1049     printf("4. \"inform plover my_demo\"\n");
1050     translate_in_filename(0, new_name, "plover", 0, 1);
1051     printf("  the source code is read from \"%s\"\n", new_name);
1052     convert_filename_flag = FALSE;
1053     translate_out_filename(new_name, "my_demo");
1054     printf("  and a story file is compiled to \"%s\".\n\n", new_name);
1055
1056     strcpy(old_name, Source_Path);
1057     sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
1058         FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
1059     printf("5. \"inform +source_path=%s zooge\"\n", new_name);
1060     printf(
1061 "   Note that four alternative paths are given, the first being the empty\n\
1062    path-name (meaning: where you are now).  Inform looks for the source code\n\
1063    by trying these four places in turn, stopping when it finds anything:\n\n");
1064
1065     set_path_value(Source_Path, new_name);
1066     x = 0;
1067     do
1068     {   x = translate_in_filename(x, new_name, "zooge", 0, 1);
1069         printf("     \"%s\"\n", new_name);
1070     } while (x != 0);
1071     strcpy(Source_Path, old_name);
1072     module_switch = save_mm;
1073 }
1074
1075 /* ------------------------------------------------------------------------- */
1076 /*  Naming temporary files                                                   */
1077 /*       (Arguably temporary files should be made using "tmpfile" in         */
1078 /*        the ANSI C library, but many supposed ANSI libraries lack it.)     */
1079 /* ------------------------------------------------------------------------- */
1080
1081 extern void translate_temp_filename(int i)
1082 {   char *p = NULL;
1083     switch(i)
1084     {   case 1: p=Temp1_Name; break;
1085         case 2: p=Temp2_Name; break;
1086         case 3: p=Temp3_Name; break;
1087         default: return;
1088     }
1089     if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
1090         printf ("Temporary_Path is too long.\n");
1091         exit(1);
1092     }
1093     sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
1094 #ifdef INCLUDE_TASK_ID
1095     sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
1096 #endif
1097 #ifdef FILE_EXTENSIONS
1098     sprintf(p+strlen(p), ".tmp");
1099 #endif
1100 }
1101
1102 #ifdef ARCHIMEDES
1103 static char riscos_ft_buffer[4];
1104
1105 extern char *riscos_file_type(void)
1106 {
1107     if (riscos_file_type_format == 1)
1108     {   if (module_switch) return("data");
1109         return("11A");
1110     }
1111
1112     if (module_switch) return("075");
1113
1114     sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
1115     return(riscos_ft_buffer);
1116 }
1117 #endif
1118
1119 /* ------------------------------------------------------------------------- */
1120 /*   The compilation pass                                                    */
1121 /* ------------------------------------------------------------------------- */
1122
1123 static void run_pass(void)
1124 {
1125     lexer_begin_prepass();
1126     files_begin_prepass();
1127     load_sourcefile(Source_Name, 0);
1128
1129     begin_pass();
1130
1131     parse_program(NULL);
1132
1133     find_the_actions();
1134     issue_unused_warnings();
1135     compile_veneer();
1136
1137     lexer_endpass();
1138     if (module_switch) linker_endpass();
1139
1140     close_all_source();
1141     if (hash_switch && hash_printed_since_newline) printf("\n");
1142
1143     if (temporary_files_switch)
1144     {   if (module_switch) flush_link_data();
1145         check_temp_files();
1146     }
1147     sort_dictionary();
1148     if (track_unused_routines)
1149         locate_dead_functions();
1150     construct_storyfile();
1151 }
1152
1153 int output_has_occurred;
1154
1155 static void rennab(int32 time_taken)
1156 {   /*  rennab = reverse of banner  */
1157
1158     int t = no_warnings + no_suppressed_warnings;
1159
1160     if (memout_switch) print_memory_usage();
1161
1162     if ((no_errors + t)!=0)
1163     {   printf("Compiled with ");
1164         if (no_errors > 0)
1165         {   printf("%d error%s", no_errors,(no_errors==1)?"":"s");
1166             if (t > 0) printf(" and ");
1167         }
1168         if (no_warnings > 0)
1169             printf("%d warning%s", t, (t==1)?"":"s");
1170         if (no_suppressed_warnings > 0)
1171         {   if (no_warnings > 0)
1172                 printf(" (%d suppressed)", no_suppressed_warnings);
1173             else
1174             printf("%d suppressed warning%s", no_suppressed_warnings,
1175                 (no_suppressed_warnings==1)?"":"s");
1176         }
1177         if (output_has_occurred == FALSE) printf(" (no output)");
1178         printf("\n");
1179     }
1180
1181     if (no_compiler_errors > 0) print_sorry_message();
1182
1183     if (statistics_switch)
1184         printf("Completed in %ld seconds\n", (long int) time_taken);
1185 }
1186
1187 /* ------------------------------------------------------------------------- */
1188 /*   The compiler abstracted to a routine.                                   */
1189 /* ------------------------------------------------------------------------- */
1190
1191 static int execute_icl_header(char *file1);
1192
1193 static int compile(int number_of_files_specified, char *file1, char *file2)
1194 {   int32 time_start;
1195
1196     if (execute_icl_header(file1))
1197       return 1;
1198
1199     select_target(glulx_mode);
1200
1201     if (define_INFIX_switch && glulx_mode) {
1202         printf("Infix (-X) facilities are not available in Glulx: \
1203 disabling -X switch\n");
1204         define_INFIX_switch = FALSE;
1205     }
1206
1207     if (module_switch && glulx_mode) {
1208         printf("Modules are not available in Glulx: \
1209 disabling -M switch\n");
1210         module_switch = FALSE;
1211     }
1212
1213     if (define_INFIX_switch && module_switch)
1214     {   printf("Infix (-X) facilities are not available when compiling \
1215 modules: disabling -X switch\n");
1216         define_INFIX_switch = FALSE;
1217     }
1218     if (runtime_error_checking_switch && module_switch)
1219     {   printf("Strict checking (-S) facilities are not available when \
1220 compiling modules: disabling -S switch\n");
1221         runtime_error_checking_switch = FALSE;
1222     }
1223
1224     time_start=time(0); no_compilations++;
1225
1226     strcpy(Source_Name, file1); convert_filename_flag = TRUE;
1227     strcpy(Code_Name, file1);
1228     if (number_of_files_specified == 2)
1229     {   strcpy(Code_Name, file2); convert_filename_flag = FALSE;
1230     }
1231
1232     init_vars();
1233
1234     if (debugfile_switch) begin_debug_file();
1235
1236     allocate_arrays();
1237
1238     if (transcript_switch) open_transcript_file(Source_Name);
1239
1240     run_pass();
1241
1242     if (transcript_switch)
1243     {   write_dictionary_to_transcript();
1244         close_transcript_file();
1245     }
1246
1247     if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
1248     else { output_has_occurred = FALSE; }
1249
1250     if (debugfile_switch)
1251     {   end_debug_file();
1252     }
1253
1254     if (temporary_files_switch && (no_errors>0)) remove_temp_files();
1255
1256     free_arrays();
1257
1258     rennab((int32) (time(0)-time_start));
1259
1260     if (optimise_switch) optimise_abbreviations();
1261
1262     if (store_the_text) my_free(&all_text,"transcription text");
1263
1264     return (no_errors==0)?0:1;
1265 }
1266
1267 /* ------------------------------------------------------------------------- */
1268 /*   The command line interpreter                                            */
1269 /* ------------------------------------------------------------------------- */
1270
1271 static void cli_print_help(int help_level)
1272 {
1273     printf(
1274 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
1275 story files, as well as \"Glulx\" story files:\n\
1276 Copyright (c) Graham Nelson 1993 - 2021.\n\n");
1277
1278    /* For people typing just "inform", a summary only: */
1279
1280    if (help_level==0)
1281    {
1282
1283 #ifndef PROMPT_INPUT
1284   printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
1285 #else
1286   printf("When run, Inform prompts you for commands (and switches),\n\
1287 which are optional, then an input <file1> and an (optional) output\n\
1288 <file2>.\n\n");
1289 #endif
1290
1291   printf(
1292 "<file1> is the Inform source file of the game to be compiled. <file2>,\n\
1293 if given, overrides the filename Inform would normally use for the\n\
1294 compiled output.  Try \"inform -h1\" for file-naming conventions.\n\n\
1295 One or more words can be supplied as \"commands\". These may be:\n\n\
1296   -switches     a list of compiler switches, 1 or 2 letter\n\
1297                 (see \"inform -h2\" for the full range)\n\n\
1298   +dir          set Include_Path to this directory\n\
1299   ++dir         add this directory to Include_Path\n\
1300   +PATH=dir     change the PATH to this directory\n\
1301   ++PATH=dir    add this directory to the PATH\n\n\
1302   $...          one of the following configuration commands:\n");
1303   
1304   printf(
1305 "     $list            list current settings\n\
1306      $huge            make standard \"huge game\" settings %s\n\
1307      $large           make standard \"large game\" settings %s\n\
1308      $small           make standard \"small game\" settings %s\n\
1309      $?SETTING        explain briefly what SETTING is for\n\
1310      $SETTING=number  change SETTING to given number\n\
1311      $#SYMBOL=number  define SYMBOL as a constant in the story\n\n",
1312     (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
1313     (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
1314     (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
1315
1316   printf(
1317 "  (filename)    read in a list of commands (in the format above)\n\
1318                 from this \"setup file\"\n\n");
1319
1320   printf("Alternate command-line formats for the above:\n\
1321   --help                 (this page)\n\
1322   --path PATH=dir\n\
1323   --addpath PATH=dir\n\
1324   --list\n\
1325   --size huge, --size large, --size small\n\
1326   --helpopt SETTING\n\
1327   --opt SETTING=number\n\
1328   --define SETTING=number\n\
1329   --config filename      (setup file)\n\n");
1330
1331 #ifndef PROMPT_INPUT
1332     printf("For example: \"inform -dexs $huge curses\".\n");
1333 #endif
1334
1335        return;
1336    }
1337
1338    /* The -h1 (filenaming) help information: */
1339
1340    if (help_level == 1) { help_on_filenames(); return; }
1341
1342    /* The -h2 (switches) help information: */
1343
1344    printf("Help on the full list of legal switch commands:\n\n\
1345   a   trace assembly-language (without hex dumps; see -t)\n\
1346   c   more concise error messages\n\
1347   d   contract double spaces after full stops in text\n\
1348   d2  contract double spaces after exclamation and question marks, too\n\
1349   e   economy mode (slower): make use of declared abbreviations\n");
1350
1351    printf("\
1352   f   frequencies mode: show how useful abbreviations are\n\
1353   g   traces calls to all game functions\n\
1354   g2  traces calls to all game and library functions\n\
1355   g3  traces calls to all functions (including veneer)\n\
1356   h   print general help information\n\
1357   h1  print help information on filenames and path options\n\
1358   h2  print help information on switches (this page)\n");
1359
1360    printf("\
1361   i   ignore default switches set within the file\n\
1362   j   list objects as constructed\n\
1363   k   output debugging information to \"%s\"\n\
1364   l   list every statement run through Inform (not implemented)\n\
1365   m   say how much memory has been allocated\n\
1366   n   print numbers of properties, attributes and actions\n",
1367           Debugging_Name);
1368    printf("\
1369   o   print offset addresses\n\
1370   p   give percentage breakdown of story file\n\
1371   q   keep quiet about obsolete usages\n\
1372   r   record all the text to \"%s\"\n\
1373   s   give statistics\n\
1374   t   trace assembly-language (with full hex dumps; see -a)\n",
1375       Transcript_Name);
1376
1377    printf("\
1378   u   work out most useful abbreviations (very very slowly)\n\
1379   v3  compile to version-3 (\"Standard\"/\"ZIP\") story file\n\
1380   v4  compile to version-4 (\"Plus\"/\"EZIP\") story file\n\
1381   v5  compile to version-5 (\"Advanced\"/\"XZIP\") story file: the default\n\
1382   v6  compile to version-6 (graphical/\"YZIP\") story file\n\
1383   v7  compile to version-7 (expanded \"Advanced\") story file\n\
1384   v8  compile to version-8 (expanded \"Advanced\") story file\n\
1385   w   disable warning messages\n\
1386   x   print # for every 100 lines compiled\n\
1387   y   trace linking system\n\
1388   z   print memory map of the virtual machine\n\n");
1389
1390 printf("\
1391   B   use big memory model (for large V6/V7 files)\n\
1392   C0  text character set is plain ASCII only\n\
1393   Cu  text character set is UTF-8\n\
1394   Cn  text character set is ISO 8859-n (n = 1 to 9)\n\
1395       (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
1396        7, Greek; 8, Hebrew; 9, Latin5.  Default is -C1.)\n");
1397 printf("  D   insert \"Constant DEBUG;\" automatically\n");
1398 printf("  E0  Archimedes-style error messages%s\n",
1399       (error_format==0)?" (current setting)":"");
1400 printf("  E1  Microsoft-style error messages%s\n",
1401       (error_format==1)?" (current setting)":"");
1402 printf("  E2  Macintosh MPW-style error messages%s\n",
1403       (error_format==2)?" (current setting)":"");
1404 #ifdef USE_TEMPORARY_FILES
1405 printf("  F0  use extra memory rather than temporary files\n");
1406 #else
1407 printf("  F1  use temporary files to reduce memory consumption\n");
1408 #endif
1409 printf("  G   compile a Glulx game file\n");
1410 printf("  H   use Huffman encoding to compress Glulx strings\n");
1411 printf("  M   compile as a Module for future linking\n");
1412
1413 #ifdef ARCHIMEDES
1414 printf("\
1415   R0  use filetype 060 + version number for games (default)\n\
1416   R1  use official Acorn filetype 11A for all games\n");
1417 #endif
1418 printf("  S   compile strict error-checking at run-time (on by default)\n");
1419 #ifdef ARC_THROWBACK
1420 printf("  T   enable throwback of errors in the DDE\n");
1421 #endif
1422 printf("  U   insert \"Constant USE_MODULES;\" automatically\n");
1423 printf("  V   print the version and date of this program\n");
1424 printf("  Wn  header extension table is at least n words (n = 3 to 99)\n");
1425 printf("  X   compile with INFIX debugging facilities present\n");
1426   printf("\n");
1427 }
1428
1429 extern void switches(char *p, int cmode)
1430 {   int i, s=1, state;
1431     /* Here cmode is 0 if switches list is from a "Switches" directive
1432        and 1 if from a "-switches" command-line or ICL list */
1433
1434     if (cmode==1)
1435     {   if (p[0]!='-')
1436         {   printf(
1437                 "Ignoring second word which should be a -list of switches.\n");
1438             return;
1439         }
1440     }
1441     for (i=cmode; p[i]!=0; i+=s, s=1)
1442     {   state = TRUE;
1443         if (p[i] == '~')
1444         {   state = FALSE;
1445             i++;
1446         }
1447         switch(p[i])
1448         {
1449         case 'a': asm_trace_setting = 1; break;
1450         case 'b': bothpasses_switch = state; break;
1451         case 'c': concise_switch = state; break;
1452         case 'd': switch(p[i+1])
1453                   {   case '1': double_space_setting=1; s=2; break;
1454                       case '2': double_space_setting=2; s=2; break;
1455                       default: double_space_setting=1; break;
1456                   }
1457                   break;
1458         case 'e': economy_switch = state; break;
1459         case 'f': frequencies_switch = state; break;
1460         case 'g': switch(p[i+1])
1461                   {   case '1': trace_fns_setting=1; s=2; break;
1462                       case '2': trace_fns_setting=2; s=2; break;
1463                       case '3': trace_fns_setting=3; s=2; break;
1464                       default: trace_fns_setting=1; break;
1465                   }
1466                   break;
1467         case 'h': switch(p[i+1])
1468                   {   case '1': cli_print_help(1); s=2; break;
1469                       case '2': cli_print_help(2); s=2; break;
1470                       case '0': s=2;
1471                       default:  cli_print_help(0); break;
1472                   }
1473                   break;
1474         case 'i': ignore_switches_switch = state; break;
1475         case 'j': listobjects_switch = state; break;
1476         case 'k': if (cmode == 0)
1477                       error("The switch '-k' can't be set with 'Switches'");
1478                   else
1479                       debugfile_switch = state;
1480                   break;
1481         case 'l': listing_switch = state; break;
1482         case 'm': memout_switch = state; break;
1483         case 'n': printprops_switch = state; break;
1484         case 'o': offsets_switch = state; break;
1485         case 'p': percentages_switch = state; break;
1486         case 'q': obsolete_switch = state; break;
1487         case 'r': if (cmode == 0)
1488                       error("The switch '-r' can't be set with 'Switches'");
1489                   else
1490                       transcript_switch = state; break;
1491         case 's': statistics_switch = state; break;
1492         case 't': asm_trace_setting=2; break;
1493         case 'u': if (cmode == 0) {
1494                       error("The switch '-u' can't be set with 'Switches'");
1495                       break;
1496                   }
1497                   optimise_switch = state; break;
1498         case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
1499                   if ((cmode==0) && (version_set_switch)) { s=2; break; }
1500                   version_set_switch = TRUE; s=2;
1501                   switch(p[i+1])
1502                   {   case '3': select_version(3); break;
1503                       case '4': select_version(4); break;
1504                       case '5': select_version(5); break;
1505                       case '6': select_version(6); break;
1506                       case '7': select_version(7); break;
1507                       case '8': select_version(8); break;
1508                       default:  printf("-v must be followed by 3 to 8\n");
1509                                 version_set_switch=0; s=1;
1510                                 break;
1511                   }
1512                   if ((version_number < 5) && (r_e_c_s_set == FALSE))
1513                       runtime_error_checking_switch = FALSE;
1514                   break;
1515         case 'w': nowarnings_switch = state; break;
1516         case 'x': hash_switch = state; break;
1517         case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
1518         case 'z': memory_map_switch = state; break;
1519         case 'B': oddeven_packing_switch = state; break;
1520         case 'C': s=2;
1521                   if (p[i+1] == 'u') {
1522                       character_set_unicode = TRUE;
1523                       /* Leave the set_setting on Latin-1, because that 
1524                          matches the first block of Unicode. */
1525                       character_set_setting = 1;
1526                   }
1527                   else 
1528                   {   character_set_setting=p[i+1]-'0';
1529                       if ((character_set_setting < 0)
1530                           || (character_set_setting > 9))
1531                       {   printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
1532                           character_set_unicode = FALSE;
1533                           character_set_setting = 1;
1534                       }
1535                   }
1536                   if (cmode == 0) change_character_set();
1537                   break;
1538         case 'D': define_DEBUG_switch = state; break;
1539         case 'E': switch(p[i+1])
1540                   {   case '0': s=2; error_format=0; break;
1541                       case '1': s=2; error_format=1; break;
1542                       case '2': s=2; error_format=2; break;
1543                       default:  error_format=1; break;
1544                   }
1545                   break;
1546         case 'F': if (cmode == 0) {
1547                       error("The switch '-F' can't be set with 'Switches'");
1548                       break;
1549                   }
1550                   switch(p[i+1])
1551                   {   case '0': s=2; temporary_files_switch = FALSE; break;
1552                       case '1': s=2; temporary_files_switch = TRUE; break;
1553                       default:  temporary_files_switch = state; break;
1554                   }
1555                   break;
1556         case 'M': module_switch = state;
1557                   if (state && (r_e_c_s_set == FALSE))
1558                       runtime_error_checking_switch = FALSE;
1559                   break;
1560 #ifdef ARCHIMEDES
1561         case 'R': switch(p[i+1])
1562                   {   case '0': s=2; riscos_file_type_format=0; break;
1563                       case '1': s=2; riscos_file_type_format=1; break;
1564                       default:  riscos_file_type_format=1; break;
1565                   }
1566                   break;
1567 #endif
1568 #ifdef ARC_THROWBACK
1569         case 'T': throwback_switch = state; break;
1570 #endif
1571         case 'S': runtime_error_checking_switch = state;
1572                   r_e_c_s_set = TRUE; break;
1573         case 'G': if (cmode == 0)
1574                       error("The switch '-G' can't be set with 'Switches'");
1575                   else if (version_set_switch)
1576                       error("The '-G' switch cannot follow the '-v' switch");
1577                   else
1578                   {   glulx_mode = state;
1579                       adjust_memory_sizes();
1580                   }
1581                   break;
1582         case 'H': compression_switch = state; break;
1583         case 'U': define_USE_MODULES_switch = state; break;
1584         case 'V': exit(0); break;
1585         case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
1586                   {   s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
1587                       if ((p[i+2]>='0') && (p[i+2]<='9'))
1588                       {   s=3; ZCODE_HEADER_EXT_WORDS *= 10;
1589                           ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
1590                       }
1591                   }
1592                   break;
1593         case 'X': define_INFIX_switch = state; break;
1594         default:
1595           printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
1596               p[i]);
1597           break;
1598         }
1599     }
1600
1601     if (optimise_switch && (!store_the_text))
1602     {   store_the_text=TRUE;
1603 #ifdef PC_QUICKC
1604         if (memout_switch)
1605             printf("Allocation %ld bytes for transcription text\n",
1606                 (long) MAX_TRANSCRIPT_SIZE);
1607         all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
1608         malloced_bytes += MAX_TRANSCRIPT_SIZE;
1609         if (all_text==NULL)
1610          fatalerror("Can't hallocate memory for transcription text.  Darn.");
1611 #else
1612         all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
1613 #endif
1614     }
1615 }
1616
1617 static int icl_command(char *p)
1618 {   if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
1619         || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
1620     return FALSE;
1621 }
1622
1623 static void icl_error(char *filename, int line)
1624 {   printf("Error in ICL file '%s', line %d:\n", filename, line);
1625 }
1626
1627 static void icl_header_error(char *filename, int line)
1628 {   printf("Error in ICL header of file '%s', line %d:\n", filename, line);
1629 }
1630
1631 static int copy_icl_word(char *from, char *to, int max)
1632 {
1633     /*  Copies one token from 'from' to 'to', null-terminated:
1634         returns the number of chars in 'from' read past (possibly 0).  */
1635
1636     int i, j, quoted_mode, truncated;
1637
1638     i = 0; truncated = 0;
1639     while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
1640            || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
1641
1642     if (from[i] == '!')
1643     {   while (from[i] != 0) i++;
1644         to[0] = 0; return i;
1645     }
1646
1647     for (quoted_mode = FALSE, j=0;;)
1648     {   if (from[i] == 0) break;
1649         if (from[i] == 10) break;
1650         if (from[i] == 13) break;
1651         if (from[i] == TAB_CHARACTER) break;
1652         if ((from[i] == ' ') && (!quoted_mode)) break;
1653         if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
1654         else to[j++] = from[i++];
1655         if (j == max) {
1656             j--;
1657             truncated = 1;
1658         }
1659     }
1660     to[j] = 0;
1661     if (truncated == 1)
1662         printf("The following parameter has been truncated:\n%s\n", to);
1663     return i;
1664 }
1665
1666 /* Copy a string, converting to uppercase. The to array should be
1667    (at least) max characters. Result will be null-terminated, so
1668    at most max-1 characters will be copied. 
1669 */
1670 static int strcpyupper(char *to, char *from, int max)
1671 {
1672     int ix;
1673     for (ix=0; ix<max-1; ix++) {
1674         char ch = from[ix];
1675         if (islower(ch)) ch = toupper(ch);
1676         to[ix] = ch;
1677     }
1678     to[ix] = 0;
1679     return ix;
1680 }
1681
1682 static void execute_icl_command(char *p);
1683 static int execute_dashdash_command(char *p, char *p2);
1684
1685 static int execute_icl_header(char *argname)
1686 {
1687   FILE *command_file;
1688   char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1689   int line = 0;
1690   int errcount = 0;
1691   int i;
1692   char filename[PATHLEN]; 
1693   int x = 0;
1694
1695   do
1696     {   x = translate_in_filename(x, filename, argname, 0, 1);
1697         command_file = fopen(filename,"r");
1698     } while ((command_file == NULL) && (x != 0));
1699   if (!command_file) {
1700     /* Fail silently. The regular compiler will try to open the file
1701        again, and report the problem. */
1702     return 0;
1703   }
1704
1705   while (feof(command_file)==0) {
1706     if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1707     line++;
1708     if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
1709       break;
1710     i = copy_icl_word(cli_buff+2, fw, CMD_BUF_SIZE);
1711     if (icl_command(fw)) {
1712       execute_icl_command(fw);
1713       copy_icl_word(cli_buff+2 + i, fw, CMD_BUF_SIZE);
1714       if ((fw[0] != 0) && (fw[0] != '!')) {
1715         icl_header_error(filename, line);
1716         errcount++;
1717         printf("expected comment or nothing but found '%s'\n", fw);
1718       }
1719     }
1720     else {
1721       if (fw[0]!=0) {
1722         icl_header_error(filename, line);
1723         errcount++;
1724         printf("Expected command or comment but found '%s'\n", fw);
1725       }
1726     }
1727   }
1728   fclose(command_file);
1729
1730   return (errcount==0)?0:1;
1731 }
1732
1733
1734 static void run_icl_file(char *filename, FILE *command_file)
1735 {   char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1736     int i, x, line = 0;
1737     printf("[Running ICL file '%s']\n", filename);
1738
1739     while (feof(command_file)==0)
1740     {   if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1741         line++;
1742         i = copy_icl_word(cli_buff, fw, CMD_BUF_SIZE);
1743         if (icl_command(fw))
1744         {   execute_icl_command(fw);
1745             copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1746             if ((fw[0] != 0) && (fw[0] != '!'))
1747             {   icl_error(filename, line);
1748                 printf("expected comment or nothing but found '%s'\n", fw);
1749             }
1750         }
1751         else
1752         {   if (strcmp(fw, "compile")==0)
1753             {   char story_name[PATHLEN], code_name[PATHLEN];
1754                 i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
1755                 i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
1756
1757                 if (code_name[0] != 0) x=2;
1758                 else if (story_name[0] != 0) x=1;
1759                 else x=0;
1760
1761                 switch(x)
1762                 {   case 0: icl_error(filename, line);
1763                             printf("No filename given to 'compile'\n");
1764                             break;
1765                     case 1: printf("[Compiling <%s>]\n", story_name);
1766                             compile(x, story_name, code_name);
1767                             break;
1768                     case 2: printf("[Compiling <%s> to <%s>]\n",
1769                                 story_name, code_name);
1770                             compile(x, story_name, code_name);
1771                             copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1772                             if (fw[0]!=0)
1773                             {   icl_error(filename, line);
1774                         printf("Expected comment or nothing but found '%s'\n",
1775                                 fw);
1776                             }
1777                             break;
1778                 }
1779             }
1780             else
1781             if (fw[0]!=0)
1782             {   icl_error(filename, line);
1783                 printf("Expected command or comment but found '%s'\n", fw);
1784             }
1785         }
1786     }
1787 }
1788
1789 /* This should only be called if the argument has been verified to be
1790    an ICL command, e.g. by checking icl_command().
1791 */
1792 static void execute_icl_command(char *p)
1793 {   char filename[PATHLEN], cli_buff[CMD_BUF_SIZE];
1794     FILE *command_file;
1795     int len;
1796     
1797     switch(p[0])
1798     {   case '+': set_path_command(p+1); break;
1799         case '-': switches(p,1); break;
1800         case '$': memory_command(p+1); break;
1801         case '(': len = strlen(p);
1802                   if (p[len-1] != ')') {
1803                       printf("Error in ICL: (command) missing closing paren\n");
1804                       break;
1805                   }
1806                   len -= 2; /* omit parens */
1807                   if (len > CMD_BUF_SIZE-1) len = CMD_BUF_SIZE-1;
1808                   strncpy(cli_buff, p+1, len);
1809                   cli_buff[len]=0;
1810                   {   int x = 0;
1811                       do
1812                       {   x = translate_icl_filename(x, filename, cli_buff);
1813                           command_file = fopen(filename,"r");
1814                       } while ((command_file == NULL) && (x != 0));
1815                   }
1816                   if (command_file == NULL) {
1817                       printf("Error in ICL: Couldn't open command file '%s'\n",
1818                           filename);
1819                       break;
1820                   }
1821                   run_icl_file(filename, command_file);
1822                   fclose(command_file);
1823                   break;
1824     }
1825 }
1826
1827 /* Convert a --command into the equivalent ICL command and call 
1828    execute_icl_command(). The dashes have already been stripped.
1829
1830    The second argument is the following command-line argument 
1831    (or NULL if there was none). This may or may not be consumed.
1832    Returns TRUE if it was.
1833 */
1834 static int execute_dashdash_command(char *p, char *p2)
1835 {
1836     char cli_buff[CMD_BUF_SIZE];
1837     int consumed2 = FALSE;
1838     
1839     if (!strcmp(p, "help")) {
1840         strcpy(cli_buff, "-h");
1841     }
1842     else if (!strcmp(p, "list")) {
1843         strcpy(cli_buff, "$LIST");
1844     }
1845     else if (!strcmp(p, "size")) {
1846         consumed2 = TRUE;
1847         if (!(p2 && (!strcmpcis(p2, "HUGE") || !strcmpcis(p2, "LARGE") || !strcmpcis(p2, "SMALL")))) {
1848             printf("--size must be followed by \"huge\", \"large\", or \"small\"\n");
1849             return consumed2;
1850         }
1851         strcpy(cli_buff, "$");
1852         strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1853     }
1854     else if (!strcmp(p, "opt")) {
1855         consumed2 = TRUE;
1856         if (!p2 || !strchr(p2, '=')) {
1857             printf("--opt must be followed by \"setting=number\"\n");
1858             return consumed2;
1859         }
1860         strcpy(cli_buff, "$");
1861         strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1862     }
1863     else if (!strcmp(p, "helpopt")) {
1864         consumed2 = TRUE;
1865         if (!p2) {
1866             printf("--helpopt must be followed by \"setting\"\n");
1867             return consumed2;
1868         }
1869         strcpy(cli_buff, "$?");
1870         strcpyupper(cli_buff+2, p2, CMD_BUF_SIZE-2);
1871     }
1872     else if (!strcmp(p, "define")) {
1873         consumed2 = TRUE;
1874         if (!p2) {
1875             printf("--define must be followed by \"symbol=number\"\n");
1876             return consumed2;
1877         }
1878         strcpy(cli_buff, "$#");
1879         strcpyupper(cli_buff+2, p2, CMD_BUF_SIZE-2);
1880     }
1881     else if (!strcmp(p, "path")) {
1882         consumed2 = TRUE;
1883         if (!p2 || !strchr(p2, '=')) {
1884             printf("--path must be followed by \"name=path\"\n");
1885             return consumed2;
1886         }
1887         snprintf(cli_buff, CMD_BUF_SIZE, "+%s", p2);
1888     }
1889     else if (!strcmp(p, "addpath")) {
1890         consumed2 = TRUE;
1891         if (!p2 || !strchr(p2, '=')) {
1892             printf("--addpath must be followed by \"name=path\"\n");
1893             return consumed2;
1894         }
1895         snprintf(cli_buff, CMD_BUF_SIZE, "++%s", p2);
1896     }
1897     else if (!strcmp(p, "config")) {
1898         consumed2 = TRUE;
1899         if (!p2) {
1900             printf("--config must be followed by \"file.icl\"\n");
1901             return consumed2;
1902         }
1903         snprintf(cli_buff, CMD_BUF_SIZE, "(%s)", p2);
1904     }
1905     else {
1906         printf("Option \"--%s\" unknown (try \"inform -h\")\n", p);
1907         return FALSE;
1908     }
1909
1910     execute_icl_command(cli_buff);
1911     return consumed2;
1912 }
1913
1914 /* ------------------------------------------------------------------------- */
1915 /*   Opening and closing banners                                             */
1916 /* ------------------------------------------------------------------------- */
1917
1918 char banner_line[CMD_BUF_SIZE];
1919
1920 /* We store the banner text for use elsewhere (see files.c).
1921 */
1922 static void banner(void)
1923 {
1924     int len;
1925     snprintf(banner_line, CMD_BUF_SIZE, "Inform %d.%d%d",
1926         (VNUMBER/100)%10, (VNUMBER/10)%10, VNUMBER%10);
1927 #ifdef RELEASE_SUFFIX
1928     len = strlen(banner_line);
1929     snprintf(banner_line+len, CMD_BUF_SIZE-len, "%s", RELEASE_SUFFIX);
1930 #endif
1931 #ifdef MACHINE_STRING
1932     len = strlen(banner_line);
1933     snprintf(banner_line+len, CMD_BUF_SIZE-len, " for %s", MACHINE_STRING);
1934 #endif
1935     len = strlen(banner_line);
1936     snprintf(banner_line+len, CMD_BUF_SIZE-len, " (%s)", RELEASE_DATE);
1937     
1938     printf("%s\n", banner_line);
1939 }
1940
1941 /* ------------------------------------------------------------------------- */
1942 /*   Input from the outside world                                            */
1943 /* ------------------------------------------------------------------------- */
1944
1945 #ifdef PROMPT_INPUT
1946 static void read_command_line(int argc, char **argv)
1947 {   int i;
1948     char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
1949     i=0;
1950     printf("Source filename?\n> ");
1951     while (gets(buffer1)==NULL); cli_file1=buffer1;
1952     printf("Output filename (RETURN for the same)?\n> ");
1953     while (gets(buffer2)==NULL); cli_file2=buffer2;
1954     cli_files_specified=1;
1955     if (buffer2[0]!=0) cli_files_specified=2;
1956     do
1957     {   printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
1958         while (gets(buffer3)==NULL); execute_icl_command(buffer3);
1959     } while (buffer3[0]!=0);
1960 }
1961 #else
1962 static void read_command_line(int argc, char **argv)
1963 {   int i;
1964     if (argc==1) switches("-h",1);
1965
1966     for (i=1, cli_files_specified=0; i<argc; i++)
1967         if (argv[i][0] == '-' && argv[i][1] == '-') {
1968             char *nextarg = NULL;
1969             int consumed2;
1970             if (i+1 < argc) nextarg = argv[i+1];
1971             consumed2 = execute_dashdash_command(argv[i]+2, nextarg);
1972             if (consumed2 && i+1 < argc) {
1973                 i++;
1974             }
1975         }
1976         else if (icl_command(argv[i])) {
1977             execute_icl_command(argv[i]);
1978         }
1979         else {
1980             switch(++cli_files_specified)
1981             {   case 1: cli_file1 = argv[i]; break;
1982                 case 2: cli_file2 = argv[i]; break;
1983                 default:
1984                     printf("Command line error: unknown parameter '%s'\n",
1985                         argv[i]); return;
1986             }
1987         }
1988 }
1989 #endif
1990
1991 /* ------------------------------------------------------------------------- */
1992 /*   M A I N : An outer shell for machine-specific quirks                    */
1993 /*   Omitted altogether if EXTERNAL_SHELL is defined, as for instance is     */
1994 /*   needed for the Macintosh front end.                                     */
1995 /* ------------------------------------------------------------------------- */
1996
1997 #ifdef EXTERNAL_SHELL
1998 extern int sub_main(int argc, char **argv);
1999 #else
2000
2001 static int sub_main(int argc, char **argv);
2002 #ifdef MAC_MPW
2003 int main(int argc, char **argv, char *envp[])
2004 #else
2005 int main(int argc, char **argv)
2006 #endif
2007 {   int rcode;
2008 #ifdef MAC_MPW
2009     InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
2010 #endif
2011     rcode = sub_main(argc, argv);
2012 #ifdef ARC_THROWBACK
2013     throwback_end();
2014 #endif
2015     return rcode;
2016 }
2017
2018 #endif
2019
2020 /* ------------------------------------------------------------------------- */
2021 /*   M A I N  II:  Starting up ICL with the command line                     */
2022 /* ------------------------------------------------------------------------- */
2023
2024 #ifdef EXTERNAL_SHELL
2025 extern int sub_main(int argc, char **argv)
2026 #else
2027 static int sub_main(int argc, char **argv)
2028 #endif
2029 {   int return_code;
2030
2031 #ifdef MAC_FACE
2032     ProcessEvents (&g_proc);
2033     if (g_proc != true)
2034     {   free_arrays();
2035         if (store_the_text) my_free(&all_text,"transcription text");
2036         longjmp (g_fallback, 1);
2037     }
2038 #endif
2039
2040     banner();
2041
2042     set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
2043     reset_switch_settings(); select_version(5);
2044
2045     cli_files_specified = 0; no_compilations = 0;
2046     cli_file1 = "source"; cli_file2 = "output";
2047
2048     read_command_line(argc, argv);
2049
2050     if (cli_files_specified > 0)
2051     {   return_code = compile(cli_files_specified, cli_file1, cli_file2);
2052
2053         if (return_code != 0) return(return_code);
2054     }
2055
2056     if (no_compilations == 0)
2057         printf("\n[No compilation requested]\n");
2058     if (no_compilations > 1)
2059         printf("[%d compilations completed]\n", no_compilations);
2060
2061     return(0);
2062 }
2063
2064 /* ========================================================================= */