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