1 /* ------------------------------------------------------------------------- */
2 /* "inform" : The top level of Inform: switches, pathnames, filenaming */
3 /* conventions, ICL (Inform Command Line) files, main */
5 /* Part of Inform 6.35 */
6 /* copyright (c) Graham Nelson 1993 - 2021 */
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. */
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. */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with Inform. If not, see https://gnu.org/licenses/ *
21 /* ------------------------------------------------------------------------- */
23 #define MAIN_INFORM_FILE
26 #define CMD_BUF_SIZE (256)
28 /* ------------------------------------------------------------------------- */
29 /* Compiler progress */
30 /* ------------------------------------------------------------------------- */
32 static int no_compilations;
34 int endofpass_flag; /* set to TRUE when an "end" directive is reached
35 (the inputs routines insert one into the stream
38 /* ------------------------------------------------------------------------- */
40 /* ------------------------------------------------------------------------- */
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
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 */
50 int32 requested_glulx_version;
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;
58 if (version_number==3) scale_factor = 2;
59 if (version_number==8) scale_factor = 8;
61 length_scale_factor = scale_factor;
62 if ((version_number==6)||(version_number==7)) length_scale_factor = 8;
64 instruction_set_number = version_number;
65 if ((version_number==7)||(version_number==8)) instruction_set_number = 5;
68 static int select_glulx_version(char *str)
70 /* Parse an "X.Y.Z" style version number, and store it for later use. */
72 int major=0, minor=0, patch=0;
75 major = major*10 + ((*cx++)-'0');
79 minor = minor*10 + ((*cx++)-'0');
83 patch = patch*10 + ((*cx++)-'0');
87 requested_glulx_version = ((major & 0x7FFF) << 16)
88 + ((minor & 0xFF) << 8)
93 /* ------------------------------------------------------------------------- */
94 /* Target: variables which vary between the Z-machine and Glulx */
95 /* ------------------------------------------------------------------------- */
97 int WORDSIZE; /* Size of a machine word: 2 or 4 */
98 int32 MAXINTWORD; /* 0x7FFF or 0x7FFFFFFF */
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.
104 int INDIV_PROP_START;
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.
110 int OBJECT_BYTE_LENGTH;
111 /* The total length of a dict entry, in bytes. Not used in Z-code.
113 int DICT_ENTRY_BYTE_LENGTH;
114 /* The position in a dict entry that the flag values begin.
117 int DICT_ENTRY_FLAG_POS;
119 static void select_target(int targ)
126 if (INDIV_PROP_START != 64) {
127 INDIV_PROP_START = 64;
128 fatalerror("You cannot change INDIV_PROP_START in Z-code");
130 if (DICT_WORD_SIZE != 6) {
132 fatalerror("You cannot change DICT_WORD_SIZE in Z-code");
134 if (DICT_CHAR_SIZE != 1) {
136 fatalerror("You cannot change DICT_CHAR_SIZE in Z-code");
138 if (NUM_ATTR_BYTES != 6) {
140 fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
142 if (MAX_LOCAL_VARIABLES != 16) {
143 MAX_LOCAL_VARIABLES = 16;
144 fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
146 if (MAX_GLOBAL_VARIABLES != 240) {
147 MAX_GLOBAL_VARIABLES = 240;
148 fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
150 if (MAX_VERBS > 255) {
152 fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
158 MAXINTWORD = 0x7FFFFFFF;
159 scale_factor = 0; /* It should never even get used in Glulx */
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);
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);
171 if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) {
173 warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE);
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
183 if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
184 DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
186 "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting",
188 /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */
190 if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) {
191 NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES;
193 "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting",
195 /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
198 /* Set up a few more variables that depend on the above values */
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;
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);
218 DICT_ENTRY_BYTE_LENGTH = (12+DICT_WORD_BYTES);
219 DICT_ENTRY_FLAG_POS = (4+DICT_WORD_BYTES);
225 /* The Z-machine's 96 abbreviations are used for these two purposes.
226 Make sure they are set consistently. If exactly one has been
227 set non-default, set the other to match. */
228 if (MAX_DYNAMIC_STRINGS == 32 && MAX_ABBREVS != 64) {
229 MAX_DYNAMIC_STRINGS = 96 - MAX_ABBREVS;
231 if (MAX_ABBREVS == 64 && MAX_DYNAMIC_STRINGS != 32) {
232 MAX_ABBREVS = 96 - MAX_DYNAMIC_STRINGS;
234 if (MAX_ABBREVS + MAX_DYNAMIC_STRINGS != 96
236 || MAX_DYNAMIC_STRINGS < 0) {
237 warning("MAX_ABBREVS plus MAX_DYNAMIC_STRINGS must be 96 in Z-code; resetting both");
238 MAX_DYNAMIC_STRINGS = 32;
243 if (MAX_DYNAMIC_STRINGS > 100) {
244 MAX_DYNAMIC_STRINGS = 100;
245 warning("MAX_DYNAMIC_STRINGS cannot exceed 100; resetting to 100");
246 /* This is because they are specified in text literals like "@00",
252 /* ------------------------------------------------------------------------- */
253 /* Tracery: output control variables */
254 /* ------------------------------------------------------------------------- */
256 int asm_trace_level, /* trace assembly: 0 for off, 1 for assembly
257 only, 2 for full assembly tracing with hex dumps */
258 line_trace_level, /* line tracing: 0 off, 1 on */
259 expr_trace_level, /* expression tracing: 0 off, 1 full, 2 brief */
260 linker_trace_level, /* set by -y: 0 to 4 levels of tracing */
261 tokens_trace_level; /* lexer output tracing: 0 off, 1 on */
263 /* ------------------------------------------------------------------------- */
264 /* On/off switch variables (by default all FALSE); other switch settings */
265 /* ------------------------------------------------------------------------- */
267 int bothpasses_switch, /* -b */
268 concise_switch, /* -c */
269 economy_switch, /* -e */
270 frequencies_switch, /* -f */
271 ignore_switches_switch, /* -i */
272 listobjects_switch, /* -j */
273 debugfile_switch, /* -k */
274 listing_switch, /* -l */
275 memout_switch, /* -m */
276 printprops_switch, /* -n */
277 offsets_switch, /* -o */
278 percentages_switch, /* -p */
279 obsolete_switch, /* -q */
280 transcript_switch, /* -r */
281 statistics_switch, /* -s */
282 optimise_switch, /* -u */
283 version_set_switch, /* -v */
284 nowarnings_switch, /* -w */
285 hash_switch, /* -x */
286 memory_map_switch, /* -z */
287 oddeven_packing_switch, /* -B */
288 define_DEBUG_switch, /* -D */
289 temporary_files_switch, /* -F */
290 module_switch, /* -M */
291 runtime_error_checking_switch, /* -S */
292 define_USE_MODULES_switch, /* -U */
293 define_INFIX_switch; /* -X */
295 int throwback_switch; /* -T */
298 int riscos_file_type_format; /* set by -R */
300 int compression_switch; /* set by -H */
301 int character_set_setting, /* set by -C0 through -C9 */
302 character_set_unicode, /* set by -Cu */
303 error_format, /* set by -E */
304 asm_trace_setting, /* set by -a and -t: value of
305 asm_trace_level to use when tracing */
306 double_space_setting, /* set by -d: 0, 1 or 2 */
307 trace_fns_setting, /* set by -g: 0, 1 or 2 */
308 linker_trace_setting, /* set by -y: ditto for linker_... */
309 store_the_text; /* when set, record game text to a chunk
310 of memory (used by both -r & -k) */
311 static int r_e_c_s_set; /* has -S been explicitly set? */
313 int glulx_mode; /* -G */
315 static void reset_switch_settings(void)
316 { asm_trace_setting=0;
317 linker_trace_level=0;
318 tokens_trace_level=0;
320 store_the_text = FALSE;
322 bothpasses_switch = FALSE;
323 concise_switch = FALSE;
324 double_space_setting = 0;
325 economy_switch = FALSE;
326 frequencies_switch = FALSE;
327 trace_fns_setting = 0;
328 ignore_switches_switch = FALSE;
329 listobjects_switch = FALSE;
330 debugfile_switch = FALSE;
331 listing_switch = FALSE;
332 memout_switch = FALSE;
333 printprops_switch = FALSE;
334 offsets_switch = FALSE;
335 percentages_switch = FALSE;
336 obsolete_switch = FALSE;
337 transcript_switch = FALSE;
338 statistics_switch = FALSE;
339 optimise_switch = FALSE;
340 version_set_switch = FALSE;
341 nowarnings_switch = FALSE;
343 memory_map_switch = FALSE;
344 oddeven_packing_switch = FALSE;
345 define_DEBUG_switch = FALSE;
346 #ifdef USE_TEMPORARY_FILES
347 temporary_files_switch = TRUE;
349 temporary_files_switch = FALSE;
351 define_USE_MODULES_switch = FALSE;
352 module_switch = FALSE;
354 throwback_switch = FALSE;
356 runtime_error_checking_switch = TRUE;
358 define_INFIX_switch = FALSE;
360 riscos_file_type_format = 0;
362 error_format=DEFAULT_ERROR_FORMAT;
364 character_set_setting = 1; /* Default is ISO Latin-1 */
365 character_set_unicode = FALSE;
367 compression_switch = TRUE;
369 requested_glulx_version = 0;
372 /* ------------------------------------------------------------------------- */
373 /* Number of files given as command line parameters (0, 1 or 2) */
374 /* ------------------------------------------------------------------------- */
376 static int cli_files_specified,
377 convert_filename_flag;
379 char Source_Name[PATHLEN]; /* Processed name of first input file */
380 char Code_Name[PATHLEN]; /* Processed name of output file */
382 static char *cli_file1, *cli_file2; /* Unprocessed (and unsafe to alter) */
384 /* ========================================================================= */
385 /* Data structure management routines */
386 /* ------------------------------------------------------------------------- */
388 static void init_vars(void)
396 init_expressc_vars();
397 init_expressp_vars();
412 static void begin_pass(void)
418 directs_begin_pass();
420 expressc_begin_pass();
421 expressp_begin_pass();
424 endofpass_flag = FALSE;
425 line_trace_level = 0; expr_trace_level = 0;
426 asm_trace_level = asm_trace_setting;
427 linker_trace_level = linker_trace_setting;
428 if (listing_switch) line_trace_level=1;
433 objects_begin_pass();
435 symbols_begin_pass();
444 /* Compile a Main__ routine (see "veneer.c") */
446 compile_initial_routine();
448 /* Make the four metaclasses: Class must be object number 1, so
449 it must come first */
454 make_class("Object");
455 make_class("Routine");
456 make_class("String");
462 extern void allocate_arrays(void)
464 arrays_allocate_arrays();
465 asm_allocate_arrays();
466 bpatch_allocate_arrays();
467 chars_allocate_arrays();
468 directs_allocate_arrays();
469 errors_allocate_arrays();
470 expressc_allocate_arrays();
471 expressp_allocate_arrays();
472 files_allocate_arrays();
474 lexer_allocate_arrays();
475 linker_allocate_arrays();
476 memory_allocate_arrays();
477 objects_allocate_arrays();
478 states_allocate_arrays();
479 symbols_allocate_arrays();
480 syntax_allocate_arrays();
481 tables_allocate_arrays();
482 text_allocate_arrays();
483 veneer_allocate_arrays();
484 verbs_allocate_arrays();
487 extern void free_arrays(void)
489 /* One array may survive this routine, all_the_text (used to hold
490 game text until the abbreviations optimiser begins work on it): this
491 array (if it was ever allocated) is freed at the top level. */
493 arrays_free_arrays();
495 bpatch_free_arrays();
497 directs_free_arrays();
498 errors_free_arrays();
499 expressc_free_arrays();
500 expressp_free_arrays();
504 linker_free_arrays();
505 memory_free_arrays();
506 objects_free_arrays();
507 states_free_arrays();
508 symbols_free_arrays();
509 syntax_free_arrays();
510 tables_free_arrays();
512 veneer_free_arrays();
516 /* ------------------------------------------------------------------------- */
517 /* Name translation code for filenames */
518 /* ------------------------------------------------------------------------- */
520 static char Source_Path[PATHLEN];
521 static char Include_Path[PATHLEN];
522 static char Code_Path[PATHLEN];
523 static char Module_Path[PATHLEN];
524 static char Temporary_Path[PATHLEN];
525 static char current_source_path[PATHLEN];
526 char Debugging_Name[PATHLEN];
527 char Transcript_Name[PATHLEN];
528 char Language_Name[PATHLEN];
529 char Charset_Map[PATHLEN];
530 static char ICL_Path[PATHLEN];
532 /* Set one of the above Path buffers to the given location, or list of
533 locations. (A list is comma-separated, and only accepted for Source_Path,
534 Include_Path, ICL_Path, Module_Path.)
536 static void set_path_value(char *path, char *value)
541 if (i >= PATHLEN-1) {
542 printf("A specified path is longer than %d characters.\n",
546 if ((value[j] == FN_ALT) || (value[j] == 0))
547 { if ((value[j] == FN_ALT)
548 && (path != Source_Path) && (path != Include_Path)
549 && (path != ICL_Path) && (path != Module_Path))
550 { printf("The character '%c' is used to divide entries in a list \
551 of possible locations, and can only be used in the Include_Path, Source_Path, \
552 Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
555 if ((path != Debugging_Name) && (path != Transcript_Name)
556 && (path != Language_Name) && (path != Charset_Map)
557 && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
558 path[i++] = value[j++];
559 if (value[j-1] == 0) return;
561 else path[i++] = value[j++];
565 /* Prepend the given location or list of locations to one of the above
566 Path buffers. This is only permitted for Source_Path, Include_Path,
567 ICL_Path, Module_Path.
569 An empty field (in the comma-separated list) means the current
570 directory. If the Path buffer is entirely empty, we assume that
571 we want to search both value and the current directory, so
572 the result will be "value,".
574 static void prepend_path_value(char *path, char *value)
577 int oldlen = strlen(path);
579 char new_path[PATHLEN];
581 if ((path != Source_Path) && (path != Include_Path)
582 && (path != ICL_Path) && (path != Module_Path))
583 { printf("The character '+' is used to add to a list \
584 of possible locations, and can only be used in the Include_Path, Source_Path, \
585 Module_Path or ICL_Path variables. Other paths are for output only.\n");
591 if (i >= PATHLEN-1) {
592 printf("A specified path is longer than %d characters.\n",
596 if ((value[j] == FN_ALT) || (value[j] == 0))
597 { if ((path != Debugging_Name) && (path != Transcript_Name)
598 && (path != Language_Name) && (path != Charset_Map)
599 && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
600 new_path[i++] = value[j++];
601 if (value[j-1] == 0) {
606 else new_path[i++] = value[j++];
609 if (newlen+1+oldlen >= PATHLEN-1) {
610 printf("A specified path is longer than %d characters.\n",
616 new_path[i++] = FN_ALT;
618 new_path[i++] = path[j++];
621 strcpy(path, new_path);
624 static void set_default_paths(void)
626 set_path_value(Source_Path, Source_Directory);
627 set_path_value(Include_Path, Include_Directory);
628 set_path_value(Code_Path, Code_Directory);
629 set_path_value(Module_Path, Module_Directory);
630 set_path_value(ICL_Path, ICL_Directory);
631 set_path_value(Temporary_Path, Temporary_Directory);
632 set_path_value(Debugging_Name, Debugging_File);
633 set_path_value(Transcript_Name, Transcript_File);
634 set_path_value(Language_Name, Default_Language);
635 set_path_value(Charset_Map, "");
638 /* Parse a path option which looks like "dir", "+dir", "pathname=dir",
639 or "+pathname=dir". If there is no "=", we assume "include_path=...".
640 If the option begins with a "+" the directory is prepended to the
641 existing path instead of replacing it.
643 static void set_path_command(char *command)
644 { int i, j; char *path_to_set = NULL;
647 if (command[0] == '+') {
652 for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
654 path_to_set=Include_Path;
656 if (command[i] == '=') {
657 char pathname[PATHLEN];
658 if (i>=PATHLEN) i=PATHLEN-1;
660 char ch = command[j];
661 if (isupper(ch)) ch=tolower(ch);
665 command = command+i+1;
668 if (strcmp(pathname, "source_path")==0) path_to_set=Source_Path;
669 if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
670 if (strcmp(pathname, "code_path")==0) path_to_set=Code_Path;
671 if (strcmp(pathname, "module_path")==0) path_to_set=Module_Path;
672 if (strcmp(pathname, "icl_path")==0) path_to_set=ICL_Path;
673 if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
674 if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
675 if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
676 if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
677 if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
679 if (path_to_set == NULL)
680 { printf("No such path setting as \"%s\"\n", pathname);
686 set_path_value(path_to_set, command);
688 prepend_path_value(path_to_set, command);
691 static int contains_separator(char *name)
693 for (i=0; name[i]!=0; i++)
694 if (name[i] == FN_SEP) return 1;
698 static int write_translated_name(char *new_name, char *old_name,
699 char *prefix_path, int start_pos,
702 if (strlen(old_name)+strlen(extension) >= PATHLEN) {
703 printf("One of your filenames is longer than %d characters.\n", PATHLEN);
706 if (prefix_path == NULL)
707 { sprintf(new_name,"%s%s", old_name, extension);
710 strcpy(new_name, prefix_path + start_pos);
711 for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
712 if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
713 if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
714 printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
717 sprintf(new_name + x, "%s%s", old_name, extension);
721 #ifdef FILE_EXTENSIONS
722 static char *check_extension(char *name, char *extension)
725 /* If a filename ends in '.', remove the dot and add no file extension: */
727 if (name[i] == '.') { name[i]=0; return ""; }
729 /* Remove the new extension if it's already got one: */
731 for (; (i>=0) && (name[i]!=FN_SEP); i--)
732 if (name[i] == '.') return "";
737 /* ------------------------------------------------------------------------- */
738 /* Three translation routines have to deal with path variables which may */
739 /* contain alternative locations separated by the FN_ALT character. */
740 /* These have the protocol: */
742 /* int translate_*_filename(int last_value, ...) */
744 /* and should first be called with last_value equal to 0. If the */
745 /* translated filename works, fine. Otherwise, if the returned integer */
746 /* was zero, the caller knows that no filename works and can issue an */
747 /* error message. If it was non-zero, the caller should pass it on as */
748 /* the last_value again. */
750 /* As implemented below, last_value is the position in the path variable */
751 /* string at which the next directory name to try begins. */
752 /* ------------------------------------------------------------------------- */
754 extern int translate_in_filename(int last_value,
755 char *new_name, char *old_name,
756 int same_directory_flag, int command_line_flag)
757 { char *prefix_path = NULL;
759 int add_path_flag = 1;
762 if ((same_directory_flag==0)
763 && (contains_separator(old_name)==1)) add_path_flag=0;
765 if (add_path_flag==1)
766 { if (command_line_flag == 0)
767 { /* File is opened as a result of an Include directive */
769 if (same_directory_flag==1)
770 prefix_path = current_source_path;
772 if (Include_Path[0]!=0) prefix_path = Include_Path;
774 /* Main file being opened from the command line */
776 else if (Source_Path[0]!=0) prefix_path = Source_Path;
779 #ifdef FILE_EXTENSIONS
780 /* Which file extension is expected? */
782 if ((command_line_flag==1)||(same_directory_flag==1))
783 extension = Source_Extension;
785 extension = Include_Extension;
787 extension = check_extension(old_name, extension);
792 last_value = write_translated_name(new_name, old_name,
793 prefix_path, last_value, extension);
795 /* Set the "current source path" (for use of Include ">...") */
797 if (command_line_flag==1)
798 { strcpy(current_source_path, new_name);
799 for (i=strlen(current_source_path)-1;
800 ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
802 if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir */
803 else current_source_path[0] = 0; /* Current file at root dir */
809 extern int translate_link_filename(int last_value,
810 char *new_name, char *old_name)
811 { char *prefix_path = NULL;
814 if (contains_separator(old_name)==0)
815 if (Module_Path[0]!=0)
816 prefix_path = Module_Path;
818 #ifdef FILE_EXTENSIONS
819 extension = check_extension(old_name, Module_Extension);
824 return write_translated_name(new_name, old_name,
825 prefix_path, last_value, extension);
828 static int translate_icl_filename(int last_value,
829 char *new_name, char *old_name)
830 { char *prefix_path = NULL;
831 char *extension = "";
833 if (contains_separator(old_name)==0)
835 prefix_path = ICL_Path;
837 #ifdef FILE_EXTENSIONS
838 extension = check_extension(old_name, ICL_Extension);
841 return write_translated_name(new_name, old_name,
842 prefix_path, last_value, extension);
845 extern void translate_out_filename(char *new_name, char *old_name)
847 char *extension = "";
850 /* If !convert_filename_flag, then the old_name is just the <file2>
851 parameter on the Inform command line, which we leave alone. */
853 if (!convert_filename_flag)
854 { strcpy(new_name, old_name); return;
857 /* Remove any pathname or extension in <file1>. */
859 if (contains_separator(old_name)==1)
860 { for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
861 if (old_name[i]==FN_SEP) i++;
864 #ifdef FILE_EXTENSIONS
865 for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
866 if (old_name[i] == '.') old_name[i] = 0;
871 { extension = Module_Extension;
872 if (Module_Path[0]!=0) prefix_path = Module_Path;
877 switch(version_number)
878 { case 3: extension = Code_Extension; break;
879 case 4: extension = V4Code_Extension; break;
880 case 5: extension = V5Code_Extension; break;
881 case 6: extension = V6Code_Extension; break;
882 case 7: extension = V7Code_Extension; break;
883 case 8: extension = V8Code_Extension; break;
887 extension = GlulxCode_Extension;
889 if (Code_Path[0]!=0) prefix_path = Code_Path;
892 #ifdef FILE_EXTENSIONS
893 extension = check_extension(old_name, extension);
896 write_translated_name(new_name, old_name, prefix_path, 0, extension);
899 static char *name_or_unset(char *p)
900 { if (p[0]==0) return "(unset)";
904 static void help_on_filenames(void)
905 { char old_name[PATHLEN];
906 char new_name[PATHLEN];
907 int save_mm = module_switch, x;
909 module_switch = FALSE;
911 printf("Help information on filenames:\n\n");
914 "The command line can take one of two forms:\n\n\
915 inform [commands...] <file1>\n\
916 inform [commands...] <file1> <file2>\n\n\
917 Inform translates <file1> into a source file name (see below) for its input.\n\
918 <file2> is usually omitted: if so, the output filename is made from <file1>\n\
919 by cutting out the name part and translating that (see below).\n\
920 If <file2> is given, however, the output filename is set to just <file2>\n\
921 (not altered in any way).\n\n");
924 "Filenames given in the game source (with commands like Include \"name\" and\n\
925 Link \"name\") are also translated by the rules below.\n\n");
928 "Rules of translation:\n\n\
929 Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
930 (such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
931 1. If the name contains a '%c' character (so it's already a pathname), it\n\
932 isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
935 " [Exception: when the name is given in an Include command using the >\n\
936 form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
937 of the file doing the inclusion");
938 #ifdef FILE_EXTENSIONS
939 printf(" and a suitable file extension is added");
944 " Filenames must never contain double-quotation marks \". To use filenames\n\
945 which contain spaces, write them in double-quotes: for instance,\n\n\
946 \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
949 "2. The file is looked for at a particular \"path\" (the filename of a\n\
950 directory), depending on what kind of file it is.\n\n\
951 File type Name Current setting\n\n\
952 Source code (in) source_path %s\n\
953 Include file (in) include_path %s\n\
954 Story file (out) code_path %s\n",
955 name_or_unset(Source_Path), name_or_unset(Include_Path),
956 name_or_unset(Code_Path));
959 " Temporary file (out) temporary_path %s\n\
960 ICL command file (in) icl_path %s\n\
961 Module (in & out) module_path %s\n\n",
962 name_or_unset(Temporary_Path),
963 name_or_unset(ICL_Path), name_or_unset(Module_Path));
966 " If the path is unset, then the current working directory is used (so\n\
967 the filename doesn't change): if, for instance, include_path is set to\n\
968 \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
969 \"backup%coldlib%cparser\".\n\n\
970 The paths can be set or unset on the Inform command line by, eg,\n\
971 \"inform +code_path=finished jigsaw\" or\n\
972 \"inform +include_path= balances\" (which unsets include_path).\n\n",
973 FN_SEP, FN_SEP, FN_SEP);
976 " The four input path variables can be set to lists of alternative paths\n\
977 separated by '%c' characters: these alternatives are always tried in\n\
978 the order they are specified in, that is, left to right through the text\n\
979 in the path variable.\n\n",
982 " If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
983 the path or paths are added to the existing list.\n\n");
985 " (Modules are written to the first alternative in the module_path list;\n\
986 it is an error to give alternatives at all for purely output paths.)\n\n");
988 #ifdef FILE_EXTENSIONS
989 printf("3. The following file extensions are added:\n\n\
992 Story files: %s (Version 3), %s (v4), %s (v5, the default),\n\
993 %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
994 Temporary files: .tmp\n\
996 Source_Extension, Include_Extension,
997 Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
998 V7Code_Extension, V8Code_Extension, GlulxCode_Extension,
1001 except that any extension you give (on the command line or in a filename\n\
1002 used in a program) will override these. If you give the null extension\n\
1003 \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
1006 printf("Names of four individual files can also be set using the same\n\
1007 + command notation (though they aren't really pathnames). These are:\n\n\
1008 transcript_name (text written by -r switch): now \"%s\"\n\
1009 debugging_name (data written by -k switch): now \"%s\"\n\
1010 language_name (library file defining natural language of game):\n\
1012 charset_map (file for character set mapping): now \"%s\"\n\n",
1013 Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
1015 translate_in_filename(0, new_name, "rezrov", 0, 1);
1016 printf("Examples: 1. \"inform rezrov\"\n\
1017 the source code is read from \"%s\"\n",
1019 convert_filename_flag = TRUE;
1020 translate_out_filename(new_name, "rezrov");
1021 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1023 translate_in_filename(0, new_name, "frotz", 0, 1);
1024 printf("2. \"inform -M frotz\"\n\
1025 the source code is read from \"%s\"\n",
1027 module_switch = TRUE;
1028 convert_filename_flag = TRUE;
1029 translate_out_filename(new_name, "frotz");
1030 printf(" and a module is compiled to \"%s\".\n\n", new_name);
1032 module_switch = FALSE;
1034 sprintf(old_name, "demos%cplugh", FN_SEP);
1035 printf("3. \"inform %s\"\n", old_name);
1036 translate_in_filename(0, new_name, old_name, 0, 1);
1037 printf(" the source code is read from \"%s\"\n", new_name);
1038 sprintf(old_name, "demos%cplugh", FN_SEP);
1039 convert_filename_flag = TRUE;
1040 translate_out_filename(new_name, old_name);
1041 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1043 printf("4. \"inform plover my_demo\"\n");
1044 translate_in_filename(0, new_name, "plover", 0, 1);
1045 printf(" the source code is read from \"%s\"\n", new_name);
1046 convert_filename_flag = FALSE;
1047 translate_out_filename(new_name, "my_demo");
1048 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1050 strcpy(old_name, Source_Path);
1051 sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
1052 FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
1053 printf("5. \"inform +source_path=%s zooge\"\n", new_name);
1055 " Note that four alternative paths are given, the first being the empty\n\
1056 path-name (meaning: where you are now). Inform looks for the source code\n\
1057 by trying these four places in turn, stopping when it finds anything:\n\n");
1059 set_path_value(Source_Path, new_name);
1062 { x = translate_in_filename(x, new_name, "zooge", 0, 1);
1063 printf(" \"%s\"\n", new_name);
1065 strcpy(Source_Path, old_name);
1066 module_switch = save_mm;
1069 /* ------------------------------------------------------------------------- */
1070 /* Naming temporary files */
1071 /* (Arguably temporary files should be made using "tmpfile" in */
1072 /* the ANSI C library, but many supposed ANSI libraries lack it.) */
1073 /* ------------------------------------------------------------------------- */
1075 extern void translate_temp_filename(int i)
1078 { case 1: p=Temp1_Name; break;
1079 case 2: p=Temp2_Name; break;
1080 case 3: p=Temp3_Name; break;
1082 if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
1083 printf ("Temporary_Path is too long.\n");
1086 sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
1087 #ifdef INCLUDE_TASK_ID
1088 sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
1090 #ifdef FILE_EXTENSIONS
1091 sprintf(p+strlen(p), ".tmp");
1096 static char riscos_ft_buffer[4];
1098 extern char *riscos_file_type(void)
1100 if (riscos_file_type_format == 1)
1101 { if (module_switch) return("data");
1105 if (module_switch) return("075");
1107 sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
1108 return(riscos_ft_buffer);
1112 /* ------------------------------------------------------------------------- */
1113 /* The compilation pass */
1114 /* ------------------------------------------------------------------------- */
1116 static void run_pass(void)
1118 lexer_begin_prepass();
1119 files_begin_prepass();
1120 load_sourcefile(Source_Name, 0);
1124 parse_program(NULL);
1127 issue_unused_warnings();
1131 if (module_switch) linker_endpass();
1134 if (hash_switch && hash_printed_since_newline) printf("\n");
1136 if (temporary_files_switch)
1137 { if (module_switch) flush_link_data();
1141 if (track_unused_routines)
1142 locate_dead_functions();
1143 construct_storyfile();
1146 int output_has_occurred;
1148 static void rennab(int32 time_taken)
1149 { /* rennab = reverse of banner */
1151 int t = no_warnings + no_suppressed_warnings;
1153 if (memout_switch) print_memory_usage();
1155 if ((no_errors + t)!=0)
1156 { printf("Compiled with ");
1158 { printf("%d error%s", no_errors,(no_errors==1)?"":"s");
1159 if (t > 0) printf(" and ");
1161 if (no_warnings > 0)
1162 printf("%d warning%s", t, (t==1)?"":"s");
1163 if (no_suppressed_warnings > 0)
1164 { if (no_warnings > 0)
1165 printf(" (%d suppressed)", no_suppressed_warnings);
1167 printf("%d suppressed warning%s", no_suppressed_warnings,
1168 (no_suppressed_warnings==1)?"":"s");
1170 if (output_has_occurred == FALSE) printf(" (no output)");
1174 if (no_compiler_errors > 0) print_sorry_message();
1176 if (statistics_switch)
1177 printf("Completed in %ld seconds\n", (long int) time_taken);
1180 /* ------------------------------------------------------------------------- */
1181 /* The compiler abstracted to a routine. */
1182 /* ------------------------------------------------------------------------- */
1184 static int execute_icl_header(char *file1);
1186 static int compile(int number_of_files_specified, char *file1, char *file2)
1189 if (execute_icl_header(file1))
1192 select_target(glulx_mode);
1194 if (define_INFIX_switch && glulx_mode) {
1195 printf("Infix (-X) facilities are not available in Glulx: \
1196 disabling -X switch\n");
1197 define_INFIX_switch = FALSE;
1200 if (module_switch && glulx_mode) {
1201 printf("Modules are not available in Glulx: \
1202 disabling -M switch\n");
1203 module_switch = FALSE;
1206 if (define_INFIX_switch && module_switch)
1207 { printf("Infix (-X) facilities are not available when compiling \
1208 modules: disabling -X switch\n");
1209 define_INFIX_switch = FALSE;
1211 if (runtime_error_checking_switch && module_switch)
1212 { printf("Strict checking (-S) facilities are not available when \
1213 compiling modules: disabling -S switch\n");
1214 runtime_error_checking_switch = FALSE;
1217 time_start=time(0); no_compilations++;
1219 strcpy(Source_Name, file1); convert_filename_flag = TRUE;
1220 strcpy(Code_Name, file1);
1221 if (number_of_files_specified == 2)
1222 { strcpy(Code_Name, file2); convert_filename_flag = FALSE;
1227 if (debugfile_switch) begin_debug_file();
1231 if (transcript_switch) open_transcript_file(Source_Name);
1235 if (transcript_switch)
1236 { write_dictionary_to_transcript();
1237 close_transcript_file();
1240 if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
1241 else { output_has_occurred = FALSE; }
1243 if (debugfile_switch)
1247 if (temporary_files_switch && (no_errors>0)) remove_temp_files();
1251 rennab((int32) (time(0)-time_start));
1253 if (optimise_switch) optimise_abbreviations();
1255 if (store_the_text) my_free(&all_text,"transcription text");
1257 return (no_errors==0)?0:1;
1260 /* ------------------------------------------------------------------------- */
1261 /* The command line interpreter */
1262 /* ------------------------------------------------------------------------- */
1264 static void cli_print_help(int help_level)
1267 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
1268 story files, as well as \"Glulx\" story files:\n\
1269 Copyright (c) Graham Nelson 1993 - 2021.\n\n");
1271 /* For people typing just "inform", a summary only: */
1276 #ifndef PROMPT_INPUT
1277 printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
1279 printf("When run, Inform prompts you for commands (and switches),\n\
1280 which are optional, then an input <file1> and an (optional) output\n\
1285 "<file1> is the Inform source file of the game to be compiled. <file2>,\n\
1286 if given, overrides the filename Inform would normally use for the\n\
1287 compiled output. Try \"inform -h1\" for file-naming conventions.\n\n\
1288 One or more words can be supplied as \"commands\". These may be:\n\n\
1289 -switches a list of compiler switches, 1 or 2 letter\n\
1290 (see \"inform -h2\" for the full range)\n\n\
1291 +dir set Include_Path to this directory\n\
1292 ++dir add this directory to Include_Path\n\
1293 +PATH=dir change the PATH to this directory\n\
1294 ++PATH=dir add this directory to the PATH\n\n\
1295 $... one of the following memory commands:\n");
1298 " $list list current memory allocation settings\n\
1299 $huge make standard \"huge game\" settings %s\n\
1300 $large make standard \"large game\" settings %s\n\
1301 $small make standard \"small game\" settings %s\n\
1302 $?SETTING explain briefly what SETTING is for\n\
1303 $SETTING=number change SETTING to given number\n\n",
1304 (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
1305 (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
1306 (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
1309 " (filename) read in a list of commands (in the format above)\n\
1310 from this \"setup file\"\n\n");
1312 printf("Alternate command-line formats for the above:\n\
1313 --help (this page)\n\
1315 --addpath PATH=dir\n\
1317 --size huge, --size large, --size small\n\
1318 --helpopt SETTING\n\
1319 --opt SETTING=number\n\
1320 --config filename (setup file)\n\n");
1322 #ifndef PROMPT_INPUT
1323 printf("For example: \"inform -dexs $huge curses\".\n");
1329 /* The -h1 (filenaming) help information: */
1331 if (help_level == 1) { help_on_filenames(); return; }
1333 /* The -h2 (switches) help information: */
1335 printf("Help on the full list of legal switch commands:\n\n\
1336 a trace assembly-language (without hex dumps; see -t)\n\
1337 c more concise error messages\n\
1338 d contract double spaces after full stops in text\n\
1339 d2 contract double spaces after exclamation and question marks, too\n\
1340 e economy mode (slower): make use of declared abbreviations\n");
1343 f frequencies mode: show how useful abbreviations are\n\
1344 g traces calls to functions (except in the library)\n\
1345 g2 traces calls to all functions\n\
1346 h print this information\n");
1349 i ignore default switches set within the file\n\
1350 j list objects as constructed\n\
1351 k output Infix debugging information to \"%s\" (and switch -D on)\n\
1352 l list every statement run through Inform\n\
1353 m say how much memory has been allocated\n\
1354 n print numbers of properties, attributes and actions\n",
1357 o print offset addresses\n\
1358 p give percentage breakdown of story file\n\
1359 q keep quiet about obsolete usages\n\
1360 r record all the text to \"%s\"\n\
1361 s give statistics\n\
1362 t trace assembly-language (with full hex dumps; see -a)\n",
1366 u work out most useful abbreviations (very very slowly)\n\
1367 v3 compile to version-3 (\"Standard\") story file\n\
1368 v4 compile to version-4 (\"Plus\") story file\n\
1369 v5 compile to version-5 (\"Advanced\") story file: the default\n\
1370 v6 compile to version-6 (graphical) story file\n\
1371 v8 compile to version-8 (expanded \"Advanced\") story file\n\
1372 w disable warning messages\n\
1373 x print # for every 100 lines compiled\n\
1374 y trace linking system\n\
1375 z print memory map of the virtual machine\n\n");
1378 B use big memory model (for large V6/V7 files)\n\
1379 C0 text character set is plain ASCII only\n\
1380 Cu text character set is UTF-8\n\
1381 Cn text character set is ISO 8859-n (n = 1 to 9)\n\
1382 (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
1383 7, Greek; 8, Hebrew; 9, Latin5. Default is -C1.)\n");
1384 printf(" D insert \"Constant DEBUG;\" automatically\n");
1385 printf(" E0 Archimedes-style error messages%s\n",
1386 (error_format==0)?" (current setting)":"");
1387 printf(" E1 Microsoft-style error messages%s\n",
1388 (error_format==1)?" (current setting)":"");
1389 printf(" E2 Macintosh MPW-style error messages%s\n",
1390 (error_format==2)?" (current setting)":"");
1391 #ifdef USE_TEMPORARY_FILES
1392 printf(" F0 use extra memory rather than temporary files\n");
1394 printf(" F1 use temporary files to reduce memory consumption\n");
1396 printf(" G compile a Glulx game file\n");
1397 printf(" H use Huffman encoding to compress Glulx strings\n");
1398 printf(" M compile as a Module for future linking\n");
1402 R0 use filetype 060 + version number for games (default)\n\
1403 R1 use official Acorn filetype 11A for all games\n");
1405 printf(" S compile strict error-checking at run-time (on by default)\n");
1406 #ifdef ARC_THROWBACK
1407 printf(" T enable throwback of errors in the DDE\n");
1409 printf(" U insert \"Constant USE_MODULES;\" automatically\n");
1410 printf(" V print the version and date of this program\n");
1411 printf(" Wn header extension table is at least n words (n = 3 to 99)\n");
1412 printf(" X compile with INFIX debugging facilities present\n");
1416 extern void switches(char *p, int cmode)
1417 { int i, s=1, state;
1418 /* Here cmode is 0 if switches list is from a "Switches" directive
1419 and 1 if from a "-switches" command-line or ICL list */
1424 "Ignoring second word which should be a -list of switches.\n");
1428 for (i=cmode; p[i]!=0; i+=s, s=1)
1436 case 'a': asm_trace_setting = 1; break;
1437 case 'b': bothpasses_switch = state; break;
1438 case 'c': concise_switch = state; break;
1439 case 'd': switch(p[i+1])
1440 { case '1': double_space_setting=1; s=2; break;
1441 case '2': double_space_setting=2; s=2; break;
1442 default: double_space_setting=1; break;
1445 case 'e': economy_switch = state; break;
1446 case 'f': frequencies_switch = state; break;
1447 case 'g': switch(p[i+1])
1448 { case '1': trace_fns_setting=1; s=2; break;
1449 case '2': trace_fns_setting=2; s=2; break;
1450 default: trace_fns_setting=1; break;
1453 case 'h': switch(p[i+1])
1454 { case '1': cli_print_help(1); s=2; break;
1455 case '2': cli_print_help(2); s=2; break;
1457 default: cli_print_help(0); break;
1460 case 'i': ignore_switches_switch = state; break;
1461 case 'j': listobjects_switch = state; break;
1462 case 'k': if (cmode == 0)
1463 error("The switch '-k' can't be set with 'Switches'");
1465 { debugfile_switch = state;
1466 if (state) define_DEBUG_switch = TRUE;
1469 case 'l': listing_switch = state; break;
1470 case 'm': memout_switch = state; break;
1471 case 'n': printprops_switch = state; break;
1472 case 'o': offsets_switch = state; break;
1473 case 'p': percentages_switch = state; break;
1474 case 'q': obsolete_switch = state; break;
1475 case 'r': if (cmode == 0)
1476 error("The switch '-r' can't be set with 'Switches'");
1478 transcript_switch = state; break;
1479 case 's': statistics_switch = state; break;
1480 case 't': asm_trace_setting=2; break;
1481 case 'u': if (cmode == 0) {
1482 error("The switch '-u' can't be set with 'Switches'");
1485 optimise_switch = state; break;
1486 case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
1487 if ((cmode==0) && (version_set_switch)) { s=2; break; }
1488 version_set_switch = TRUE; s=2;
1490 { case '3': select_version(3); break;
1491 case '4': select_version(4); break;
1492 case '5': select_version(5); break;
1493 case '6': select_version(6); break;
1494 case '7': select_version(7); break;
1495 case '8': select_version(8); break;
1496 default: printf("-v must be followed by 3 to 8\n");
1497 version_set_switch=0; s=1;
1500 if ((version_number < 5) && (r_e_c_s_set == FALSE))
1501 runtime_error_checking_switch = FALSE;
1503 case 'w': nowarnings_switch = state; break;
1504 case 'x': hash_switch = state; break;
1505 case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
1506 case 'z': memory_map_switch = state; break;
1507 case 'B': oddeven_packing_switch = state; break;
1509 if (p[i+1] == 'u') {
1510 character_set_unicode = TRUE;
1511 /* Leave the set_setting on Latin-1, because that
1512 matches the first block of Unicode. */
1513 character_set_setting = 1;
1516 { character_set_setting=p[i+1]-'0';
1517 if ((character_set_setting < 0)
1518 || (character_set_setting > 9))
1519 { printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
1520 character_set_unicode = FALSE;
1521 character_set_setting = 1;
1524 if (cmode == 0) change_character_set();
1526 case 'D': define_DEBUG_switch = state; break;
1527 case 'E': switch(p[i+1])
1528 { case '0': s=2; error_format=0; break;
1529 case '1': s=2; error_format=1; break;
1530 case '2': s=2; error_format=2; break;
1531 default: error_format=1; break;
1534 case 'F': if (cmode == 0) {
1535 error("The switch '-F' can't be set with 'Switches'");
1539 { case '0': s=2; temporary_files_switch = FALSE; break;
1540 case '1': s=2; temporary_files_switch = TRUE; break;
1541 default: temporary_files_switch = state; break;
1544 case 'M': module_switch = state;
1545 if (state && (r_e_c_s_set == FALSE))
1546 runtime_error_checking_switch = FALSE;
1549 case 'R': switch(p[i+1])
1550 { case '0': s=2; riscos_file_type_format=0; break;
1551 case '1': s=2; riscos_file_type_format=1; break;
1552 default: riscos_file_type_format=1; break;
1556 #ifdef ARC_THROWBACK
1557 case 'T': throwback_switch = state; break;
1559 case 'S': runtime_error_checking_switch = state;
1560 r_e_c_s_set = TRUE; break;
1561 case 'G': if (cmode == 0)
1562 error("The switch '-G' can't be set with 'Switches'");
1563 else if (version_set_switch)
1564 error("The '-G' switch cannot follow the '-v' switch");
1566 { glulx_mode = state;
1567 adjust_memory_sizes();
1570 case 'H': compression_switch = state; break;
1571 case 'U': define_USE_MODULES_switch = state; break;
1572 case 'V': exit(0); break;
1573 case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
1574 { s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
1575 if ((p[i+2]>='0') && (p[i+2]<='9'))
1576 { s=3; ZCODE_HEADER_EXT_WORDS *= 10;
1577 ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
1581 case 'X': define_INFIX_switch = state; break;
1583 printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
1589 if (optimise_switch && (!store_the_text))
1590 { store_the_text=TRUE;
1593 printf("Allocation %ld bytes for transcription text\n",
1594 (long) MAX_TRANSCRIPT_SIZE);
1595 all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
1596 malloced_bytes += MAX_TRANSCRIPT_SIZE;
1598 fatalerror("Can't hallocate memory for transcription text. Darn.");
1600 all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
1605 static int icl_command(char *p)
1606 { if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
1607 || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
1611 static void icl_error(char *filename, int line)
1612 { printf("Error in ICL file '%s', line %d:\n", filename, line);
1615 static void icl_header_error(char *filename, int line)
1616 { printf("Error in ICL header of file '%s', line %d:\n", filename, line);
1619 static int copy_icl_word(char *from, char *to, int max)
1621 /* Copies one token from 'from' to 'to', null-terminated:
1622 returns the number of chars in 'from' read past (possibly 0). */
1624 int i, j, quoted_mode, truncated;
1626 i = 0; truncated = 0;
1627 while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
1628 || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
1631 { while (from[i] != 0) i++;
1632 to[0] = 0; return i;
1635 for (quoted_mode = FALSE, j=0;;)
1636 { if (from[i] == 0) break;
1637 if (from[i] == 10) break;
1638 if (from[i] == 13) break;
1639 if (from[i] == TAB_CHARACTER) break;
1640 if ((from[i] == ' ') && (!quoted_mode)) break;
1641 if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
1642 else to[j++] = from[i++];
1650 printf("The following parameter has been truncated:\n%s\n", to);
1654 /* Copy a string, converting to uppercase. The to array should be
1655 (at least) max characters. Result will be null-terminated, so
1656 at most max-1 characters will be copied.
1658 static int strcpyupper(char *to, char *from, int max)
1661 for (ix=0; ix<max-1; ix++) {
1663 if (islower(ch)) ch = toupper(ch);
1670 static void execute_icl_command(char *p);
1671 static int execute_dashdash_command(char *p, char *p2);
1673 static int execute_icl_header(char *argname)
1676 char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1680 char filename[PATHLEN];
1684 { x = translate_in_filename(x, filename, argname, 0, 1);
1685 command_file = fopen(filename,"r");
1686 } while ((command_file == NULL) && (x != 0));
1687 if (!command_file) {
1688 /* Fail silently. The regular compiler will try to open the file
1689 again, and report the problem. */
1693 while (feof(command_file)==0) {
1694 if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1696 if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
1698 i = copy_icl_word(cli_buff+2, fw, CMD_BUF_SIZE);
1699 if (icl_command(fw)) {
1700 execute_icl_command(fw);
1701 copy_icl_word(cli_buff+2 + i, fw, CMD_BUF_SIZE);
1702 if ((fw[0] != 0) && (fw[0] != '!')) {
1703 icl_header_error(filename, line);
1705 printf("expected comment or nothing but found '%s'\n", fw);
1710 icl_header_error(filename, line);
1712 printf("Expected command or comment but found '%s'\n", fw);
1716 fclose(command_file);
1718 return (errcount==0)?0:1;
1722 static void run_icl_file(char *filename, FILE *command_file)
1723 { char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1725 printf("[Running ICL file '%s']\n", filename);
1727 while (feof(command_file)==0)
1728 { if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1730 i = copy_icl_word(cli_buff, fw, CMD_BUF_SIZE);
1731 if (icl_command(fw))
1732 { execute_icl_command(fw);
1733 copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1734 if ((fw[0] != 0) && (fw[0] != '!'))
1735 { icl_error(filename, line);
1736 printf("expected comment or nothing but found '%s'\n", fw);
1740 { if (strcmp(fw, "compile")==0)
1741 { char story_name[PATHLEN], code_name[PATHLEN];
1742 i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
1743 i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
1745 if (code_name[0] != 0) x=2;
1746 else if (story_name[0] != 0) x=1;
1750 { case 0: icl_error(filename, line);
1751 printf("No filename given to 'compile'\n");
1753 case 1: printf("[Compiling <%s>]\n", story_name);
1754 compile(x, story_name, code_name);
1756 case 2: printf("[Compiling <%s> to <%s>]\n",
1757 story_name, code_name);
1758 compile(x, story_name, code_name);
1759 copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1761 { icl_error(filename, line);
1762 printf("Expected comment or nothing but found '%s'\n",
1770 { icl_error(filename, line);
1771 printf("Expected command or comment but found '%s'\n", fw);
1777 /* This should only be called if the argument has been verified to be
1778 an ICL command, e.g. by checking icl_command().
1780 static void execute_icl_command(char *p)
1781 { char filename[PATHLEN], cli_buff[CMD_BUF_SIZE];
1786 { case '+': set_path_command(p+1); break;
1787 case '-': switches(p,1); break;
1788 case '$': memory_command(p+1); break;
1789 case '(': len = strlen(p);
1790 if (p[len-1] != ')') {
1791 printf("Error in ICL: (command) missing closing paren\n");
1794 len -= 2; /* omit parens */
1795 if (len > CMD_BUF_SIZE-1) len = CMD_BUF_SIZE-1;
1796 strncpy(cli_buff, p+1, len);
1800 { x = translate_icl_filename(x, filename, cli_buff);
1801 command_file = fopen(filename,"r");
1802 } while ((command_file == NULL) && (x != 0));
1804 if (command_file == NULL) {
1805 printf("Error in ICL: Couldn't open command file '%s'\n",
1809 run_icl_file(filename, command_file);
1810 fclose(command_file);
1815 /* Convert a --command into the equivalent ICL command and call
1816 execute_icl_command(). The dashes have already been stripped.
1818 The second argument is the following command-line argument
1819 (or NULL if there was none). This may or may not be consumed.
1820 Returns TRUE if it was.
1822 static int execute_dashdash_command(char *p, char *p2)
1824 char cli_buff[CMD_BUF_SIZE];
1825 int consumed2 = FALSE;
1827 if (!strcmp(p, "help")) {
1828 strcpy(cli_buff, "-h");
1830 else if (!strcmp(p, "list")) {
1831 strcpy(cli_buff, "$LIST");
1833 else if (!strcmp(p, "size")) {
1835 if (!(p2 && (!strcmpcis(p2, "HUGE") || !strcmpcis(p2, "LARGE") || !strcmpcis(p2, "SMALL")))) {
1836 printf("--size must be followed by \"huge\", \"large\", or \"small\"\n");
1839 strcpy(cli_buff, "$");
1840 strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1842 else if (!strcmp(p, "opt")) {
1844 if (!p2 || !strchr(p2, '=')) {
1845 printf("--opt must be followed by \"setting=number\"\n");
1848 strcpy(cli_buff, "$");
1849 strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1851 else if (!strcmp(p, "helpopt")) {
1854 printf("--helpopt must be followed by \"setting\"\n");
1857 strcpy(cli_buff, "$?");
1858 strcpyupper(cli_buff+2, p2, CMD_BUF_SIZE-2);
1860 else if (!strcmp(p, "path")) {
1862 if (!p2 || !strchr(p2, '=')) {
1863 printf("--path must be followed by \"name=path\"\n");
1866 snprintf(cli_buff, CMD_BUF_SIZE, "+%s", p2);
1868 else if (!strcmp(p, "addpath")) {
1870 if (!p2 || !strchr(p2, '=')) {
1871 printf("--addpath must be followed by \"name=path\"\n");
1874 snprintf(cli_buff, CMD_BUF_SIZE, "++%s", p2);
1876 else if (!strcmp(p, "config")) {
1879 printf("--config must be followed by \"file.icl\"\n");
1882 snprintf(cli_buff, CMD_BUF_SIZE, "(%s)", p2);
1885 printf("Option \"--%s\" unknown (try \"inform -h\")\n", p);
1889 execute_icl_command(cli_buff);
1893 /* ------------------------------------------------------------------------- */
1894 /* Opening and closing banners */
1895 /* ------------------------------------------------------------------------- */
1897 char banner_line[CMD_BUF_SIZE];
1899 /* We store the banner text for use elsewhere (see files.c).
1901 static void banner(void)
1904 snprintf(banner_line, CMD_BUF_SIZE, "Inform %d.%d%d",
1905 (VNUMBER/100)%10, (VNUMBER/10)%10, VNUMBER%10);
1906 #ifdef RELEASE_SUFFIX
1907 len = strlen(banner_line);
1908 snprintf(banner_line+len, CMD_BUF_SIZE-len, "%s", RELEASE_SUFFIX);
1910 #ifdef MACHINE_STRING
1911 len = strlen(banner_line);
1912 snprintf(banner_line+len, CMD_BUF_SIZE-len, " for %s", MACHINE_STRING);
1914 len = strlen(banner_line);
1915 snprintf(banner_line+len, CMD_BUF_SIZE-len, " (%s)", RELEASE_DATE);
1917 printf("%s\n", banner_line);
1920 /* ------------------------------------------------------------------------- */
1921 /* Input from the outside world */
1922 /* ------------------------------------------------------------------------- */
1925 static void read_command_line(int argc, char **argv)
1927 char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
1929 printf("Source filename?\n> ");
1930 while (gets(buffer1)==NULL); cli_file1=buffer1;
1931 printf("Output filename (RETURN for the same)?\n> ");
1932 while (gets(buffer2)==NULL); cli_file2=buffer2;
1933 cli_files_specified=1;
1934 if (buffer2[0]!=0) cli_files_specified=2;
1936 { printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
1937 while (gets(buffer3)==NULL); execute_icl_command(buffer3);
1938 } while (buffer3[0]!=0);
1941 static void read_command_line(int argc, char **argv)
1943 if (argc==1) switches("-h",1);
1945 for (i=1, cli_files_specified=0; i<argc; i++)
1946 if (argv[i][0] == '-' && argv[i][1] == '-') {
1947 char *nextarg = NULL;
1949 if (i+1 < argc) nextarg = argv[i+1];
1950 consumed2 = execute_dashdash_command(argv[i]+2, nextarg);
1951 if (consumed2 && i+1 < argc) {
1955 else if (icl_command(argv[i])) {
1956 execute_icl_command(argv[i]);
1959 switch(++cli_files_specified)
1960 { case 1: cli_file1 = argv[i]; break;
1961 case 2: cli_file2 = argv[i]; break;
1963 printf("Command line error: unknown parameter '%s'\n",
1970 /* ------------------------------------------------------------------------- */
1971 /* M A I N : An outer shell for machine-specific quirks */
1972 /* Omitted altogether if EXTERNAL_SHELL is defined, as for instance is */
1973 /* needed for the Macintosh front end. */
1974 /* ------------------------------------------------------------------------- */
1976 #ifdef EXTERNAL_SHELL
1977 extern int sub_main(int argc, char **argv);
1980 static int sub_main(int argc, char **argv);
1982 int main(int argc, char **argv, char *envp[])
1984 int main(int argc, char **argv)
1988 InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
1990 rcode = sub_main(argc, argv);
1991 #ifdef ARC_THROWBACK
1999 /* ------------------------------------------------------------------------- */
2000 /* M A I N II: Starting up ICL with the command line */
2001 /* ------------------------------------------------------------------------- */
2003 #ifdef EXTERNAL_SHELL
2004 extern int sub_main(int argc, char **argv)
2006 static int sub_main(int argc, char **argv)
2011 ProcessEvents (&g_proc);
2014 if (store_the_text) my_free(&all_text,"transcription text");
2015 longjmp (g_fallback, 1);
2021 set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
2022 reset_switch_settings(); select_version(5);
2024 cli_files_specified = 0; no_compilations = 0;
2025 cli_file1 = "source"; cli_file2 = "output";
2027 read_command_line(argc, argv);
2029 if (cli_files_specified > 0)
2030 { return_code = compile(cli_files_specified, cli_file1, cli_file2);
2032 if (return_code != 0) return(return_code);
2035 if (no_compilations == 0)
2036 printf("\n[No compilation requested]\n");
2037 if (no_compilations > 1)
2038 printf("[%d compilations completed]\n", no_compilations);
2043 /* ========================================================================= */