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