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