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