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 - 2020 */
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);
224 /* ------------------------------------------------------------------------- */
225 /* Tracery: output control variables */
226 /* ------------------------------------------------------------------------- */
228 int asm_trace_level, /* trace assembly: 0 for off, 1 for assembly
229 only, 2 for full assembly tracing with hex dumps */
230 line_trace_level, /* line tracing: 0 off, 1 on */
231 expr_trace_level, /* expression tracing: 0 off, 1 full, 2 brief */
232 linker_trace_level, /* set by -y: 0 to 4 levels of tracing */
233 tokens_trace_level; /* lexer output tracing: 0 off, 1 on */
235 /* ------------------------------------------------------------------------- */
236 /* On/off switch variables (by default all FALSE); other switch settings */
237 /* ------------------------------------------------------------------------- */
239 int bothpasses_switch, /* -b */
240 concise_switch, /* -c */
241 economy_switch, /* -e */
242 frequencies_switch, /* -f */
243 ignore_switches_switch, /* -i */
244 listobjects_switch, /* -j */
245 debugfile_switch, /* -k */
246 listing_switch, /* -l */
247 memout_switch, /* -m */
248 printprops_switch, /* -n */
249 offsets_switch, /* -o */
250 percentages_switch, /* -p */
251 obsolete_switch, /* -q */
252 transcript_switch, /* -r */
253 statistics_switch, /* -s */
254 optimise_switch, /* -u */
255 version_set_switch, /* -v */
256 nowarnings_switch, /* -w */
257 hash_switch, /* -x */
258 memory_map_switch, /* -z */
259 oddeven_packing_switch, /* -B */
260 define_DEBUG_switch, /* -D */
261 temporary_files_switch, /* -F */
262 module_switch, /* -M */
263 runtime_error_checking_switch, /* -S */
264 define_USE_MODULES_switch, /* -U */
265 define_INFIX_switch; /* -X */
267 int throwback_switch; /* -T */
270 int riscos_file_type_format; /* set by -R */
272 int compression_switch; /* set by -H */
273 int character_set_setting, /* set by -C0 through -C9 */
274 character_set_unicode, /* set by -Cu */
275 error_format, /* set by -E */
276 asm_trace_setting, /* set by -a and -t: value of
277 asm_trace_level to use when tracing */
278 double_space_setting, /* set by -d: 0, 1 or 2 */
279 trace_fns_setting, /* set by -g: 0, 1 or 2 */
280 linker_trace_setting, /* set by -y: ditto for linker_... */
281 store_the_text; /* when set, record game text to a chunk
282 of memory (used by both -r & -k) */
283 static int r_e_c_s_set; /* has -S been explicitly set? */
285 int glulx_mode; /* -G */
287 static void reset_switch_settings(void)
288 { asm_trace_setting=0;
289 linker_trace_level=0;
290 tokens_trace_level=0;
292 store_the_text = FALSE;
294 bothpasses_switch = FALSE;
295 concise_switch = FALSE;
296 double_space_setting = 0;
297 economy_switch = FALSE;
298 frequencies_switch = FALSE;
299 trace_fns_setting = 0;
300 ignore_switches_switch = FALSE;
301 listobjects_switch = FALSE;
302 debugfile_switch = FALSE;
303 listing_switch = FALSE;
304 memout_switch = FALSE;
305 printprops_switch = FALSE;
306 offsets_switch = FALSE;
307 percentages_switch = FALSE;
308 obsolete_switch = FALSE;
309 transcript_switch = FALSE;
310 statistics_switch = FALSE;
311 optimise_switch = FALSE;
312 version_set_switch = FALSE;
313 nowarnings_switch = FALSE;
315 memory_map_switch = FALSE;
316 oddeven_packing_switch = FALSE;
317 define_DEBUG_switch = FALSE;
318 #ifdef USE_TEMPORARY_FILES
319 temporary_files_switch = TRUE;
321 temporary_files_switch = FALSE;
323 define_USE_MODULES_switch = FALSE;
324 module_switch = FALSE;
326 throwback_switch = FALSE;
328 runtime_error_checking_switch = TRUE;
330 define_INFIX_switch = FALSE;
332 riscos_file_type_format = 0;
334 error_format=DEFAULT_ERROR_FORMAT;
336 character_set_setting = 1; /* Default is ISO Latin-1 */
337 character_set_unicode = FALSE;
339 compression_switch = TRUE;
341 requested_glulx_version = 0;
344 /* ------------------------------------------------------------------------- */
345 /* Number of files given as command line parameters (0, 1 or 2) */
346 /* ------------------------------------------------------------------------- */
348 static int cli_files_specified,
349 convert_filename_flag;
351 char Source_Name[PATHLEN]; /* Processed name of first input file */
352 char Code_Name[PATHLEN]; /* Processed name of output file */
354 static char *cli_file1, *cli_file2; /* Unprocessed (and unsafe to alter) */
356 /* ========================================================================= */
357 /* Data structure management routines */
358 /* ------------------------------------------------------------------------- */
360 static void init_vars(void)
368 init_expressc_vars();
369 init_expressp_vars();
384 static void begin_pass(void)
390 directs_begin_pass();
392 expressc_begin_pass();
393 expressp_begin_pass();
396 endofpass_flag = FALSE;
397 line_trace_level = 0; expr_trace_level = 0;
398 asm_trace_level = asm_trace_setting;
399 linker_trace_level = linker_trace_setting;
400 if (listing_switch) line_trace_level=1;
405 objects_begin_pass();
407 symbols_begin_pass();
416 /* Compile a Main__ routine (see "veneer.c") */
418 compile_initial_routine();
420 /* Make the four metaclasses: Class must be object number 1, so
421 it must come first */
426 make_class("Object");
427 make_class("Routine");
428 make_class("String");
434 extern void allocate_arrays(void)
436 arrays_allocate_arrays();
437 asm_allocate_arrays();
438 bpatch_allocate_arrays();
439 chars_allocate_arrays();
440 directs_allocate_arrays();
441 errors_allocate_arrays();
442 expressc_allocate_arrays();
443 expressp_allocate_arrays();
444 files_allocate_arrays();
446 lexer_allocate_arrays();
447 linker_allocate_arrays();
448 memory_allocate_arrays();
449 objects_allocate_arrays();
450 states_allocate_arrays();
451 symbols_allocate_arrays();
452 syntax_allocate_arrays();
453 tables_allocate_arrays();
454 text_allocate_arrays();
455 veneer_allocate_arrays();
456 verbs_allocate_arrays();
459 extern void free_arrays(void)
461 /* One array may survive this routine, all_the_text (used to hold
462 game text until the abbreviations optimiser begins work on it): this
463 array (if it was ever allocated) is freed at the top level. */
465 arrays_free_arrays();
467 bpatch_free_arrays();
469 directs_free_arrays();
470 errors_free_arrays();
471 expressc_free_arrays();
472 expressp_free_arrays();
476 linker_free_arrays();
477 memory_free_arrays();
478 objects_free_arrays();
479 states_free_arrays();
480 symbols_free_arrays();
481 syntax_free_arrays();
482 tables_free_arrays();
484 veneer_free_arrays();
488 /* ------------------------------------------------------------------------- */
489 /* Name translation code for filenames */
490 /* ------------------------------------------------------------------------- */
492 static char Source_Path[PATHLEN];
493 static char Include_Path[PATHLEN];
494 static char Code_Path[PATHLEN];
495 static char Module_Path[PATHLEN];
496 static char Temporary_Path[PATHLEN];
497 static char current_source_path[PATHLEN];
498 char Debugging_Name[PATHLEN];
499 char Transcript_Name[PATHLEN];
500 char Language_Name[PATHLEN];
501 char Charset_Map[PATHLEN];
502 static char ICL_Path[PATHLEN];
504 /* Set one of the above Path buffers to the given location, or list of
505 locations. (A list is comma-separated, and only accepted for Source_Path,
506 Include_Path, ICL_Path, Module_Path.)
508 static void set_path_value(char *path, char *value)
513 if (i >= PATHLEN-1) {
514 printf("A specified path is longer than %d characters.\n",
518 if ((value[j] == FN_ALT) || (value[j] == 0))
519 { if ((value[j] == FN_ALT)
520 && (path != Source_Path) && (path != Include_Path)
521 && (path != ICL_Path) && (path != Module_Path))
522 { printf("The character '%c' is used to divide entries in a list \
523 of possible locations, and can only be used in the Include_Path, Source_Path, \
524 Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
527 if ((path != Debugging_Name) && (path != Transcript_Name)
528 && (path != Language_Name) && (path != Charset_Map)
529 && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
530 path[i++] = value[j++];
531 if (value[j-1] == 0) return;
533 else path[i++] = value[j++];
537 /* Prepend the given location or list of locations to one of the above
538 Path buffers. This is only permitted for Source_Path, Include_Path,
539 ICL_Path, Module_Path.
541 An empty field (in the comma-separated list) means the current
542 directory. If the Path buffer is entirely empty, we assume that
543 we want to search both value and the current directory, so
544 the result will be "value,".
546 static void prepend_path_value(char *path, char *value)
549 int oldlen = strlen(path);
551 char new_path[PATHLEN];
553 if ((path != Source_Path) && (path != Include_Path)
554 && (path != ICL_Path) && (path != Module_Path))
555 { printf("The character '+' is used to add to a list \
556 of possible locations, and can only be used in the Include_Path, Source_Path, \
557 Module_Path or ICL_Path variables. Other paths are for output only.\n");
563 if (i >= PATHLEN-1) {
564 printf("A specified path is longer than %d characters.\n",
568 if ((value[j] == FN_ALT) || (value[j] == 0))
569 { if ((path != Debugging_Name) && (path != Transcript_Name)
570 && (path != Language_Name) && (path != Charset_Map)
571 && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
572 new_path[i++] = value[j++];
573 if (value[j-1] == 0) {
578 else new_path[i++] = value[j++];
581 if (newlen+1+oldlen >= PATHLEN-1) {
582 printf("A specified path is longer than %d characters.\n",
588 new_path[i++] = FN_ALT;
590 new_path[i++] = path[j++];
593 strcpy(path, new_path);
596 static void set_default_paths(void)
598 set_path_value(Source_Path, Source_Directory);
599 set_path_value(Include_Path, Include_Directory);
600 set_path_value(Code_Path, Code_Directory);
601 set_path_value(Module_Path, Module_Directory);
602 set_path_value(ICL_Path, ICL_Directory);
603 set_path_value(Temporary_Path, Temporary_Directory);
604 set_path_value(Debugging_Name, Debugging_File);
605 set_path_value(Transcript_Name, Transcript_File);
606 set_path_value(Language_Name, Default_Language);
607 set_path_value(Charset_Map, "");
610 /* Parse a path option which looks like "dir", "+dir", "pathname=dir",
611 or "+pathname=dir". If there is no "=", we assume "include_path=...".
612 If the option begins with a "+" the directory is prepended to the
613 existing path instead of replacing it.
615 static void set_path_command(char *command)
616 { int i, j; char *path_to_set = NULL;
619 if (command[0] == '+') {
624 for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
626 path_to_set=Include_Path;
628 if (command[i] == '=') {
629 char pathname[PATHLEN];
630 if (i>=PATHLEN) i=PATHLEN-1;
632 char ch = command[j];
633 if (isupper(ch)) ch=tolower(ch);
637 command = command+i+1;
640 if (strcmp(pathname, "source_path")==0) path_to_set=Source_Path;
641 if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
642 if (strcmp(pathname, "code_path")==0) path_to_set=Code_Path;
643 if (strcmp(pathname, "module_path")==0) path_to_set=Module_Path;
644 if (strcmp(pathname, "icl_path")==0) path_to_set=ICL_Path;
645 if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
646 if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
647 if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
648 if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
649 if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
651 if (path_to_set == NULL)
652 { printf("No such path setting as \"%s\"\n", pathname);
658 set_path_value(path_to_set, command);
660 prepend_path_value(path_to_set, command);
663 static int contains_separator(char *name)
665 for (i=0; name[i]!=0; i++)
666 if (name[i] == FN_SEP) return 1;
670 static int write_translated_name(char *new_name, char *old_name,
671 char *prefix_path, int start_pos,
674 if (strlen(old_name)+strlen(extension) >= PATHLEN) {
675 printf("One of your filenames is longer than %d characters.\n", PATHLEN);
678 if (prefix_path == NULL)
679 { sprintf(new_name,"%s%s", old_name, extension);
682 strcpy(new_name, prefix_path + start_pos);
683 for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
684 if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
685 if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
686 printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
689 sprintf(new_name + x, "%s%s", old_name, extension);
693 #ifdef FILE_EXTENSIONS
694 static char *check_extension(char *name, char *extension)
697 /* If a filename ends in '.', remove the dot and add no file extension: */
699 if (name[i] == '.') { name[i]=0; return ""; }
701 /* Remove the new extension if it's already got one: */
703 for (; (i>=0) && (name[i]!=FN_SEP); i--)
704 if (name[i] == '.') return "";
709 /* ------------------------------------------------------------------------- */
710 /* Three translation routines have to deal with path variables which may */
711 /* contain alternative locations separated by the FN_ALT character. */
712 /* These have the protocol: */
714 /* int translate_*_filename(int last_value, ...) */
716 /* and should first be called with last_value equal to 0. If the */
717 /* translated filename works, fine. Otherwise, if the returned integer */
718 /* was zero, the caller knows that no filename works and can issue an */
719 /* error message. If it was non-zero, the caller should pass it on as */
720 /* the last_value again. */
722 /* As implemented below, last_value is the position in the path variable */
723 /* string at which the next directory name to try begins. */
724 /* ------------------------------------------------------------------------- */
726 extern int translate_in_filename(int last_value,
727 char *new_name, char *old_name,
728 int same_directory_flag, int command_line_flag)
729 { char *prefix_path = NULL;
731 int add_path_flag = 1;
734 if ((same_directory_flag==0)
735 && (contains_separator(old_name)==1)) add_path_flag=0;
737 if (add_path_flag==1)
738 { if (command_line_flag == 0)
739 { /* File is opened as a result of an Include directive */
741 if (same_directory_flag==1)
742 prefix_path = current_source_path;
744 if (Include_Path[0]!=0) prefix_path = Include_Path;
746 /* Main file being opened from the command line */
748 else if (Source_Path[0]!=0) prefix_path = Source_Path;
751 #ifdef FILE_EXTENSIONS
752 /* Which file extension is expected? */
754 if ((command_line_flag==1)||(same_directory_flag==1))
755 extension = Source_Extension;
757 extension = Include_Extension;
759 extension = check_extension(old_name, extension);
764 last_value = write_translated_name(new_name, old_name,
765 prefix_path, last_value, extension);
767 /* Set the "current source path" (for use of Include ">...") */
769 if (command_line_flag==1)
770 { strcpy(current_source_path, new_name);
771 for (i=strlen(current_source_path)-1;
772 ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
774 if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir */
775 else current_source_path[0] = 0; /* Current file at root dir */
781 extern int translate_link_filename(int last_value,
782 char *new_name, char *old_name)
783 { char *prefix_path = NULL;
786 if (contains_separator(old_name)==0)
787 if (Module_Path[0]!=0)
788 prefix_path = Module_Path;
790 #ifdef FILE_EXTENSIONS
791 extension = check_extension(old_name, Module_Extension);
796 return write_translated_name(new_name, old_name,
797 prefix_path, last_value, extension);
800 static int translate_icl_filename(int last_value,
801 char *new_name, char *old_name)
802 { char *prefix_path = NULL;
803 char *extension = "";
805 if (contains_separator(old_name)==0)
807 prefix_path = ICL_Path;
809 #ifdef FILE_EXTENSIONS
810 extension = check_extension(old_name, ICL_Extension);
813 return write_translated_name(new_name, old_name,
814 prefix_path, last_value, extension);
817 extern void translate_out_filename(char *new_name, char *old_name)
819 char *extension = "";
822 /* If !convert_filename_flag, then the old_name is just the <file2>
823 parameter on the Inform command line, which we leave alone. */
825 if (!convert_filename_flag)
826 { strcpy(new_name, old_name); return;
829 /* Remove any pathname or extension in <file1>. */
831 if (contains_separator(old_name)==1)
832 { for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
833 if (old_name[i]==FN_SEP) i++;
836 #ifdef FILE_EXTENSIONS
837 for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
838 if (old_name[i] == '.') old_name[i] = 0;
843 { extension = Module_Extension;
844 if (Module_Path[0]!=0) prefix_path = Module_Path;
849 switch(version_number)
850 { case 3: extension = Code_Extension; break;
851 case 4: extension = V4Code_Extension; break;
852 case 5: extension = V5Code_Extension; break;
853 case 6: extension = V6Code_Extension; break;
854 case 7: extension = V7Code_Extension; break;
855 case 8: extension = V8Code_Extension; break;
859 extension = GlulxCode_Extension;
861 if (Code_Path[0]!=0) prefix_path = Code_Path;
864 #ifdef FILE_EXTENSIONS
865 extension = check_extension(old_name, extension);
868 write_translated_name(new_name, old_name, prefix_path, 0, extension);
871 static char *name_or_unset(char *p)
872 { if (p[0]==0) return "(unset)";
876 static void help_on_filenames(void)
877 { char old_name[PATHLEN];
878 char new_name[PATHLEN];
879 int save_mm = module_switch, x;
881 module_switch = FALSE;
883 printf("Help information on filenames:\n\n");
886 "The command line can take one of two forms:\n\n\
887 inform [commands...] <file1>\n\
888 inform [commands...] <file1> <file2>\n\n\
889 Inform translates <file1> into a source file name (see below) for its input.\n\
890 <file2> is usually omitted: if so, the output filename is made from <file1>\n\
891 by cutting out the name part and translating that (see below).\n\
892 If <file2> is given, however, the output filename is set to just <file2>\n\
893 (not altered in any way).\n\n");
896 "Filenames given in the game source (with commands like Include \"name\" and\n\
897 Link \"name\") are also translated by the rules below.\n\n");
900 "Rules of translation:\n\n\
901 Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
902 (such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
903 1. If the name contains a '%c' character (so it's already a pathname), it\n\
904 isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
907 " [Exception: when the name is given in an Include command using the >\n\
908 form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
909 of the file doing the inclusion");
910 #ifdef FILE_EXTENSIONS
911 printf(" and a suitable file extension is added");
916 " Filenames must never contain double-quotation marks \". To use filenames\n\
917 which contain spaces, write them in double-quotes: for instance,\n\n\
918 \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
921 "2. The file is looked for at a particular \"path\" (the filename of a\n\
922 directory), depending on what kind of file it is.\n\n\
923 File type Name Current setting\n\n\
924 Source code (in) source_path %s\n\
925 Include file (in) include_path %s\n\
926 Story file (out) code_path %s\n",
927 name_or_unset(Source_Path), name_or_unset(Include_Path),
928 name_or_unset(Code_Path));
931 " Temporary file (out) temporary_path %s\n\
932 ICL command file (in) icl_path %s\n\
933 Module (in & out) module_path %s\n\n",
934 name_or_unset(Temporary_Path),
935 name_or_unset(ICL_Path), name_or_unset(Module_Path));
938 " If the path is unset, then the current working directory is used (so\n\
939 the filename doesn't change): if, for instance, include_path is set to\n\
940 \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
941 \"backup%coldlib%cparser\".\n\n\
942 The paths can be set or unset on the Inform command line by, eg,\n\
943 \"inform +code_path=finished jigsaw\" or\n\
944 \"inform +include_path= balances\" (which unsets include_path).\n\n",
945 FN_SEP, FN_SEP, FN_SEP);
948 " The four input path variables can be set to lists of alternative paths\n\
949 separated by '%c' characters: these alternatives are always tried in\n\
950 the order they are specified in, that is, left to right through the text\n\
951 in the path variable.\n\n",
954 " If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
955 the path or paths are added to the existing list.\n\n");
957 " (Modules are written to the first alternative in the module_path list;\n\
958 it is an error to give alternatives at all for purely output paths.)\n\n");
960 #ifdef FILE_EXTENSIONS
961 printf("3. The following file extensions are added:\n\n\
964 Story files: %s (Version 3), %s (v4), %s (v5, the default),\n\
965 %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
966 Temporary files: .tmp\n\
968 Source_Extension, Include_Extension,
969 Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
970 V7Code_Extension, V8Code_Extension, GlulxCode_Extension,
973 except that any extension you give (on the command line or in a filename\n\
974 used in a program) will override these. If you give the null extension\n\
975 \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
978 printf("Names of four individual files can also be set using the same\n\
979 + command notation (though they aren't really pathnames). These are:\n\n\
980 transcript_name (text written by -r switch): now \"%s\"\n\
981 debugging_name (data written by -k switch): now \"%s\"\n\
982 language_name (library file defining natural language of game):\n\
984 charset_map (file for character set mapping): now \"%s\"\n\n",
985 Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
987 translate_in_filename(0, new_name, "rezrov", 0, 1);
988 printf("Examples: 1. \"inform rezrov\"\n\
989 the source code is read from \"%s\"\n",
991 convert_filename_flag = TRUE;
992 translate_out_filename(new_name, "rezrov");
993 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
995 translate_in_filename(0, new_name, "frotz", 0, 1);
996 printf("2. \"inform -M frotz\"\n\
997 the source code is read from \"%s\"\n",
999 module_switch = TRUE;
1000 convert_filename_flag = TRUE;
1001 translate_out_filename(new_name, "frotz");
1002 printf(" and a module is compiled to \"%s\".\n\n", new_name);
1004 module_switch = FALSE;
1006 sprintf(old_name, "demos%cplugh", FN_SEP);
1007 printf("3. \"inform %s\"\n", old_name);
1008 translate_in_filename(0, new_name, old_name, 0, 1);
1009 printf(" the source code is read from \"%s\"\n", new_name);
1010 sprintf(old_name, "demos%cplugh", FN_SEP);
1011 convert_filename_flag = TRUE;
1012 translate_out_filename(new_name, old_name);
1013 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1015 printf("4. \"inform plover my_demo\"\n");
1016 translate_in_filename(0, new_name, "plover", 0, 1);
1017 printf(" the source code is read from \"%s\"\n", new_name);
1018 convert_filename_flag = FALSE;
1019 translate_out_filename(new_name, "my_demo");
1020 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1022 strcpy(old_name, Source_Path);
1023 sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
1024 FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
1025 printf("5. \"inform +source_path=%s zooge\"\n", new_name);
1027 " Note that four alternative paths are given, the first being the empty\n\
1028 path-name (meaning: where you are now). Inform looks for the source code\n\
1029 by trying these four places in turn, stopping when it finds anything:\n\n");
1031 set_path_value(Source_Path, new_name);
1034 { x = translate_in_filename(x, new_name, "zooge", 0, 1);
1035 printf(" \"%s\"\n", new_name);
1037 strcpy(Source_Path, old_name);
1038 module_switch = save_mm;
1041 /* ------------------------------------------------------------------------- */
1042 /* Naming temporary files */
1043 /* (Arguably temporary files should be made using "tmpfile" in */
1044 /* the ANSI C library, but many supposed ANSI libraries lack it.) */
1045 /* ------------------------------------------------------------------------- */
1047 extern void translate_temp_filename(int i)
1050 { case 1: p=Temp1_Name; break;
1051 case 2: p=Temp2_Name; break;
1052 case 3: p=Temp3_Name; break;
1054 if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
1055 printf ("Temporary_Path is too long.\n");
1058 sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
1059 #ifdef INCLUDE_TASK_ID
1060 sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
1062 #ifdef FILE_EXTENSIONS
1063 sprintf(p+strlen(p), ".tmp");
1068 static char riscos_ft_buffer[4];
1070 extern char *riscos_file_type(void)
1072 if (riscos_file_type_format == 1)
1073 { if (module_switch) return("data");
1077 if (module_switch) return("075");
1079 sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
1080 return(riscos_ft_buffer);
1084 /* ------------------------------------------------------------------------- */
1085 /* The compilation pass */
1086 /* ------------------------------------------------------------------------- */
1088 static void run_pass(void)
1090 lexer_begin_prepass();
1091 files_begin_prepass();
1092 load_sourcefile(Source_Name, 0);
1096 parse_program(NULL);
1099 issue_unused_warnings();
1103 if (module_switch) linker_endpass();
1106 if (hash_switch && hash_printed_since_newline) printf("\n");
1108 if (temporary_files_switch)
1109 { if (module_switch) flush_link_data();
1113 if (track_unused_routines)
1114 locate_dead_functions();
1115 construct_storyfile();
1118 int output_has_occurred;
1120 static void rennab(int32 time_taken)
1121 { /* rennab = reverse of banner */
1123 int t = no_warnings + no_suppressed_warnings;
1125 if (memout_switch) print_memory_usage();
1127 if ((no_errors + t)!=0)
1128 { printf("Compiled with ");
1130 { printf("%d error%s", no_errors,(no_errors==1)?"":"s");
1131 if (t > 0) printf(" and ");
1133 if (no_warnings > 0)
1134 printf("%d warning%s", t, (t==1)?"":"s");
1135 if (no_suppressed_warnings > 0)
1136 { if (no_warnings > 0)
1137 printf(" (%d suppressed)", no_suppressed_warnings);
1139 printf("%d suppressed warning%s", no_suppressed_warnings,
1140 (no_suppressed_warnings==1)?"":"s");
1142 if (output_has_occurred == FALSE) printf(" (no output)");
1146 if (no_compiler_errors > 0) print_sorry_message();
1148 if (statistics_switch)
1149 printf("Completed in %ld seconds\n", (long int) time_taken);
1152 /* ------------------------------------------------------------------------- */
1153 /* The compiler abstracted to a routine. */
1154 /* ------------------------------------------------------------------------- */
1156 static int execute_icl_header(char *file1);
1158 static int compile(int number_of_files_specified, char *file1, char *file2)
1161 if (execute_icl_header(file1))
1164 select_target(glulx_mode);
1166 if (define_INFIX_switch && glulx_mode) {
1167 printf("Infix (-X) facilities are not available in Glulx: \
1168 disabling -X switch\n");
1169 define_INFIX_switch = FALSE;
1172 if (module_switch && glulx_mode) {
1173 printf("Modules are not available in Glulx: \
1174 disabling -M switch\n");
1175 module_switch = FALSE;
1178 if (define_INFIX_switch && module_switch)
1179 { printf("Infix (-X) facilities are not available when compiling \
1180 modules: disabling -X switch\n");
1181 define_INFIX_switch = FALSE;
1183 if (runtime_error_checking_switch && module_switch)
1184 { printf("Strict checking (-S) facilities are not available when \
1185 compiling modules: disabling -S switch\n");
1186 runtime_error_checking_switch = FALSE;
1189 time_start=time(0); no_compilations++;
1191 strcpy(Source_Name, file1); convert_filename_flag = TRUE;
1192 strcpy(Code_Name, file1);
1193 if (number_of_files_specified == 2)
1194 { strcpy(Code_Name, file2); convert_filename_flag = FALSE;
1199 if (debugfile_switch) begin_debug_file();
1203 if (transcript_switch) open_transcript_file(Source_Name);
1207 if (transcript_switch)
1208 { write_dictionary_to_transcript();
1209 close_transcript_file();
1212 if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
1213 else { output_has_occurred = FALSE; }
1215 if (debugfile_switch)
1219 if (temporary_files_switch && (no_errors>0)) remove_temp_files();
1223 rennab((int32) (time(0)-time_start));
1225 if (optimise_switch) optimise_abbreviations();
1227 if (store_the_text) my_free(&all_text,"transcription text");
1229 return (no_errors==0)?0:1;
1232 /* ------------------------------------------------------------------------- */
1233 /* The command line interpreter */
1234 /* ------------------------------------------------------------------------- */
1236 static void cli_print_help(int help_level)
1239 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
1240 story files, as well as \"Glulx\" story files:\n\
1241 Copyright (c) Graham Nelson 1993 - 2020.\n\n");
1243 /* For people typing just "inform", a summary only: */
1248 #ifndef PROMPT_INPUT
1249 printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
1251 printf("When run, Inform prompts you for commands (and switches),\n\
1252 which are optional, then an input <file1> and an (optional) output\n\
1257 "<file1> is the Inform source file of the game to be compiled. <file2>,\n\
1258 if given, overrides the filename Inform would normally use for the\n\
1259 compiled output. Try \"inform -h1\" for file-naming conventions.\n\n\
1260 One or more words can be supplied as \"commands\". These may be:\n\n\
1261 -switches a list of compiler switches, 1 or 2 letter\n\
1262 (see \"inform -h2\" for the full range)\n\n\
1263 +dir set Include_Path to this directory\n\
1264 ++dir add this directory to Include_Path\n\
1265 +PATH=dir change the PATH to this directory\n\
1266 ++PATH=dir add this directory to the PATH\n\n\
1267 $... one of the following memory commands:\n");
1270 " $list list current memory allocation settings\n\
1271 $huge make standard \"huge game\" settings %s\n\
1272 $large make standard \"large game\" settings %s\n\
1273 $small make standard \"small game\" settings %s\n\
1274 $?SETTING explain briefly what SETTING is for\n\
1275 $SETTING=number change SETTING to given number\n\n",
1276 (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
1277 (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
1278 (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
1281 " (filename) read in a list of commands (in the format above)\n\
1282 from this \"setup file\"\n\n");
1284 printf("Alternate command-line formats for the above:\n\
1285 --help (this page)\n\
1287 --addpath PATH=dir\n\
1289 --size huge, --size large, --size small\n\
1290 --helpopt SETTING\n\
1291 --opt SETTING=number\n\
1292 --config filename (setup file)\n\n");
1294 #ifndef PROMPT_INPUT
1295 printf("For example: \"inform -dexs $huge curses\".\n");
1301 /* The -h1 (filenaming) help information: */
1303 if (help_level == 1) { help_on_filenames(); return; }
1305 /* The -h2 (switches) help information: */
1307 printf("Help on the full list of legal switch commands:\n\n\
1308 a trace assembly-language (without hex dumps; see -t)\n\
1309 c more concise error messages\n\
1310 d contract double spaces after full stops in text\n\
1311 d2 contract double spaces after exclamation and question marks, too\n\
1312 e economy mode (slower): make use of declared abbreviations\n");
1315 f frequencies mode: show how useful abbreviations are\n\
1316 g traces calls to functions (except in the library)\n\
1317 g2 traces calls to all functions\n\
1318 h print this information\n");
1321 i ignore default switches set within the file\n\
1322 j list objects as constructed\n\
1323 k output Infix debugging information to \"%s\" (and switch -D on)\n\
1324 l list every statement run through Inform\n\
1325 m say how much memory has been allocated\n\
1326 n print numbers of properties, attributes and actions\n",
1329 o print offset addresses\n\
1330 p give percentage breakdown of story file\n\
1331 q keep quiet about obsolete usages\n\
1332 r record all the text to \"%s\"\n\
1333 s give statistics\n\
1334 t trace assembly-language (with full hex dumps; see -a)\n",
1338 u work out most useful abbreviations (very very slowly)\n\
1339 v3 compile to version-3 (\"Standard\") story file\n\
1340 v4 compile to version-4 (\"Plus\") story file\n\
1341 v5 compile to version-5 (\"Advanced\") story file: the default\n\
1342 v6 compile to version-6 (graphical) story file\n\
1343 v8 compile to version-8 (expanded \"Advanced\") story file\n\
1344 w disable warning messages\n\
1345 x print # for every 100 lines compiled\n\
1346 y trace linking system\n\
1347 z print memory map of the virtual machine\n\n");
1350 B use big memory model (for large V6/V7 files)\n\
1351 C0 text character set is plain ASCII only\n\
1352 Cu text character set is UTF-8\n\
1353 Cn text character set is ISO 8859-n (n = 1 to 9)\n\
1354 (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
1355 7, Greek; 8, Hebrew; 9, Latin5. Default is -C1.)\n");
1356 printf(" D insert \"Constant DEBUG;\" automatically\n");
1357 printf(" E0 Archimedes-style error messages%s\n",
1358 (error_format==0)?" (current setting)":"");
1359 printf(" E1 Microsoft-style error messages%s\n",
1360 (error_format==1)?" (current setting)":"");
1361 printf(" E2 Macintosh MPW-style error messages%s\n",
1362 (error_format==2)?" (current setting)":"");
1363 #ifdef USE_TEMPORARY_FILES
1364 printf(" F0 use extra memory rather than temporary files\n");
1366 printf(" F1 use temporary files to reduce memory consumption\n");
1368 printf(" G compile a Glulx game file\n");
1369 printf(" H use Huffman encoding to compress Glulx strings\n");
1370 printf(" M compile as a Module for future linking\n");
1374 R0 use filetype 060 + version number for games (default)\n\
1375 R1 use official Acorn filetype 11A for all games\n");
1377 printf(" S compile strict error-checking at run-time (on by default)\n");
1378 #ifdef ARC_THROWBACK
1379 printf(" T enable throwback of errors in the DDE\n");
1381 printf(" U insert \"Constant USE_MODULES;\" automatically\n");
1382 printf(" V print the version and date of this program\n");
1383 printf(" Wn header extension table is at least n words (n = 3 to 99)\n");
1384 printf(" X compile with INFIX debugging facilities present\n");
1388 extern void switches(char *p, int cmode)
1389 { int i, s=1, state;
1390 /* Here cmode is 0 if switches list is from a "Switches" directive
1391 and 1 if from a "-switches" command-line or ICL list */
1396 "Ignoring second word which should be a -list of switches.\n");
1400 for (i=cmode; p[i]!=0; i+=s, s=1)
1408 case 'a': asm_trace_setting = 1; break;
1409 case 'b': bothpasses_switch = state; break;
1410 case 'c': concise_switch = state; break;
1411 case 'd': switch(p[i+1])
1412 { case '1': double_space_setting=1; s=2; break;
1413 case '2': double_space_setting=2; s=2; break;
1414 default: double_space_setting=1; break;
1417 case 'e': economy_switch = state; break;
1418 case 'f': frequencies_switch = state; break;
1419 case 'g': switch(p[i+1])
1420 { case '1': trace_fns_setting=1; s=2; break;
1421 case '2': trace_fns_setting=2; s=2; break;
1422 default: trace_fns_setting=1; break;
1425 case 'h': switch(p[i+1])
1426 { case '1': cli_print_help(1); s=2; break;
1427 case '2': cli_print_help(2); s=2; break;
1429 default: cli_print_help(0); break;
1432 case 'i': ignore_switches_switch = state; break;
1433 case 'j': listobjects_switch = state; break;
1434 case 'k': if (cmode == 0)
1435 error("The switch '-k' can't be set with 'Switches'");
1437 { debugfile_switch = state;
1438 if (state) define_DEBUG_switch = TRUE;
1441 case 'l': listing_switch = state; break;
1442 case 'm': memout_switch = state; break;
1443 case 'n': printprops_switch = state; break;
1444 case 'o': offsets_switch = state; break;
1445 case 'p': percentages_switch = state; break;
1446 case 'q': obsolete_switch = state; break;
1447 case 'r': if (cmode == 0)
1448 error("The switch '-r' can't be set with 'Switches'");
1450 transcript_switch = state; break;
1451 case 's': statistics_switch = state; break;
1452 case 't': asm_trace_setting=2; break;
1453 case 'u': if (cmode == 0) {
1454 error("The switch '-u' can't be set with 'Switches'");
1457 optimise_switch = state; break;
1458 case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
1459 if ((cmode==0) && (version_set_switch)) { s=2; break; }
1460 version_set_switch = TRUE; s=2;
1462 { case '3': select_version(3); break;
1463 case '4': select_version(4); break;
1464 case '5': select_version(5); break;
1465 case '6': select_version(6); break;
1466 case '7': select_version(7); break;
1467 case '8': select_version(8); break;
1468 default: printf("-v must be followed by 3 to 8\n");
1469 version_set_switch=0; s=1;
1472 if ((version_number < 5) && (r_e_c_s_set == FALSE))
1473 runtime_error_checking_switch = FALSE;
1475 case 'w': nowarnings_switch = state; break;
1476 case 'x': hash_switch = state; break;
1477 case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
1478 case 'z': memory_map_switch = state; break;
1479 case 'B': oddeven_packing_switch = state; break;
1481 if (p[i+1] == 'u') {
1482 character_set_unicode = TRUE;
1483 /* Leave the set_setting on Latin-1, because that
1484 matches the first block of Unicode. */
1485 character_set_setting = 1;
1488 { character_set_setting=p[i+1]-'0';
1489 if ((character_set_setting < 0)
1490 || (character_set_setting > 9))
1491 { printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
1492 character_set_unicode = FALSE;
1493 character_set_setting = 1;
1496 if (cmode == 0) change_character_set();
1498 case 'D': define_DEBUG_switch = state; break;
1499 case 'E': switch(p[i+1])
1500 { case '0': s=2; error_format=0; break;
1501 case '1': s=2; error_format=1; break;
1502 case '2': s=2; error_format=2; break;
1503 default: error_format=1; break;
1506 case 'F': if (cmode == 0) {
1507 error("The switch '-F' can't be set with 'Switches'");
1511 { case '0': s=2; temporary_files_switch = FALSE; break;
1512 case '1': s=2; temporary_files_switch = TRUE; break;
1513 default: temporary_files_switch = state; break;
1516 case 'M': module_switch = state;
1517 if (state && (r_e_c_s_set == FALSE))
1518 runtime_error_checking_switch = FALSE;
1521 case 'R': switch(p[i+1])
1522 { case '0': s=2; riscos_file_type_format=0; break;
1523 case '1': s=2; riscos_file_type_format=1; break;
1524 default: riscos_file_type_format=1; break;
1528 #ifdef ARC_THROWBACK
1529 case 'T': throwback_switch = state; break;
1531 case 'S': runtime_error_checking_switch = state;
1532 r_e_c_s_set = TRUE; break;
1533 case 'G': if (cmode == 0)
1534 error("The switch '-G' can't be set with 'Switches'");
1535 else if (version_set_switch)
1536 error("The '-G' switch cannot follow the '-v' switch");
1538 { glulx_mode = state;
1539 adjust_memory_sizes();
1542 case 'H': compression_switch = state; break;
1543 case 'U': define_USE_MODULES_switch = state; break;
1544 case 'V': exit(0); break;
1545 case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
1546 { s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
1547 if ((p[i+2]>='0') && (p[i+2]<='9'))
1548 { s=3; ZCODE_HEADER_EXT_WORDS *= 10;
1549 ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
1553 case 'X': define_INFIX_switch = state; break;
1555 printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
1561 if (optimise_switch && (!store_the_text))
1562 { store_the_text=TRUE;
1565 printf("Allocation %ld bytes for transcription text\n",
1566 (long) MAX_TRANSCRIPT_SIZE);
1567 all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
1568 malloced_bytes += MAX_TRANSCRIPT_SIZE;
1570 fatalerror("Can't hallocate memory for transcription text. Darn.");
1572 all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
1577 static int icl_command(char *p)
1578 { if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
1579 || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
1583 static void icl_error(char *filename, int line)
1584 { printf("Error in ICL file '%s', line %d:\n", filename, line);
1587 static void icl_header_error(char *filename, int line)
1588 { printf("Error in ICL header of file '%s', line %d:\n", filename, line);
1591 static int copy_icl_word(char *from, char *to, int max)
1593 /* Copies one token from 'from' to 'to', null-terminated:
1594 returns the number of chars in 'from' read past (possibly 0). */
1596 int i, j, quoted_mode, truncated;
1598 i = 0; truncated = 0;
1599 while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
1600 || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
1603 { while (from[i] != 0) i++;
1604 to[0] = 0; return i;
1607 for (quoted_mode = FALSE, j=0;;)
1608 { if (from[i] == 0) break;
1609 if (from[i] == 10) break;
1610 if (from[i] == 13) break;
1611 if (from[i] == TAB_CHARACTER) break;
1612 if ((from[i] == ' ') && (!quoted_mode)) break;
1613 if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
1614 else to[j++] = from[i++];
1622 printf("The following parameter has been truncated:\n%s\n", to);
1626 /* Copy a string, converting to uppercase. The to array should be
1627 (at least) max characters. Result will be null-terminated, so
1628 at most max-1 characters will be copied.
1630 static int strcpyupper(char *to, char *from, int max)
1633 for (ix=0; ix<max-1; ix++) {
1635 if (islower(ch)) ch = toupper(ch);
1642 static void execute_icl_command(char *p);
1643 static int execute_dashdash_command(char *p, char *p2);
1645 static int execute_icl_header(char *argname)
1648 char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1652 char filename[PATHLEN];
1656 { x = translate_in_filename(x, filename, argname, 0, 1);
1657 command_file = fopen(filename,"r");
1658 } while ((command_file == NULL) && (x != 0));
1659 if (!command_file) {
1660 /* Fail silently. The regular compiler will try to open the file
1661 again, and report the problem. */
1665 while (feof(command_file)==0) {
1666 if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1668 if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
1670 i = copy_icl_word(cli_buff+2, fw, CMD_BUF_SIZE);
1671 if (icl_command(fw)) {
1672 execute_icl_command(fw);
1673 copy_icl_word(cli_buff+2 + i, fw, CMD_BUF_SIZE);
1674 if ((fw[0] != 0) && (fw[0] != '!')) {
1675 icl_header_error(filename, line);
1677 printf("expected comment or nothing but found '%s'\n", fw);
1682 icl_header_error(filename, line);
1684 printf("Expected command or comment but found '%s'\n", fw);
1688 fclose(command_file);
1690 return (errcount==0)?0:1;
1694 static void run_icl_file(char *filename, FILE *command_file)
1695 { char cli_buff[CMD_BUF_SIZE], fw[CMD_BUF_SIZE];
1697 printf("[Running ICL file '%s']\n", filename);
1699 while (feof(command_file)==0)
1700 { if (fgets(cli_buff,CMD_BUF_SIZE,command_file)==0) break;
1702 i = copy_icl_word(cli_buff, fw, CMD_BUF_SIZE);
1703 if (icl_command(fw))
1704 { execute_icl_command(fw);
1705 copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1706 if ((fw[0] != 0) && (fw[0] != '!'))
1707 { icl_error(filename, line);
1708 printf("expected comment or nothing but found '%s'\n", fw);
1712 { if (strcmp(fw, "compile")==0)
1713 { char story_name[PATHLEN], code_name[PATHLEN];
1714 i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
1715 i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
1717 if (code_name[0] != 0) x=2;
1718 else if (story_name[0] != 0) x=1;
1722 { case 0: icl_error(filename, line);
1723 printf("No filename given to 'compile'\n");
1725 case 1: printf("[Compiling <%s>]\n", story_name);
1726 compile(x, story_name, code_name);
1728 case 2: printf("[Compiling <%s> to <%s>]\n",
1729 story_name, code_name);
1730 compile(x, story_name, code_name);
1731 copy_icl_word(cli_buff + i, fw, CMD_BUF_SIZE);
1733 { icl_error(filename, line);
1734 printf("Expected comment or nothing but found '%s'\n",
1742 { icl_error(filename, line);
1743 printf("Expected command or comment but found '%s'\n", fw);
1749 /* This should only be called if the argument has been verified to be
1750 an ICL command, e.g. by checking icl_command().
1752 static void execute_icl_command(char *p)
1753 { char filename[PATHLEN], cli_buff[CMD_BUF_SIZE];
1758 { case '+': set_path_command(p+1); break;
1759 case '-': switches(p,1); break;
1760 case '$': memory_command(p+1); break;
1761 case '(': len = strlen(p);
1762 if (p[len-1] != ')') {
1763 printf("Error in ICL: (command) missing closing paren\n");
1766 len -= 2; /* omit parens */
1767 if (len > CMD_BUF_SIZE-1) len = CMD_BUF_SIZE-1;
1768 strncpy(cli_buff, p+1, len);
1772 { x = translate_icl_filename(x, filename, cli_buff);
1773 command_file = fopen(filename,"r");
1774 } while ((command_file == NULL) && (x != 0));
1776 if (command_file == NULL) {
1777 printf("Error in ICL: Couldn't open command file '%s'\n",
1781 run_icl_file(filename, command_file);
1782 fclose(command_file);
1787 /* Convert a --command into the equivalent ICL command and call
1788 execute_icl_command(). The dashes have already been stripped.
1790 The second argument is the following command-line argument
1791 (or NULL if there was none). This may or may not be consumed.
1792 Returns TRUE if it was.
1794 static int execute_dashdash_command(char *p, char *p2)
1796 char cli_buff[CMD_BUF_SIZE];
1797 int consumed2 = FALSE;
1799 if (!strcmp(p, "help")) {
1800 strcpy(cli_buff, "-h");
1802 else if (!strcmp(p, "list")) {
1803 strcpy(cli_buff, "$LIST");
1805 else if (!strcmp(p, "size")) {
1807 if (!(p2 && (!strcmpcis(p2, "HUGE") || !strcmpcis(p2, "LARGE") || !strcmpcis(p2, "SMALL")))) {
1808 printf("--size must be followed by \"huge\", \"large\", or \"small\"\n");
1811 strcpy(cli_buff, "$");
1812 strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1814 else if (!strcmp(p, "opt")) {
1816 if (!p2 || !strchr(p2, '=')) {
1817 printf("--opt must be followed by \"setting=number\"\n");
1820 strcpy(cli_buff, "$");
1821 strcpyupper(cli_buff+1, p2, CMD_BUF_SIZE-1);
1823 else if (!strcmp(p, "helpopt")) {
1826 printf("--helpopt must be followed by \"setting\"\n");
1829 strcpy(cli_buff, "$?");
1830 strcpyupper(cli_buff+2, p2, CMD_BUF_SIZE-2);
1832 else if (!strcmp(p, "path")) {
1834 if (!p2 || !strchr(p2, '=')) {
1835 printf("--path must be followed by \"name=path\"\n");
1838 snprintf(cli_buff, CMD_BUF_SIZE, "+%s", p2);
1840 else if (!strcmp(p, "addpath")) {
1842 if (!p2 || !strchr(p2, '=')) {
1843 printf("--addpath must be followed by \"name=path\"\n");
1846 snprintf(cli_buff, CMD_BUF_SIZE, "++%s", p2);
1848 else if (!strcmp(p, "config")) {
1851 printf("--config must be followed by \"file.icl\"\n");
1854 snprintf(cli_buff, CMD_BUF_SIZE, "(%s)", p2);
1857 printf("Option \"--%s\" unknown (try \"inform -h\")\n", p);
1861 execute_icl_command(cli_buff);
1865 /* ------------------------------------------------------------------------- */
1866 /* Opening and closing banners */
1867 /* ------------------------------------------------------------------------- */
1869 char banner_line[CMD_BUF_SIZE];
1871 /* We store the banner text for use elsewhere (see files.c).
1873 static void banner(void)
1876 snprintf(banner_line, CMD_BUF_SIZE, "Inform %d.%d%d",
1877 (VNUMBER/100)%10, (VNUMBER/10)%10, VNUMBER%10);
1878 #ifdef RELEASE_SUFFIX
1879 len = strlen(banner_line);
1880 snprintf(banner_line+len, CMD_BUF_SIZE-len, "%s", RELEASE_SUFFIX);
1882 #ifdef MACHINE_STRING
1883 len = strlen(banner_line);
1884 snprintf(banner_line+len, CMD_BUF_SIZE-len, " for %s", MACHINE_STRING);
1886 len = strlen(banner_line);
1887 snprintf(banner_line+len, CMD_BUF_SIZE-len, " (%s)", RELEASE_DATE);
1889 printf("%s\n", banner_line);
1892 /* ------------------------------------------------------------------------- */
1893 /* Input from the outside world */
1894 /* ------------------------------------------------------------------------- */
1897 static void read_command_line(int argc, char **argv)
1899 char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
1901 printf("Source filename?\n> ");
1902 while (gets(buffer1)==NULL); cli_file1=buffer1;
1903 printf("Output filename (RETURN for the same)?\n> ");
1904 while (gets(buffer2)==NULL); cli_file2=buffer2;
1905 cli_files_specified=1;
1906 if (buffer2[0]!=0) cli_files_specified=2;
1908 { printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
1909 while (gets(buffer3)==NULL); execute_icl_command(buffer3);
1910 } while (buffer3[0]!=0);
1913 static void read_command_line(int argc, char **argv)
1915 if (argc==1) switches("-h",1);
1917 for (i=1, cli_files_specified=0; i<argc; i++)
1918 if (argv[i][0] == '-' && argv[i][1] == '-') {
1919 char *nextarg = NULL;
1920 if (i+1 < argc) nextarg = argv[i+1];
1921 int consumed2 = execute_dashdash_command(argv[i]+2, nextarg);
1922 if (consumed2 && i+1 < argc) {
1926 else if (icl_command(argv[i])) {
1927 execute_icl_command(argv[i]);
1930 switch(++cli_files_specified)
1931 { case 1: cli_file1 = argv[i]; break;
1932 case 2: cli_file2 = argv[i]; break;
1934 printf("Command line error: unknown parameter '%s'\n",
1941 /* ------------------------------------------------------------------------- */
1942 /* M A I N : An outer shell for machine-specific quirks */
1943 /* Omitted altogether if EXTERNAL_SHELL is defined, as for instance is */
1944 /* needed for the Macintosh front end. */
1945 /* ------------------------------------------------------------------------- */
1947 #ifdef EXTERNAL_SHELL
1948 extern int sub_main(int argc, char **argv);
1951 static int sub_main(int argc, char **argv);
1953 int main(int argc, char **argv, char *envp[])
1955 int main(int argc, char **argv)
1959 InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
1961 rcode = sub_main(argc, argv);
1962 #ifdef ARC_THROWBACK
1970 /* ------------------------------------------------------------------------- */
1971 /* M A I N II: Starting up ICL with the command line */
1972 /* ------------------------------------------------------------------------- */
1974 #ifdef EXTERNAL_SHELL
1975 extern int sub_main(int argc, char **argv)
1977 static int sub_main(int argc, char **argv)
1982 ProcessEvents (&g_proc);
1985 if (store_the_text) my_free(&all_text,"transcription text");
1986 longjmp (g_fallback, 1);
1992 set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
1993 reset_switch_settings(); select_version(5);
1995 cli_files_specified = 0; no_compilations = 0;
1996 cli_file1 = "source"; cli_file2 = "output";
1998 read_command_line(argc, argv);
2000 if (cli_files_specified > 0)
2001 { return_code = compile(cli_files_specified, cli_file1, cli_file2);
2003 if (return_code != 0) return(return_code);
2006 if (no_compilations == 0)
2007 printf("\n[No compilation requested]\n");
2008 if (no_compilations > 1)
2009 printf("[%d compilations completed]\n", no_compilations);
2014 /* ========================================================================= */