1 /* ------------------------------------------------------------------------- */
2 /* "inform" : The top level of Inform: switches, pathnames, filenaming */
3 /* conventions, ICL (Inform Command Line) files, main */
5 /* Copyright (c) Graham Nelson 1993 - 2018 */
7 /* This file is part of Inform. */
9 /* Inform is free software: you can redistribute it and/or modify */
10 /* it under the terms of the GNU General Public License as published by */
11 /* the Free Software Foundation, either version 3 of the License, or */
12 /* (at your option) any later version. */
14 /* Inform is distributed in the hope that it will be useful, */
15 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
16 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
17 /* GNU General Public License for more details. */
19 /* You should have received a copy of the GNU General Public License */
20 /* along with Inform. If not, see https://gnu.org/licenses/ */
22 /* ------------------------------------------------------------------------- */
24 #define MAIN_INFORM_FILE
27 /* ------------------------------------------------------------------------- */
28 /* Compiler progress */
29 /* ------------------------------------------------------------------------- */
31 static int no_compilations;
33 int endofpass_flag; /* set to TRUE when an "end" directive is reached
34 (the inputs routines insert one into the stream
37 /* ------------------------------------------------------------------------- */
39 /* ------------------------------------------------------------------------- */
41 int version_number, /* 3 to 8 (Z-code) */
42 instruction_set_number,
43 /* 3 to 6: versions 7 and 8 use instruction set of
45 extend_memory_map; /* extend using function- and string-offsets */
46 int32 scale_factor, /* packed address multiplier */
47 length_scale_factor; /* length-in-header multiplier */
49 int32 requested_glulx_version;
51 extern void select_version(int vn)
52 { version_number = vn;
53 extend_memory_map = FALSE;
54 if ((version_number==6)||(version_number==7)) extend_memory_map = TRUE;
57 if (version_number==3) scale_factor = 2;
58 if (version_number==8) scale_factor = 8;
60 length_scale_factor = scale_factor;
61 if ((version_number==6)||(version_number==7)) length_scale_factor = 8;
63 instruction_set_number = version_number;
64 if ((version_number==7)||(version_number==8)) instruction_set_number = 5;
67 static int select_glulx_version(char *str)
69 /* Parse an "X.Y.Z" style version number, and store it for later use. */
71 int major=0, minor=0, patch=0;
74 major = major*10 + ((*cx++)-'0');
78 minor = minor*10 + ((*cx++)-'0');
82 patch = patch*10 + ((*cx++)-'0');
86 requested_glulx_version = ((major & 0x7FFF) << 16)
87 + ((minor & 0xFF) << 8)
92 /* ------------------------------------------------------------------------- */
93 /* Target: variables which vary between the Z-machine and Glulx */
94 /* ------------------------------------------------------------------------- */
96 int WORDSIZE; /* Size of a machine word: 2 or 4 */
97 int32 MAXINTWORD; /* 0x7FFF or 0x7FFFFFFF */
99 /* The first property number which is an individual property. The
100 eight class-system i-props (create, recreate, ... print_to_array)
101 are numbered from INDIV_PROP_START to INDIV_PROP_START+7.
103 int INDIV_PROP_START;
105 /* The length of an object, as written in tables.c. It's easier to define
106 it here than to repeat the same expression all over the source code.
109 int OBJECT_BYTE_LENGTH;
110 /* The total length of a dict entry, in bytes. Not used in Z-code.
112 int DICT_ENTRY_BYTE_LENGTH;
113 /* The position in a dict entry that the flag values begin.
116 int DICT_ENTRY_FLAG_POS;
118 static void select_target(int targ)
125 if (INDIV_PROP_START != 64) {
126 INDIV_PROP_START = 64;
127 fatalerror("You cannot change INDIV_PROP_START in Z-code");
129 if (DICT_WORD_SIZE != 6) {
131 fatalerror("You cannot change DICT_WORD_SIZE in Z-code");
133 if (DICT_CHAR_SIZE != 1) {
135 fatalerror("You cannot change DICT_CHAR_SIZE in Z-code");
137 if (NUM_ATTR_BYTES != 6) {
139 fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
141 if (MAX_LOCAL_VARIABLES != 16) {
142 MAX_LOCAL_VARIABLES = 16;
143 fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
145 if (MAX_GLOBAL_VARIABLES != 240) {
146 MAX_GLOBAL_VARIABLES = 240;
147 fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
149 if (MAX_VERBS > 255) {
151 fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
157 MAXINTWORD = 0x7FFFFFFF;
158 scale_factor = 0; /* It should never even get used in Glulx */
160 if (INDIV_PROP_START < 256) {
161 INDIV_PROP_START = 256;
162 warning_numbered("INDIV_PROP_START should be at least 256 in Glulx. Setting to", INDIV_PROP_START);
165 if (NUM_ATTR_BYTES % 4 != 3) {
166 NUM_ATTR_BYTES += (3 - (NUM_ATTR_BYTES % 4));
167 warning_numbered("NUM_ATTR_BYTES must be a multiple of four, plus three. Increasing to", NUM_ATTR_BYTES);
170 if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) {
172 warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE);
176 if (MAX_LOCAL_VARIABLES >= 120) {
177 MAX_LOCAL_VARIABLES = 119;
178 warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119");
179 /* This is because the keyword table in the lexer only has 120
182 if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
183 DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
185 "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting",
187 /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */
189 if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) {
190 NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES;
192 "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting",
194 /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
197 /* Set up a few more variables that depend on the above values */
201 DICT_WORD_BYTES = DICT_WORD_SIZE;
202 /* The Z-code generator doesn't use the following variables, although
203 it would be a little cleaner if it did. */
204 OBJECT_BYTE_LENGTH = 0;
205 DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9;
206 DICT_ENTRY_FLAG_POS = 0;
210 OBJECT_BYTE_LENGTH = (1 + (NUM_ATTR_BYTES) + 6*4 + (GLULX_OBJECT_EXT_BYTES));
211 DICT_WORD_BYTES = DICT_WORD_SIZE*DICT_CHAR_SIZE;
212 if (DICT_CHAR_SIZE == 1) {
213 DICT_ENTRY_BYTE_LENGTH = (7+DICT_WORD_BYTES);
214 DICT_ENTRY_FLAG_POS = (1+DICT_WORD_BYTES);
217 DICT_ENTRY_BYTE_LENGTH = (12+DICT_WORD_BYTES);
218 DICT_ENTRY_FLAG_POS = (4+DICT_WORD_BYTES);
223 /* ------------------------------------------------------------------------- */
224 /* Tracery: output control variables */
225 /* ------------------------------------------------------------------------- */
227 int asm_trace_level, /* trace assembly: 0 for off, 1 for assembly
228 only, 2 for full assembly tracing with hex dumps */
229 line_trace_level, /* line tracing: 0 off, 1 on */
230 expr_trace_level, /* expression tracing: 0 off, 1 full, 2 brief */
231 linker_trace_level, /* set by -y: 0 to 4 levels of tracing */
232 tokens_trace_level; /* lexer output tracing: 0 off, 1 on */
234 /* ------------------------------------------------------------------------- */
235 /* On/off switch variables (by default all FALSE); other switch settings */
236 /* ------------------------------------------------------------------------- */
238 int bothpasses_switch, /* -b */
239 concise_switch, /* -c */
240 economy_switch, /* -e */
241 frequencies_switch, /* -f */
242 ignore_switches_switch, /* -i */
243 listobjects_switch, /* -j */
244 debugfile_switch, /* -k */
245 listing_switch, /* -l */
246 memout_switch, /* -m */
247 printprops_switch, /* -n */
248 offsets_switch, /* -o */
249 percentages_switch, /* -p */
250 obsolete_switch, /* -q */
251 transcript_switch, /* -r */
252 statistics_switch, /* -s */
253 optimise_switch, /* -u */
254 version_set_switch, /* -v */
255 nowarnings_switch, /* -w */
256 hash_switch, /* -x */
257 memory_map_switch, /* -z */
258 oddeven_packing_switch, /* -B */
259 define_DEBUG_switch, /* -D */
260 temporary_files_switch, /* -F */
261 module_switch, /* -M */
262 runtime_error_checking_switch, /* -S */
263 define_USE_MODULES_switch, /* -U */
264 define_INFIX_switch; /* -X */
266 int throwback_switch; /* -T */
269 int riscos_file_type_format; /* set by -R */
271 int compression_switch; /* set by -H */
272 int character_set_setting, /* set by -C0 through -C9 */
273 character_set_unicode, /* set by -Cu */
274 error_format, /* set by -E */
275 asm_trace_setting, /* set by -a and -t: value of
276 asm_trace_level to use when tracing */
277 double_space_setting, /* set by -d: 0, 1 or 2 */
278 trace_fns_setting, /* set by -g: 0, 1 or 2 */
279 linker_trace_setting, /* set by -y: ditto for linker_... */
280 store_the_text; /* when set, record game text to a chunk
281 of memory (used by both -r & -k) */
282 static int r_e_c_s_set; /* has -S been explicitly set? */
284 int glulx_mode; /* -G */
286 static void reset_switch_settings(void)
287 { asm_trace_setting=0;
288 linker_trace_level=0;
289 tokens_trace_level=0;
291 store_the_text = FALSE;
293 bothpasses_switch = FALSE;
294 concise_switch = FALSE;
295 double_space_setting = 0;
296 economy_switch = FALSE;
297 frequencies_switch = FALSE;
298 trace_fns_setting = 0;
299 ignore_switches_switch = FALSE;
300 listobjects_switch = FALSE;
301 debugfile_switch = FALSE;
302 listing_switch = FALSE;
303 memout_switch = FALSE;
304 printprops_switch = FALSE;
305 offsets_switch = FALSE;
306 percentages_switch = FALSE;
307 obsolete_switch = FALSE;
308 transcript_switch = FALSE;
309 statistics_switch = FALSE;
310 optimise_switch = FALSE;
311 version_set_switch = FALSE;
312 nowarnings_switch = FALSE;
314 memory_map_switch = FALSE;
315 oddeven_packing_switch = FALSE;
316 define_DEBUG_switch = FALSE;
317 #ifdef USE_TEMPORARY_FILES
318 temporary_files_switch = TRUE;
320 temporary_files_switch = FALSE;
322 define_USE_MODULES_switch = FALSE;
323 module_switch = FALSE;
325 throwback_switch = FALSE;
327 runtime_error_checking_switch = TRUE;
329 define_INFIX_switch = FALSE;
331 riscos_file_type_format = 0;
333 error_format=DEFAULT_ERROR_FORMAT;
335 character_set_setting = 1; /* Default is ISO Latin-1 */
336 character_set_unicode = FALSE;
338 compression_switch = TRUE;
340 requested_glulx_version = 0;
343 /* ------------------------------------------------------------------------- */
344 /* Number of files given as command line parameters (0, 1 or 2) */
345 /* ------------------------------------------------------------------------- */
347 static int cli_files_specified,
348 convert_filename_flag;
350 char Source_Name[PATHLEN]; /* Processed name of first input file */
351 char Code_Name[PATHLEN]; /* Processed name of output file */
353 static char *cli_file1, *cli_file2; /* Unprocessed (and unsafe to alter) */
355 /* ========================================================================= */
356 /* Data structure management routines */
357 /* ------------------------------------------------------------------------- */
359 static void init_vars(void)
367 init_expressc_vars();
368 init_expressp_vars();
383 static void begin_pass(void)
389 directs_begin_pass();
391 expressc_begin_pass();
392 expressp_begin_pass();
395 endofpass_flag = FALSE;
396 line_trace_level = 0; expr_trace_level = 0;
397 asm_trace_level = asm_trace_setting;
398 linker_trace_level = linker_trace_setting;
399 if (listing_switch) line_trace_level=1;
404 objects_begin_pass();
406 symbols_begin_pass();
415 /* Compile a Main__ routine (see "veneer.c") */
417 compile_initial_routine();
419 /* Make the four metaclasses: Class must be object number 1, so
420 it must come first */
425 make_class("Object");
426 make_class("Routine");
427 make_class("String");
433 extern void allocate_arrays(void)
435 arrays_allocate_arrays();
436 asm_allocate_arrays();
437 bpatch_allocate_arrays();
438 chars_allocate_arrays();
439 directs_allocate_arrays();
440 errors_allocate_arrays();
441 expressc_allocate_arrays();
442 expressp_allocate_arrays();
443 files_allocate_arrays();
445 lexer_allocate_arrays();
446 linker_allocate_arrays();
447 memory_allocate_arrays();
448 objects_allocate_arrays();
449 states_allocate_arrays();
450 symbols_allocate_arrays();
451 syntax_allocate_arrays();
452 tables_allocate_arrays();
453 text_allocate_arrays();
454 veneer_allocate_arrays();
455 verbs_allocate_arrays();
458 extern void free_arrays(void)
460 /* One array may survive this routine, all_the_text (used to hold
461 game text until the abbreviations optimiser begins work on it): this
462 array (if it was ever allocated) is freed at the top level. */
464 arrays_free_arrays();
466 bpatch_free_arrays();
468 directs_free_arrays();
469 errors_free_arrays();
470 expressc_free_arrays();
471 expressp_free_arrays();
475 linker_free_arrays();
476 memory_free_arrays();
477 objects_free_arrays();
478 states_free_arrays();
479 symbols_free_arrays();
480 syntax_free_arrays();
481 tables_free_arrays();
483 veneer_free_arrays();
487 /* ------------------------------------------------------------------------- */
488 /* Name translation code for filenames */
489 /* ------------------------------------------------------------------------- */
491 static char Source_Path[PATHLEN];
492 static char Include_Path[PATHLEN];
493 static char Code_Path[PATHLEN];
494 static char Module_Path[PATHLEN];
495 static char Temporary_Path[PATHLEN];
496 static char current_source_path[PATHLEN];
497 char Debugging_Name[PATHLEN];
498 char Transcript_Name[PATHLEN];
499 char Language_Name[PATHLEN];
500 char Charset_Map[PATHLEN];
501 static char ICL_Path[PATHLEN];
503 /* Set one of the above Path buffers to the given location, or list of
504 locations. (A list is comma-separated, and only accepted for Source_Path,
505 Include_Path, ICL_Path, Module_Path.)
507 static void set_path_value(char *path, char *value)
512 if (i >= PATHLEN-1) {
513 printf("A specified path is longer than %d characters.\n",
517 if ((value[j] == FN_ALT) || (value[j] == 0))
518 { if ((value[j] == FN_ALT)
519 && (path != Source_Path) && (path != Include_Path)
520 && (path != ICL_Path) && (path != Module_Path))
521 { printf("The character '%c' is used to divide entries in a list \
522 of possible locations, and can only be used in the Include_Path, Source_Path, \
523 Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
526 if ((path != Debugging_Name) && (path != Transcript_Name)
527 && (path != Language_Name) && (path != Charset_Map)
528 && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
529 path[i++] = value[j++];
530 if (value[j-1] == 0) return;
532 else path[i++] = value[j++];
536 /* Prepend the given location or list of locations to one of the above
537 Path buffers. This is only permitted for Source_Path, Include_Path,
538 ICL_Path, Module_Path.
540 An empty field (in the comma-separated list) means the current
541 directory. If the Path buffer is entirely empty, we assume that
542 we want to search both value and the current directory, so
543 the result will be "value,".
545 static void prepend_path_value(char *path, char *value)
548 int oldlen = strlen(path);
550 char new_path[PATHLEN];
552 if ((path != Source_Path) && (path != Include_Path)
553 && (path != ICL_Path) && (path != Module_Path))
554 { printf("The character '+' is used to add to a list \
555 of possible locations, and can only be used in the Include_Path, Source_Path, \
556 Module_Path or ICL_Path variables. Other paths are for output only.\n");
562 if (i >= PATHLEN-1) {
563 printf("A specified path is longer than %d characters.\n",
567 if ((value[j] == FN_ALT) || (value[j] == 0))
568 { if ((path != Debugging_Name) && (path != Transcript_Name)
569 && (path != Language_Name) && (path != Charset_Map)
570 && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
571 new_path[i++] = value[j++];
572 if (value[j-1] == 0) {
577 else new_path[i++] = value[j++];
580 if (newlen+1+oldlen >= PATHLEN-1) {
581 printf("A specified path is longer than %d characters.\n",
587 new_path[i++] = FN_ALT;
589 new_path[i++] = path[j++];
592 strcpy(path, new_path);
595 static void set_default_paths(void)
597 set_path_value(Source_Path, Source_Directory);
598 set_path_value(Include_Path, Include_Directory);
599 set_path_value(Code_Path, Code_Directory);
600 set_path_value(Module_Path, Module_Directory);
601 set_path_value(ICL_Path, ICL_Directory);
602 set_path_value(Temporary_Path, Temporary_Directory);
603 set_path_value(Debugging_Name, Debugging_File);
604 set_path_value(Transcript_Name, Transcript_File);
605 set_path_value(Language_Name, "english");
606 set_path_value(Charset_Map, "");
609 /* Parse a path option which looks like "dir", "+dir", "pathname=dir",
610 or "+pathname=dir". If there is no "=", we assume "include_path=...".
611 If the option begins with a "+" the directory is prepended to the
612 existing path instead of replacing it.
614 static void set_path_command(char *command)
615 { int i, j; char *path_to_set = NULL;
618 if (command[0] == '+') {
623 for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
625 path_to_set=Include_Path;
627 if (command[i] == '=') {
628 char pathname[PATHLEN];
629 if (i>=PATHLEN) i=PATHLEN-1;
631 char ch = command[j];
632 if (isupper(ch)) ch=tolower(ch);
636 command = command+i+1;
639 if (strcmp(pathname, "source_path")==0) path_to_set=Source_Path;
640 if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
641 if (strcmp(pathname, "code_path")==0) path_to_set=Code_Path;
642 if (strcmp(pathname, "module_path")==0) path_to_set=Module_Path;
643 if (strcmp(pathname, "icl_path")==0) path_to_set=ICL_Path;
644 if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
645 if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
646 if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
647 if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
648 if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
650 if (path_to_set == NULL)
651 { printf("No such path setting as \"%s\"\n", pathname);
657 set_path_value(path_to_set, command);
659 prepend_path_value(path_to_set, command);
662 static int contains_separator(char *name)
664 for (i=0; name[i]!=0; i++)
665 if (name[i] == FN_SEP) return 1;
669 static int write_translated_name(char *new_name, char *old_name,
670 char *prefix_path, int start_pos,
673 if (strlen(old_name)+strlen(extension) >= PATHLEN) {
674 printf("One of your filenames is longer than %d characters.\n", PATHLEN);
677 if (prefix_path == NULL)
678 { sprintf(new_name,"%s%s", old_name, extension);
681 strcpy(new_name, prefix_path + start_pos);
682 for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
683 if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
684 if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
685 printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
688 sprintf(new_name + x, "%s%s", old_name, extension);
692 #ifdef FILE_EXTENSIONS
693 static char *check_extension(char *name, char *extension)
696 /* If a filename ends in '.', remove the dot and add no file extension: */
698 if (name[i] == '.') { name[i]=0; return ""; }
700 /* Remove the new extension if it's already got one: */
702 for (; (i>=0) && (name[i]!=FN_SEP); i--)
703 if (name[i] == '.') return "";
708 /* ------------------------------------------------------------------------- */
709 /* Three translation routines have to deal with path variables which may */
710 /* contain alternative locations separated by the FN_ALT character. */
711 /* These have the protocol: */
713 /* int translate_*_filename(int last_value, ...) */
715 /* and should first be called with last_value equal to 0. If the */
716 /* translated filename works, fine. Otherwise, if the returned integer */
717 /* was zero, the caller knows that no filename works and can issue an */
718 /* error message. If it was non-zero, the caller should pass it on as */
719 /* the last_value again. */
721 /* As implemented below, last_value is the position in the path variable */
722 /* string at which the next directory name to try begins. */
723 /* ------------------------------------------------------------------------- */
725 extern int translate_in_filename(int last_value,
726 char *new_name, char *old_name,
727 int same_directory_flag, int command_line_flag)
728 { char *prefix_path = NULL;
730 int add_path_flag = 1;
733 if ((same_directory_flag==0)
734 && (contains_separator(old_name)==1)) add_path_flag=0;
736 if (add_path_flag==1)
737 { if (command_line_flag == 0)
738 { /* File is opened as a result of an Include directive */
740 if (same_directory_flag==1)
741 prefix_path = current_source_path;
743 if (Include_Path[0]!=0) prefix_path = Include_Path;
745 /* Main file being opened from the command line */
747 else if (Source_Path[0]!=0) prefix_path = Source_Path;
750 #ifdef FILE_EXTENSIONS
751 /* Which file extension is expected? */
753 if ((command_line_flag==1)||(same_directory_flag==1))
754 extension = Source_Extension;
756 extension = Include_Extension;
758 extension = check_extension(old_name, extension);
763 last_value = write_translated_name(new_name, old_name,
764 prefix_path, last_value, extension);
766 /* Set the "current source path" (for use of Include ">...") */
768 if (command_line_flag==1)
769 { strcpy(current_source_path, new_name);
770 for (i=strlen(current_source_path)-1;
771 ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
773 if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir */
774 else current_source_path[0] = 0; /* Current file at root dir */
780 extern int translate_link_filename(int last_value,
781 char *new_name, char *old_name)
782 { char *prefix_path = NULL;
785 if (contains_separator(old_name)==0)
786 if (Module_Path[0]!=0)
787 prefix_path = Module_Path;
789 #ifdef FILE_EXTENSIONS
790 extension = check_extension(old_name, Module_Extension);
795 return write_translated_name(new_name, old_name,
796 prefix_path, last_value, extension);
799 static int translate_icl_filename(int last_value,
800 char *new_name, char *old_name)
801 { char *prefix_path = NULL;
802 char *extension = "";
804 if (contains_separator(old_name)==0)
806 prefix_path = ICL_Path;
808 #ifdef FILE_EXTENSIONS
809 extension = check_extension(old_name, ICL_Extension);
812 return write_translated_name(new_name, old_name,
813 prefix_path, last_value, extension);
816 extern void translate_out_filename(char *new_name, char *old_name)
818 char *extension = "";
821 /* If !convert_filename_flag, then the old_name is just the <file2>
822 parameter on the Inform command line, which we leave alone. */
824 if (!convert_filename_flag)
825 { strcpy(new_name, old_name); return;
828 /* Remove any pathname or extension in <file1>. */
830 if (contains_separator(old_name)==1)
831 { for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
832 if (old_name[i]==FN_SEP) i++;
835 #ifdef FILE_EXTENSIONS
836 for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
837 if (old_name[i] == '.') old_name[i] = 0;
842 { extension = Module_Extension;
843 if (Module_Path[0]!=0) prefix_path = Module_Path;
848 switch(version_number)
849 { case 3: extension = Code_Extension; break;
850 case 4: extension = V4Code_Extension; break;
851 case 5: extension = V5Code_Extension; break;
852 case 6: extension = V6Code_Extension; break;
853 case 7: extension = V7Code_Extension; break;
854 case 8: extension = V8Code_Extension; break;
858 extension = GlulxCode_Extension;
860 if (Code_Path[0]!=0) prefix_path = Code_Path;
863 #ifdef FILE_EXTENSIONS
864 extension = check_extension(old_name, extension);
867 write_translated_name(new_name, old_name, prefix_path, 0, extension);
870 static char *name_or_unset(char *p)
871 { if (p[0]==0) return "(unset)";
875 static void help_on_filenames(void)
876 { char old_name[PATHLEN];
877 char new_name[PATHLEN];
878 int save_mm = module_switch, x;
880 module_switch = FALSE;
882 printf("Help information on filenames:\n\n");
885 "The command line can take one of two forms:\n\n\
886 inform [commands...] <file1>\n\
887 inform [commands...] <file1> <file2>\n\n\
888 Inform translates <file1> into a source file name (see below) for its input.\n\
889 <file2> is usually omitted: if so, the output filename is made from <file1>\n\
890 by cutting out the name part and translating that (see below).\n\
891 If <file2> is given, however, the output filename is set to just <file2>\n\
892 (not altered in any way).\n\n");
895 "Filenames given in the game source (with commands like Include \"name\" and\n\
896 Link \"name\") are also translated by the rules below.\n\n");
899 "Rules of translation:\n\n\
900 Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
901 (such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
902 1. If the name contains a '%c' character (so it's already a pathname), it\n\
903 isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
906 " [Exception: when the name is given in an Include command using the >\n\
907 form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
908 of the file doing the inclusion");
909 #ifdef FILE_EXTENSIONS
910 printf(" and a suitable file extension is added");
915 " Filenames must never contain double-quotation marks \". To use filenames\n\
916 which contain spaces, write them in double-quotes: for instance,\n\n\
917 \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
920 "2. The file is looked for at a particular \"path\" (the filename of a\n\
921 directory), depending on what kind of file it is.\n\n\
922 File type Name Current setting\n\n\
923 Source code (in) source_path %s\n\
924 Include file (in) include_path %s\n\
925 Story file (out) code_path %s\n",
926 name_or_unset(Source_Path), name_or_unset(Include_Path),
927 name_or_unset(Code_Path));
930 " Temporary file (out) temporary_path %s\n\
931 ICL command file (in) icl_path %s\n\
932 Module (in & out) module_path %s\n\n",
933 name_or_unset(Temporary_Path),
934 name_or_unset(ICL_Path), name_or_unset(Module_Path));
937 " If the path is unset, then the current working directory is used (so\n\
938 the filename doesn't change): if, for instance, include_path is set to\n\
939 \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
940 \"backup%coldlib%cparser\".\n\n\
941 The paths can be set or unset on the Inform command line by, eg,\n\
942 \"inform +code_path=finished jigsaw\" or\n\
943 \"inform +include_path= balances\" (which unsets include_path).\n\n",
944 FN_SEP, FN_SEP, FN_SEP);
947 " The four input path variables can be set to lists of alternative paths\n\
948 separated by '%c' characters: these alternatives are always tried in\n\
949 the order they are specified in, that is, left to right through the text\n\
950 in the path variable.\n\n",
953 " If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
954 the path or paths are added to the existing list.\n\n");
956 " (Modules are written to the first alternative in the module_path list;\n\
957 it is an error to give alternatives at all for purely output paths.)\n\n");
959 #ifdef FILE_EXTENSIONS
960 printf("3. The following file extensions are added:\n\n\
963 Story files: %s (Version 3), %s (v4), %s (v5, the default),\n\
964 %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
965 Temporary files: .tmp\n\
967 Source_Extension, Include_Extension,
968 Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
969 V7Code_Extension, V8Code_Extension, GlulxCode_Extension,
972 except that any extension you give (on the command line or in a filename\n\
973 used in a program) will override these. If you give the null extension\n\
974 \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
977 printf("Names of four individual files can also be set using the same\n\
978 + command notation (though they aren't really pathnames). These are:\n\n\
979 transcript_name (text written by -r switch): now \"%s\"\n\
980 debugging_name (data written by -k switch): now \"%s\"\n\
981 language_name (library file defining natural language of game):\n\
983 charset_map (file for character set mapping): now \"%s\"\n\n",
984 Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
986 translate_in_filename(0, new_name, "rezrov", 0, 1);
987 printf("Examples: 1. \"inform rezrov\"\n\
988 the source code is read from \"%s\"\n",
990 convert_filename_flag = TRUE;
991 translate_out_filename(new_name, "rezrov");
992 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
994 translate_in_filename(0, new_name, "frotz", 0, 1);
995 printf("2. \"inform -M frotz\"\n\
996 the source code is read from \"%s\"\n",
998 module_switch = TRUE;
999 convert_filename_flag = TRUE;
1000 translate_out_filename(new_name, "frotz");
1001 printf(" and a module is compiled to \"%s\".\n\n", new_name);
1003 module_switch = FALSE;
1005 sprintf(old_name, "demos%cplugh", FN_SEP);
1006 printf("3. \"inform %s\"\n", old_name);
1007 translate_in_filename(0, new_name, old_name, 0, 1);
1008 printf(" the source code is read from \"%s\"\n", new_name);
1009 sprintf(old_name, "demos%cplugh", FN_SEP);
1010 convert_filename_flag = TRUE;
1011 translate_out_filename(new_name, old_name);
1012 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1014 printf("4. \"inform plover my_demo\"\n");
1015 translate_in_filename(0, new_name, "plover", 0, 1);
1016 printf(" the source code is read from \"%s\"\n", new_name);
1017 convert_filename_flag = FALSE;
1018 translate_out_filename(new_name, "my_demo");
1019 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1021 strcpy(old_name, Source_Path);
1022 sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
1023 FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
1024 printf("5. \"inform +source_path=%s zooge\"\n", new_name);
1026 " Note that four alternative paths are given, the first being the empty\n\
1027 path-name (meaning: where you are now). Inform looks for the source code\n\
1028 by trying these four places in turn, stopping when it finds anything:\n\n");
1030 set_path_value(Source_Path, new_name);
1033 { x = translate_in_filename(x, new_name, "zooge", 0, 1);
1034 printf(" \"%s\"\n", new_name);
1036 strcpy(Source_Path, old_name);
1037 module_switch = save_mm;
1040 /* ------------------------------------------------------------------------- */
1041 /* Naming temporary files */
1042 /* (Arguably temporary files should be made using "tmpfile" in */
1043 /* the ANSI C library, but many supposed ANSI libraries lack it.) */
1044 /* ------------------------------------------------------------------------- */
1046 extern void translate_temp_filename(int i)
1049 { case 1: p=Temp1_Name; break;
1050 case 2: p=Temp2_Name; break;
1051 case 3: p=Temp3_Name; break;
1053 if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
1054 printf ("Temporary_Path is too long.\n");
1057 sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
1058 #ifdef INCLUDE_TASK_ID
1059 sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
1061 #ifdef FILE_EXTENSIONS
1062 sprintf(p+strlen(p), ".tmp");
1067 static char riscos_ft_buffer[4];
1069 extern char *riscos_file_type(void)
1071 if (riscos_file_type_format == 1)
1072 { if (module_switch) return("data");
1076 if (module_switch) return("075");
1078 sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
1079 return(riscos_ft_buffer);
1083 /* ------------------------------------------------------------------------- */
1084 /* The compilation pass */
1085 /* ------------------------------------------------------------------------- */
1087 static void run_pass(void)
1089 lexer_begin_prepass();
1090 files_begin_prepass();
1091 load_sourcefile(Source_Name, 0);
1095 parse_program(NULL);
1098 issue_unused_warnings();
1102 if (module_switch) linker_endpass();
1105 if (hash_switch && hash_printed_since_newline) printf("\n");
1107 if (temporary_files_switch)
1108 { if (module_switch) flush_link_data();
1112 if (track_unused_routines)
1113 locate_dead_functions();
1114 construct_storyfile();
1117 int output_has_occurred;
1119 static void rennab(int32 time_taken)
1120 { /* rennab = reverse of banner */
1122 int t = no_warnings + no_suppressed_warnings;
1124 if (memout_switch) print_memory_usage();
1126 if ((no_errors + t)!=0)
1127 { printf("Compiled with ");
1129 { printf("%d error%s", no_errors,(no_errors==1)?"":"s");
1130 if (t > 0) printf(" and ");
1132 if (no_warnings > 0)
1133 printf("%d warning%s", t, (t==1)?"":"s");
1134 if (no_suppressed_warnings > 0)
1135 { if (no_warnings > 0)
1136 printf(" (%d suppressed)", no_suppressed_warnings);
1138 printf("%d suppressed warning%s", no_suppressed_warnings,
1139 (no_suppressed_warnings==1)?"":"s");
1141 if (output_has_occurred == FALSE) printf(" (no output)");
1145 if (no_compiler_errors > 0) print_sorry_message();
1147 if (statistics_switch)
1148 printf("Completed in %ld seconds\n", (long int) time_taken);
1151 /* ------------------------------------------------------------------------- */
1152 /* The compiler abstracted to a routine. */
1153 /* ------------------------------------------------------------------------- */
1155 static int execute_icl_header(char *file1);
1157 static int compile(int number_of_files_specified, char *file1, char *file2)
1160 if (execute_icl_header(file1))
1163 select_target(glulx_mode);
1165 if (define_INFIX_switch && glulx_mode) {
1166 printf("Infix (-X) facilities are not available in Glulx: \
1167 disabling -X switch\n");
1168 define_INFIX_switch = FALSE;
1171 if (module_switch && glulx_mode) {
1172 printf("Modules are not available in Glulx: \
1173 disabling -M switch\n");
1174 module_switch = FALSE;
1177 if (define_INFIX_switch && module_switch)
1178 { printf("Infix (-X) facilities are not available when compiling \
1179 modules: disabling -X switch\n");
1180 define_INFIX_switch = FALSE;
1182 if (runtime_error_checking_switch && module_switch)
1183 { printf("Strict checking (-S) facilities are not available when \
1184 compiling modules: disabling -S switch\n");
1185 runtime_error_checking_switch = FALSE;
1188 time_start=time(0); no_compilations++;
1190 strcpy(Source_Name, file1); convert_filename_flag = TRUE;
1191 strcpy(Code_Name, file1);
1192 if (number_of_files_specified == 2)
1193 { strcpy(Code_Name, file2); convert_filename_flag = FALSE;
1198 if (debugfile_switch) begin_debug_file();
1202 if (transcript_switch) open_transcript_file(Source_Name);
1206 if (transcript_switch)
1207 { write_dictionary_to_transcript();
1208 close_transcript_file();
1211 if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
1212 else { output_has_occurred = FALSE; }
1214 if (debugfile_switch)
1218 if (temporary_files_switch && (no_errors>0)) remove_temp_files();
1222 rennab((int32) (time(0)-time_start));
1224 if (optimise_switch) optimise_abbreviations();
1226 if (store_the_text) my_free(&all_text,"transcription text");
1228 return (no_errors==0)?0:1;
1231 /* ------------------------------------------------------------------------- */
1232 /* The command line interpreter */
1233 /* ------------------------------------------------------------------------- */
1235 static void cli_print_help(int help_level)
1238 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
1239 story files: copyright (c) Graham Nelson 1993 - 2017.\n\n");
1241 /* For people typing just "inform", a summary only: */
1246 #ifndef PROMPT_INPUT
1247 printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
1249 printf("When run, Inform prompts you for commands (and switches),\n\
1250 which are optional, then an input <file1> and an (optional) output\n\
1255 "<file1> is the Inform source file of the game to be compiled. <file2>,\n\
1256 if given, overrides the filename Inform would normally use for the\n\
1257 compiled output. Try \"inform -h1\" for file-naming conventions.\n\n\
1258 One or more words can be supplied as \"commands\". These may be:\n\n\
1259 -switches a list of compiler switches, 1 or 2 letter\n\
1260 (see \"inform -h2\" for the full range)\n\n\
1261 +dir set Include_Path to this directory\n\
1262 +PATH=dir change the PATH to this directory\n\n\
1263 $... one of the following memory commands:\n");
1265 " $list list current memory allocation settings\n\
1266 $huge make standard \"huge game\" settings %s\n\
1267 $large make standard \"large game\" settings %s\n\
1268 $small make standard \"small game\" settings %s\n\
1269 $?SETTING explain briefly what SETTING is for\n\
1270 $SETTING=number change SETTING to given number\n\n\
1271 (filename) read in a list of commands (in the format above)\n\
1272 from this \"setup file\"\n\n",
1273 (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
1274 (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
1275 (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
1277 #ifndef PROMPT_INPUT
1278 printf("For example: \"inform -dexs $huge curses\".\n\n");
1284 /* The -h1 (filenaming) help information: */
1286 if (help_level == 1) { help_on_filenames(); return; }
1288 /* The -h2 (switches) help information: */
1290 printf("Help on the full list of legal switch commands:\n\n\
1291 a trace assembly-language (without hex dumps; see -t)\n\
1292 c more concise error messages\n\
1293 d contract double spaces after full stops in text\n\
1294 d2 contract double spaces after exclamation and question marks, too\n\
1295 e economy mode (slower): make use of declared abbreviations\n");
1298 f frequencies mode: show how useful abbreviations are\n\
1299 g traces calls to functions (except in the library)\n\
1300 g2 traces calls to all functions\n\
1301 h print this information\n");
1304 i ignore default switches set within the file\n\
1305 j list objects as constructed\n\
1306 k output Infix debugging information to \"%s\" (and switch -D on)\n\
1307 l list every statement run through Inform\n\
1308 m say how much memory has been allocated\n\
1309 n print numbers of properties, attributes and actions\n",
1312 o print offset addresses\n\
1313 p give percentage breakdown of story file\n\
1314 q keep quiet about obsolete usages\n\
1315 r record all the text to \"%s\"\n\
1316 s give statistics\n\
1317 t trace assembly-language (with full hex dumps; see -a)\n",
1321 u work out most useful abbreviations (very very slowly)\n\
1322 v3 compile to version-3 (\"Standard\") story file\n\
1323 v4 compile to version-4 (\"Plus\") story file\n\
1324 v5 compile to version-5 (\"Advanced\") story file: the default\n\
1325 v6 compile to version-6 (graphical) story file\n\
1326 v8 compile to version-8 (expanded \"Advanced\") story file\n\
1327 w disable warning messages\n\
1328 x print # for every 100 lines compiled\n\
1329 y trace linking system\n\
1330 z print memory map of the Z-machine\n\n");
1333 B use big memory model (for large V6/V7 files)\n\
1334 C0 text character set is plain ASCII only\n\
1335 Cu text character set is UTF-8\n\
1336 Cn text character set is ISO 8859-n (n = 1 to 9)\n\
1337 (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
1338 7, Greek; 8, Hebrew; 9, Latin5. Default is -C1.)\n");
1339 printf(" D insert \"Constant DEBUG;\" automatically\n");
1340 printf(" E0 Archimedes-style error messages%s\n",
1341 (error_format==0)?" (current setting)":"");
1342 printf(" E1 Microsoft-style error messages%s\n",
1343 (error_format==1)?" (current setting)":"");
1344 printf(" E2 Macintosh MPW-style error messages%s\n",
1345 (error_format==2)?" (current setting)":"");
1346 #ifdef USE_TEMPORARY_FILES
1347 printf(" F0 use extra memory rather than temporary files\n");
1349 printf(" F1 use temporary files to reduce memory consumption\n");
1351 printf(" G compile a Glulx game file\n");
1352 printf(" H use Huffman encoding to compress Glulx strings\n");
1353 printf(" M compile as a Module for future linking\n");
1357 R0 use filetype 060 + version number for games (default)\n\
1358 R1 use official Acorn filetype 11A for all games\n");
1360 printf(" S compile strict error-checking at run-time (on by default)\n");
1361 #ifdef ARC_THROWBACK
1362 printf(" T enable throwback of errors in the DDE\n");
1364 printf(" U insert \"Constant USE_MODULES;\" automatically\n");
1365 printf(" V print the version and date of this program\n");
1366 printf(" Wn header extension table is at least n words (n = 3 to 99)\n");
1367 printf(" X compile with INFIX debugging facilities present\n");
1371 extern void switches(char *p, int cmode)
1372 { int i, s=1, state;
1373 /* Here cmode is 0 if switches list is from a "Switches" directive
1374 and 1 if from a "-switches" command-line or ICL list */
1379 "Ignoring second word which should be a -list of switches.\n");
1383 for (i=cmode; p[i]!=0; i+=s, s=1)
1391 case 'a': asm_trace_setting = 1; break;
1392 case 'b': bothpasses_switch = state; break;
1393 case 'c': concise_switch = state; break;
1394 case 'd': switch(p[i+1])
1395 { case '1': double_space_setting=1; s=2; break;
1396 case '2': double_space_setting=2; s=2; break;
1397 default: double_space_setting=1; break;
1400 case 'e': economy_switch = state; break;
1401 case 'f': frequencies_switch = state; break;
1402 case 'g': switch(p[i+1])
1403 { case '1': trace_fns_setting=1; s=2; break;
1404 case '2': trace_fns_setting=2; s=2; break;
1405 default: trace_fns_setting=1; break;
1408 case 'h': switch(p[i+1])
1409 { case '1': cli_print_help(1); s=2; break;
1410 case '2': cli_print_help(2); s=2; break;
1412 default: cli_print_help(0); break;
1415 case 'i': ignore_switches_switch = state; break;
1416 case 'j': listobjects_switch = state; break;
1417 case 'k': if (cmode == 0)
1418 error("The switch '-k' can't be set with 'Switches'");
1420 { debugfile_switch = state;
1421 if (state) define_DEBUG_switch = TRUE;
1424 case 'l': listing_switch = state; break;
1425 case 'm': memout_switch = state; break;
1426 case 'n': printprops_switch = state; break;
1427 case 'o': offsets_switch = state; break;
1428 case 'p': percentages_switch = state; break;
1429 case 'q': obsolete_switch = state; break;
1430 case 'r': if (cmode == 0)
1431 error("The switch '-r' can't be set with 'Switches'");
1433 transcript_switch = state; break;
1434 case 's': statistics_switch = state; break;
1435 case 't': asm_trace_setting=2; break;
1436 case 'u': if (cmode == 0) {
1437 error("The switch '-u' can't be set with 'Switches'");
1440 optimise_switch = state; break;
1441 case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
1442 if ((cmode==0) && (version_set_switch)) { s=2; break; }
1443 version_set_switch = TRUE; s=2;
1445 { case '3': select_version(3); break;
1446 case '4': select_version(4); break;
1447 case '5': select_version(5); break;
1448 case '6': select_version(6); break;
1449 case '7': select_version(7); break;
1450 case '8': select_version(8); break;
1451 default: printf("-v must be followed by 3 to 8\n");
1452 version_set_switch=0; s=1;
1455 if ((version_number < 5) && (r_e_c_s_set == FALSE))
1456 runtime_error_checking_switch = FALSE;
1458 case 'w': nowarnings_switch = state; break;
1459 case 'x': hash_switch = state; break;
1460 case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
1461 case 'z': memory_map_switch = state; break;
1462 case 'B': oddeven_packing_switch = state; break;
1464 if (p[i+1] == 'u') {
1465 character_set_unicode = TRUE;
1466 /* Leave the set_setting on Latin-1, because that
1467 matches the first block of Unicode. */
1468 character_set_setting = 1;
1471 { character_set_setting=p[i+1]-'0';
1472 if ((character_set_setting < 0)
1473 || (character_set_setting > 9))
1474 { printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
1475 character_set_unicode = FALSE;
1476 character_set_setting = 1;
1479 if (cmode == 0) change_character_set();
1481 case 'D': define_DEBUG_switch = state; break;
1482 case 'E': switch(p[i+1])
1483 { case '0': s=2; error_format=0; break;
1484 case '1': s=2; error_format=1; break;
1485 case '2': s=2; error_format=2; break;
1486 default: error_format=1; break;
1489 case 'F': if (cmode == 0) {
1490 error("The switch '-F' can't be set with 'Switches'");
1494 { case '0': s=2; temporary_files_switch = FALSE; break;
1495 case '1': s=2; temporary_files_switch = TRUE; break;
1496 default: temporary_files_switch = state; break;
1499 case 'M': module_switch = state;
1500 if (state && (r_e_c_s_set == FALSE))
1501 runtime_error_checking_switch = FALSE;
1504 case 'R': switch(p[i+1])
1505 { case '0': s=2; riscos_file_type_format=0; break;
1506 case '1': s=2; riscos_file_type_format=1; break;
1507 default: riscos_file_type_format=1; break;
1511 #ifdef ARC_THROWBACK
1512 case 'T': throwback_switch = state; break;
1514 case 'S': runtime_error_checking_switch = state;
1515 r_e_c_s_set = TRUE; break;
1516 case 'G': if (cmode == 0)
1517 error("The switch '-G' can't be set with 'Switches'");
1519 { glulx_mode = state;
1520 adjust_memory_sizes();
1523 case 'H': compression_switch = state; break;
1524 case 'U': define_USE_MODULES_switch = state; break;
1525 case 'V': exit(0); break;
1526 case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
1527 { s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
1528 if ((p[i+2]>='0') && (p[i+2]<='9'))
1529 { s=3; ZCODE_HEADER_EXT_WORDS *= 10;
1530 ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
1534 case 'X': define_INFIX_switch = state; break;
1536 printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
1542 if (optimise_switch && (!store_the_text))
1543 { store_the_text=TRUE;
1546 printf("Allocation %ld bytes for transcription text\n",
1547 (long) MAX_TRANSCRIPT_SIZE);
1548 all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
1549 malloced_bytes += MAX_TRANSCRIPT_SIZE;
1551 fatalerror("Can't hallocate memory for transcription text. Darn.");
1553 all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
1558 static int icl_command(char *p)
1559 { if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
1560 || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
1564 static void icl_error(char *filename, int line)
1565 { printf("Error in ICL file '%s', line %d:\n", filename, line);
1568 static void icl_header_error(char *filename, int line)
1569 { printf("Error in ICL header of file '%s', line %d:\n", filename, line);
1572 static int copy_icl_word(char *from, char *to, int max)
1574 /* Copies one token from 'from' to 'to', null-terminated:
1575 returns the number of chars in 'from' read past (possibly 0). */
1577 int i, j, quoted_mode, truncated;
1579 i = 0; truncated = 0;
1580 while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
1581 || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
1584 { while (from[i] != 0) i++;
1585 to[0] = 0; return i;
1588 for (quoted_mode = FALSE, j=0;;)
1589 { if (from[i] == 0) break;
1590 if (from[i] == 10) break;
1591 if (from[i] == 13) break;
1592 if (from[i] == TAB_CHARACTER) break;
1593 if ((from[i] == ' ') && (!quoted_mode)) break;
1594 if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
1595 else to[j++] = from[i++];
1603 printf("The following parameter has been truncated:\n%s\n", to);
1607 static void execute_icl_command(char *p);
1609 static int execute_icl_header(char *argname)
1612 char cli_buff[256], fw[256];
1616 char filename[PATHLEN];
1620 { x = translate_in_filename(x, filename, argname, 0, 1);
1621 command_file = fopen(filename,"r");
1622 } while ((command_file == NULL) && (x != 0));
1623 if (!command_file) {
1624 /* Fail silently. The regular compiler will try to open the file
1625 again, and report the problem. */
1629 while (feof(command_file)==0) {
1630 if (fgets(cli_buff,256,command_file)==0) break;
1632 if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
1634 i = copy_icl_word(cli_buff+2, fw, 256);
1635 if (icl_command(fw)) {
1636 execute_icl_command(fw);
1637 copy_icl_word(cli_buff+2 + i, fw, 256);
1638 if ((fw[0] != 0) && (fw[0] != '!')) {
1639 icl_header_error(filename, line);
1641 printf("expected comment or nothing but found '%s'\n", fw);
1646 icl_header_error(filename, line);
1648 printf("Expected command or comment but found '%s'\n", fw);
1652 fclose(command_file);
1654 return (errcount==0)?0:1;
1658 static void run_icl_file(char *filename, FILE *command_file)
1659 { char cli_buff[256], fw[256];
1661 printf("[Running ICL file '%s']\n", filename);
1663 while (feof(command_file)==0)
1664 { if (fgets(cli_buff,256,command_file)==0) break;
1666 i = copy_icl_word(cli_buff, fw, 256);
1667 if (icl_command(fw))
1668 { execute_icl_command(fw);
1669 copy_icl_word(cli_buff + i, fw, 256);
1670 if ((fw[0] != 0) && (fw[0] != '!'))
1671 { icl_error(filename, line);
1672 printf("expected comment or nothing but found '%s'\n", fw);
1676 { if (strcmp(fw, "compile")==0)
1677 { char story_name[PATHLEN], code_name[PATHLEN];
1678 i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
1679 i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
1681 if (code_name[0] != 0) x=2;
1682 else if (story_name[0] != 0) x=1;
1686 { case 0: icl_error(filename, line);
1687 printf("No filename given to 'compile'\n");
1689 case 1: printf("[Compiling <%s>]\n", story_name);
1690 compile(x, story_name, code_name);
1692 case 2: printf("[Compiling <%s> to <%s>]\n",
1693 story_name, code_name);
1694 compile(x, story_name, code_name);
1695 copy_icl_word(cli_buff + i, fw, 256);
1697 { icl_error(filename, line);
1698 printf("Expected comment or nothing but found '%s'\n",
1706 { icl_error(filename, line);
1707 printf("Expected command or comment but found '%s'\n", fw);
1713 static void execute_icl_command(char *p)
1714 { char filename[PATHLEN], cli_buff[256];
1718 { case '+': set_path_command(p+1); break;
1719 case '-': switches(p,1); break;
1720 case '$': memory_command(p+1); break;
1721 case '(': strcpy(cli_buff,p+1); cli_buff[strlen(cli_buff)-1]=0;
1724 { x = translate_icl_filename(x, filename, cli_buff);
1725 command_file = fopen(filename,"r");
1726 } while ((command_file == NULL) && (x != 0));
1728 if (command_file == NULL)
1729 printf("Error in ICL: Couldn't open command file '%s'\n",
1732 { run_icl_file(filename, command_file);
1733 fclose(command_file);
1739 /* ------------------------------------------------------------------------- */
1740 /* Opening and closing banners */
1741 /* ------------------------------------------------------------------------- */
1743 char banner_line[80];
1745 static void banner(void)
1747 sprintf(banner_line, "Inform %d.%d%d",
1748 (VNUMBER/100)%10, (VNUMBER/10)%10, VNUMBER%10);
1749 #ifdef RELEASE_SUFFIX
1750 strcat(banner_line, RELEASE_SUFFIX);
1752 #ifdef MACHINE_STRING
1753 sprintf(banner_line+strlen(banner_line), " for %s", MACHINE_STRING);
1755 sprintf(banner_line+strlen(banner_line), " (%s)", RELEASE_DATE);
1756 printf("%s\n", banner_line);
1759 /* ------------------------------------------------------------------------- */
1760 /* Input from the outside world */
1761 /* ------------------------------------------------------------------------- */
1764 static void read_command_line(int argc, char **argv)
1766 char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
1768 printf("Source filename?\n> ");
1769 while (gets(buffer1)==NULL); cli_file1=buffer1;
1770 printf("Output filename (RETURN for the same)?\n> ");
1771 while (gets(buffer2)==NULL); cli_file2=buffer2;
1772 cli_files_specified=1;
1773 if (buffer2[0]!=0) cli_files_specified=2;
1775 { printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
1776 while (gets(buffer3)==NULL); execute_icl_command(buffer3);
1777 } while (buffer3[0]!=0);
1780 static void read_command_line(int argc, char **argv)
1782 if (argc==1) switches("-h",1);
1784 for (i=1, cli_files_specified=0; i<argc; i++)
1785 if (icl_command(argv[i]))
1786 execute_icl_command(argv[i]);
1788 switch(++cli_files_specified)
1789 { case 1: cli_file1 = argv[i]; break;
1790 case 2: cli_file2 = argv[i]; break;
1792 printf("Command line error: unknown parameter '%s'\n",
1798 /* ------------------------------------------------------------------------- */
1799 /* M A I N : An outer shell for machine-specific quirks */
1800 /* Omitted altogether if EXTERNAL_SHELL is defined, as for instance is */
1801 /* needed for the Macintosh front end. */
1802 /* ------------------------------------------------------------------------- */
1804 #ifdef EXTERNAL_SHELL
1805 extern int sub_main(int argc, char **argv);
1808 static int sub_main(int argc, char **argv);
1810 int main(int argc, char **argv, char *envp[])
1812 int main(int argc, char **argv)
1816 InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
1818 rcode = sub_main(argc, argv);
1819 #ifdef ARC_THROWBACK
1827 /* ------------------------------------------------------------------------- */
1828 /* M A I N II: Starting up ICL with the command line */
1829 /* ------------------------------------------------------------------------- */
1831 #ifdef EXTERNAL_SHELL
1832 extern int sub_main(int argc, char **argv)
1834 static int sub_main(int argc, char **argv)
1839 ProcessEvents (&g_proc);
1842 if (store_the_text) my_free(&all_text,"transcription text");
1843 longjmp (g_fallback, 1);
1849 set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
1850 reset_switch_settings(); select_version(5);
1852 cli_files_specified = 0; no_compilations = 0;
1853 cli_file1 = "source"; cli_file2 = "output";
1855 read_command_line(argc, argv);
1857 if (cli_files_specified > 0)
1858 { return_code = compile(cli_files_specified, cli_file1, cli_file2);
1860 if (return_code != 0) return(return_code);
1863 if (no_compilations == 0)
1864 printf("\n[No compilation requested]\n");
1865 if (no_compilations > 1)
1866 printf("[%d compilations completed]\n", no_compilations);
1871 /* ========================================================================= */