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