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