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 - 2016 */
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)
124 INDIV_PROP_START = 64;
126 if (DICT_WORD_SIZE != 6) {
128 fatalerror("You cannot change DICT_WORD_SIZE in Z-code");
130 if (DICT_CHAR_SIZE != 1) {
132 fatalerror("You cannot change DICT_CHAR_SIZE in Z-code");
134 if (NUM_ATTR_BYTES != 6) {
136 fatalerror("You cannot change NUM_ATTR_BYTES in Z-code");
138 if (MAX_LOCAL_VARIABLES != 16) {
139 MAX_LOCAL_VARIABLES = 16;
140 fatalerror("You cannot change MAX_LOCAL_VARIABLES in Z-code");
142 if (MAX_GLOBAL_VARIABLES != 240) {
143 MAX_GLOBAL_VARIABLES = 240;
144 fatalerror("You cannot change MAX_GLOBAL_VARIABLES in Z-code");
146 if (MAX_VERBS > 255) {
148 fatalerror("MAX_VERBS can only go above 255 when Glulx is used");
154 MAXINTWORD = 0x7FFFFFFF;
155 INDIV_PROP_START = 256; /* This could be a memory setting */
156 scale_factor = 0; /* It should never even get used in Glulx */
158 if (NUM_ATTR_BYTES % 4 != 3) {
159 NUM_ATTR_BYTES += (3 - (NUM_ATTR_BYTES % 4));
160 warning_numbered("NUM_ATTR_BYTES must be a multiple of four, plus three. Increasing to", NUM_ATTR_BYTES);
163 if (DICT_CHAR_SIZE != 1 && DICT_CHAR_SIZE != 4) {
165 warning_numbered("DICT_CHAR_SIZE must be either 1 or 4. Setting to", DICT_CHAR_SIZE);
169 if (MAX_LOCAL_VARIABLES >= 120) {
170 MAX_LOCAL_VARIABLES = 119;
171 warning("MAX_LOCAL_VARIABLES cannot exceed 119; resetting to 119");
172 /* This is because the keyword table in the lexer only has 120
175 if (DICT_WORD_SIZE > MAX_DICT_WORD_SIZE) {
176 DICT_WORD_SIZE = MAX_DICT_WORD_SIZE;
178 "DICT_WORD_SIZE cannot exceed MAX_DICT_WORD_SIZE; resetting",
180 /* MAX_DICT_WORD_SIZE can be increased in header.h without fear. */
182 if (NUM_ATTR_BYTES > MAX_NUM_ATTR_BYTES) {
183 NUM_ATTR_BYTES = MAX_NUM_ATTR_BYTES;
185 "NUM_ATTR_BYTES cannot exceed MAX_NUM_ATTR_BYTES; resetting",
187 /* MAX_NUM_ATTR_BYTES can be increased in header.h without fear. */
190 /* Set up a few more variables that depend on the above values */
194 DICT_WORD_BYTES = DICT_WORD_SIZE;
195 /* The Z-code generator doesn't use the following variables, although
196 it would be a little cleaner if it did. */
197 OBJECT_BYTE_LENGTH = 0;
198 DICT_ENTRY_BYTE_LENGTH = (version_number==3)?7:9;
199 DICT_ENTRY_FLAG_POS = 0;
203 OBJECT_BYTE_LENGTH = (1 + (NUM_ATTR_BYTES) + 6*4 + (GLULX_OBJECT_EXT_BYTES));
204 DICT_WORD_BYTES = DICT_WORD_SIZE*DICT_CHAR_SIZE;
205 if (DICT_CHAR_SIZE == 1) {
206 DICT_ENTRY_BYTE_LENGTH = (7+DICT_WORD_BYTES);
207 DICT_ENTRY_FLAG_POS = (1+DICT_WORD_BYTES);
210 DICT_ENTRY_BYTE_LENGTH = (12+DICT_WORD_BYTES);
211 DICT_ENTRY_FLAG_POS = (4+DICT_WORD_BYTES);
216 /* ------------------------------------------------------------------------- */
217 /* Tracery: output control variables */
218 /* ------------------------------------------------------------------------- */
220 int asm_trace_level, /* trace assembly: 0 for off, 1 for assembly
221 only, 2 for full assembly tracing with hex dumps */
222 line_trace_level, /* line tracing: 0 off, 1 on */
223 expr_trace_level, /* expression tracing: 0 off, 1 full, 2 brief */
224 linker_trace_level, /* set by -y: 0 to 4 levels of tracing */
225 tokens_trace_level; /* lexer output tracing: 0 off, 1 on */
227 /* ------------------------------------------------------------------------- */
228 /* On/off switch variables (by default all FALSE); other switch settings */
229 /* ------------------------------------------------------------------------- */
231 int bothpasses_switch, /* -b */
232 concise_switch, /* -c */
233 economy_switch, /* -e */
234 frequencies_switch, /* -f */
235 ignore_switches_switch, /* -i */
236 listobjects_switch, /* -j */
237 debugfile_switch, /* -k */
238 listing_switch, /* -l */
239 memout_switch, /* -m */
240 printprops_switch, /* -n */
241 offsets_switch, /* -o */
242 percentages_switch, /* -p */
243 obsolete_switch, /* -q */
244 transcript_switch, /* -r */
245 statistics_switch, /* -s */
246 optimise_switch, /* -u */
247 version_set_switch, /* -v */
248 nowarnings_switch, /* -w */
249 hash_switch, /* -x */
250 memory_map_switch, /* -z */
251 oddeven_packing_switch, /* -B */
252 define_DEBUG_switch, /* -D */
253 temporary_files_switch, /* -F */
254 module_switch, /* -M */
255 runtime_error_checking_switch, /* -S */
256 define_USE_MODULES_switch, /* -U */
257 define_INFIX_switch; /* -X */
259 int throwback_switch; /* -T */
262 int riscos_file_type_format; /* set by -R */
264 int compression_switch; /* set by -H */
265 int character_set_setting, /* set by -C0 through -C9 */
266 character_set_unicode, /* set by -Cu */
267 error_format, /* set by -E */
268 asm_trace_setting, /* set by -a and -t: value of
269 asm_trace_level to use when tracing */
270 double_space_setting, /* set by -d: 0, 1 or 2 */
271 trace_fns_setting, /* set by -g: 0, 1 or 2 */
272 linker_trace_setting, /* set by -y: ditto for linker_... */
273 store_the_text; /* when set, record game text to a chunk
274 of memory (used by both -r & -k) */
275 static int r_e_c_s_set; /* has -S been explicitly set? */
277 int glulx_mode; /* -G */
279 static void reset_switch_settings(void)
280 { asm_trace_setting=0;
281 linker_trace_level=0;
282 tokens_trace_level=0;
284 store_the_text = FALSE;
286 bothpasses_switch = FALSE;
287 concise_switch = FALSE;
288 double_space_setting = 0;
289 economy_switch = FALSE;
290 frequencies_switch = FALSE;
291 trace_fns_setting = 0;
292 ignore_switches_switch = FALSE;
293 listobjects_switch = FALSE;
294 debugfile_switch = FALSE;
295 listing_switch = FALSE;
296 memout_switch = FALSE;
297 printprops_switch = FALSE;
298 offsets_switch = FALSE;
299 percentages_switch = FALSE;
300 obsolete_switch = FALSE;
301 transcript_switch = FALSE;
302 statistics_switch = FALSE;
303 optimise_switch = FALSE;
304 version_set_switch = FALSE;
305 nowarnings_switch = FALSE;
307 memory_map_switch = FALSE;
308 oddeven_packing_switch = FALSE;
309 define_DEBUG_switch = FALSE;
310 #ifdef USE_TEMPORARY_FILES
311 temporary_files_switch = TRUE;
313 temporary_files_switch = FALSE;
315 define_USE_MODULES_switch = FALSE;
316 module_switch = FALSE;
318 throwback_switch = FALSE;
320 runtime_error_checking_switch = TRUE;
322 define_INFIX_switch = FALSE;
324 riscos_file_type_format = 0;
326 error_format=DEFAULT_ERROR_FORMAT;
328 character_set_setting = 1; /* Default is ISO Latin-1 */
329 character_set_unicode = FALSE;
331 compression_switch = TRUE;
333 requested_glulx_version = 0;
336 /* ------------------------------------------------------------------------- */
337 /* Number of files given as command line parameters (0, 1 or 2) */
338 /* ------------------------------------------------------------------------- */
340 static int cli_files_specified,
341 convert_filename_flag;
343 char Source_Name[PATHLEN]; /* Processed name of first input file */
344 char Code_Name[PATHLEN]; /* Processed name of output file */
346 static char *cli_file1, *cli_file2; /* Unprocessed (and unsafe to alter) */
348 /* ========================================================================= */
349 /* Data structure management routines */
350 /* ------------------------------------------------------------------------- */
352 static void init_vars(void)
360 init_expressc_vars();
361 init_expressp_vars();
376 static void begin_pass(void)
382 directs_begin_pass();
384 expressc_begin_pass();
385 expressp_begin_pass();
388 endofpass_flag = FALSE;
389 line_trace_level = 0; expr_trace_level = 0;
390 asm_trace_level = asm_trace_setting;
391 linker_trace_level = linker_trace_setting;
392 if (listing_switch) line_trace_level=1;
397 objects_begin_pass();
399 symbols_begin_pass();
408 /* Compile a Main__ routine (see "veneer.c") */
410 compile_initial_routine();
412 /* Make the four metaclasses: Class must be object number 1, so
413 it must come first */
418 make_class("Object");
419 make_class("Routine");
420 make_class("String");
426 extern void allocate_arrays(void)
428 arrays_allocate_arrays();
429 asm_allocate_arrays();
430 bpatch_allocate_arrays();
431 chars_allocate_arrays();
432 directs_allocate_arrays();
433 errors_allocate_arrays();
434 expressc_allocate_arrays();
435 expressp_allocate_arrays();
436 files_allocate_arrays();
438 lexer_allocate_arrays();
439 linker_allocate_arrays();
440 memory_allocate_arrays();
441 objects_allocate_arrays();
442 states_allocate_arrays();
443 symbols_allocate_arrays();
444 syntax_allocate_arrays();
445 tables_allocate_arrays();
446 text_allocate_arrays();
447 veneer_allocate_arrays();
448 verbs_allocate_arrays();
451 extern void free_arrays(void)
453 /* One array may survive this routine, all_the_text (used to hold
454 game text until the abbreviations optimiser begins work on it): this
455 array (if it was ever allocated) is freed at the top level. */
457 arrays_free_arrays();
459 bpatch_free_arrays();
461 directs_free_arrays();
462 errors_free_arrays();
463 expressc_free_arrays();
464 expressp_free_arrays();
468 linker_free_arrays();
469 memory_free_arrays();
470 objects_free_arrays();
471 states_free_arrays();
472 symbols_free_arrays();
473 syntax_free_arrays();
474 tables_free_arrays();
476 veneer_free_arrays();
480 /* ------------------------------------------------------------------------- */
481 /* Name translation code for filenames */
482 /* ------------------------------------------------------------------------- */
484 static char Source_Path[PATHLEN];
485 static char Include_Path[PATHLEN];
486 static char Code_Path[PATHLEN];
487 static char Module_Path[PATHLEN];
488 static char Temporary_Path[PATHLEN];
489 static char current_source_path[PATHLEN];
490 char Debugging_Name[PATHLEN];
491 char Transcript_Name[PATHLEN];
492 char Language_Name[PATHLEN];
493 char Charset_Map[PATHLEN];
494 static char ICL_Path[PATHLEN];
496 /* Set one of the above Path buffers to the given location, or list of
497 locations. (A list is comma-separated, and only accepted for Source_Path,
498 Include_Path, ICL_Path, Module_Path.)
500 static void set_path_value(char *path, char *value)
505 if (i >= PATHLEN-1) {
506 printf("A specified path is longer than %d characters.\n",
510 if ((value[j] == FN_ALT) || (value[j] == 0))
511 { if ((value[j] == FN_ALT)
512 && (path != Source_Path) && (path != Include_Path)
513 && (path != ICL_Path) && (path != Module_Path))
514 { printf("The character '%c' is used to divide entries in a list \
515 of possible locations, and can only be used in the Include_Path, Source_Path, \
516 Module_Path or ICL_Path variables. Other paths are for output only.\n", FN_ALT);
519 if ((path != Debugging_Name) && (path != Transcript_Name)
520 && (path != Language_Name) && (path != Charset_Map)
521 && (i>0) && (isalnum(path[i-1]))) path[i++] = FN_SEP;
522 path[i++] = value[j++];
523 if (value[j-1] == 0) return;
525 else path[i++] = value[j++];
529 /* Prepend the given location or list of locations to one of the above
530 Path buffers. This is only permitted for Source_Path, Include_Path,
531 ICL_Path, Module_Path.
533 An empty field (in the comma-separated list) means the current
534 directory. If the Path buffer is entirely empty, we assume that
535 we want to search both value and the current directory, so
536 the result will be "value,".
538 static void prepend_path_value(char *path, char *value)
541 int oldlen = strlen(path);
543 char new_path[PATHLEN];
545 if ((path != Source_Path) && (path != Include_Path)
546 && (path != ICL_Path) && (path != Module_Path))
547 { printf("The character '+' is used to add to a list \
548 of possible locations, and can only be used in the Include_Path, Source_Path, \
549 Module_Path or ICL_Path variables. Other paths are for output only.\n");
555 if (i >= PATHLEN-1) {
556 printf("A specified path is longer than %d characters.\n",
560 if ((value[j] == FN_ALT) || (value[j] == 0))
561 { if ((path != Debugging_Name) && (path != Transcript_Name)
562 && (path != Language_Name) && (path != Charset_Map)
563 && (i>0) && (isalnum(new_path[i-1]))) new_path[i++] = FN_SEP;
564 new_path[i++] = value[j++];
565 if (value[j-1] == 0) {
570 else new_path[i++] = value[j++];
573 if (newlen+1+oldlen >= PATHLEN-1) {
574 printf("A specified path is longer than %d characters.\n",
580 new_path[i++] = FN_ALT;
582 new_path[i++] = path[j++];
585 strcpy(path, new_path);
588 static void set_default_paths(void)
590 set_path_value(Source_Path, Source_Directory);
591 set_path_value(Include_Path, Include_Directory);
592 set_path_value(Code_Path, Code_Directory);
593 set_path_value(Module_Path, Module_Directory);
594 set_path_value(ICL_Path, ICL_Directory);
595 set_path_value(Temporary_Path, Temporary_Directory);
596 set_path_value(Debugging_Name, Debugging_File);
597 set_path_value(Transcript_Name, Transcript_File);
598 set_path_value(Language_Name, "english");
599 set_path_value(Charset_Map, "");
602 /* Parse a path option which looks like "dir", "+dir", "pathname=dir",
603 or "+pathname=dir". If there is no "=", we assume "include_path=...".
604 If the option begins with a "+" the directory is prepended to the
605 existing path instead of replacing it.
607 static void set_path_command(char *command)
608 { int i, j; char *path_to_set = NULL;
611 if (command[0] == '+') {
616 for (i=0; (command[i]!=0) && (command[i]!='=');i++) ;
618 path_to_set=Include_Path;
620 if (command[i] == '=') {
621 char pathname[PATHLEN];
622 if (i>=PATHLEN) i=PATHLEN-1;
624 char ch = command[j];
625 if (isupper(ch)) ch=tolower(ch);
629 command = command+i+1;
632 if (strcmp(pathname, "source_path")==0) path_to_set=Source_Path;
633 if (strcmp(pathname, "include_path")==0) path_to_set=Include_Path;
634 if (strcmp(pathname, "code_path")==0) path_to_set=Code_Path;
635 if (strcmp(pathname, "module_path")==0) path_to_set=Module_Path;
636 if (strcmp(pathname, "icl_path")==0) path_to_set=ICL_Path;
637 if (strcmp(pathname, "temporary_path")==0) path_to_set=Temporary_Path;
638 if (strcmp(pathname, "debugging_name")==0) path_to_set=Debugging_Name;
639 if (strcmp(pathname, "transcript_name")==0) path_to_set=Transcript_Name;
640 if (strcmp(pathname, "language_name")==0) path_to_set=Language_Name;
641 if (strcmp(pathname, "charset_map")==0) path_to_set=Charset_Map;
643 if (path_to_set == NULL)
644 { printf("No such path setting as \"%s\"\n", pathname);
650 set_path_value(path_to_set, command);
652 prepend_path_value(path_to_set, command);
655 static int contains_separator(char *name)
657 for (i=0; name[i]!=0; i++)
658 if (name[i] == FN_SEP) return 1;
662 static int write_translated_name(char *new_name, char *old_name,
663 char *prefix_path, int start_pos,
666 if (strlen(old_name)+strlen(extension) >= PATHLEN) {
667 printf("One of your filenames is longer than %d characters.\n", PATHLEN);
670 if (prefix_path == NULL)
671 { sprintf(new_name,"%s%s", old_name, extension);
674 strcpy(new_name, prefix_path + start_pos);
675 for (x=0; (new_name[x]!=0) && (new_name[x]!=FN_ALT); x++) ;
676 if (new_name[x] == 0) start_pos = 0; else start_pos += x+1;
677 if (x+strlen(old_name)+strlen(extension) >= PATHLEN) {
678 printf("One of your pathnames is longer than %d characters.\n", PATHLEN);
681 sprintf(new_name + x, "%s%s", old_name, extension);
685 #ifdef FILE_EXTENSIONS
686 static char *check_extension(char *name, char *extension)
689 /* If a filename ends in '.', remove the dot and add no file extension: */
691 if (name[i] == '.') { name[i]=0; return ""; }
693 /* Remove the new extension if it's already got one: */
695 for (; (i>=0) && (name[i]!=FN_SEP); i--)
696 if (name[i] == '.') return "";
701 /* ------------------------------------------------------------------------- */
702 /* Three translation routines have to deal with path variables which may */
703 /* contain alternative locations separated by the FN_ALT character. */
704 /* These have the protocol: */
706 /* int translate_*_filename(int last_value, ...) */
708 /* and should first be called with last_value equal to 0. If the */
709 /* translated filename works, fine. Otherwise, if the returned integer */
710 /* was zero, the caller knows that no filename works and can issue an */
711 /* error message. If it was non-zero, the caller should pass it on as */
712 /* the last_value again. */
714 /* As implemented below, last_value is the position in the path variable */
715 /* string at which the next directory name to try begins. */
716 /* ------------------------------------------------------------------------- */
718 extern int translate_in_filename(int last_value,
719 char *new_name, char *old_name,
720 int same_directory_flag, int command_line_flag)
721 { char *prefix_path = NULL;
723 int add_path_flag = 1;
726 if ((same_directory_flag==0)
727 && (contains_separator(old_name)==1)) add_path_flag=0;
729 if (add_path_flag==1)
730 { if (command_line_flag == 0)
731 { /* File is opened as a result of an Include directive */
733 if (same_directory_flag==1)
734 prefix_path = current_source_path;
736 if (Include_Path[0]!=0) prefix_path = Include_Path;
738 /* Main file being opened from the command line */
740 else if (Source_Path[0]!=0) prefix_path = Source_Path;
743 #ifdef FILE_EXTENSIONS
744 /* Which file extension is expected? */
746 if ((command_line_flag==1)||(same_directory_flag==1))
747 extension = Source_Extension;
749 extension = Include_Extension;
751 extension = check_extension(old_name, extension);
756 last_value = write_translated_name(new_name, old_name,
757 prefix_path, last_value, extension);
759 /* Set the "current source path" (for use of Include ">...") */
761 if (command_line_flag==1)
762 { strcpy(current_source_path, new_name);
763 for (i=strlen(current_source_path)-1;
764 ((i>0)&&(current_source_path[i]!=FN_SEP));i--) ;
766 if (i!=0) current_source_path[i+1] = 0; /* Current file in subdir */
767 else current_source_path[0] = 0; /* Current file at root dir */
773 extern int translate_link_filename(int last_value,
774 char *new_name, char *old_name)
775 { char *prefix_path = NULL;
778 if (contains_separator(old_name)==0)
779 if (Module_Path[0]!=0)
780 prefix_path = Module_Path;
782 #ifdef FILE_EXTENSIONS
783 extension = check_extension(old_name, Module_Extension);
788 return write_translated_name(new_name, old_name,
789 prefix_path, last_value, extension);
792 static int translate_icl_filename(int last_value,
793 char *new_name, char *old_name)
794 { char *prefix_path = NULL;
795 char *extension = "";
797 if (contains_separator(old_name)==0)
799 prefix_path = ICL_Path;
801 #ifdef FILE_EXTENSIONS
802 extension = check_extension(old_name, ICL_Extension);
805 return write_translated_name(new_name, old_name,
806 prefix_path, last_value, extension);
809 extern void translate_out_filename(char *new_name, char *old_name)
811 char *extension = "";
814 /* If !convert_filename_flag, then the old_name is just the <file2>
815 parameter on the Inform command line, which we leave alone. */
817 if (!convert_filename_flag)
818 { strcpy(new_name, old_name); return;
821 /* Remove any pathname or extension in <file1>. */
823 if (contains_separator(old_name)==1)
824 { for (i=strlen(old_name)-1; (i>0)&&(old_name[i]!=FN_SEP) ;i--) { };
825 if (old_name[i]==FN_SEP) i++;
828 #ifdef FILE_EXTENSIONS
829 for (i=strlen(old_name)-1; (i>=0)&&(old_name[i]!='.') ;i--) ;
830 if (old_name[i] == '.') old_name[i] = 0;
835 { extension = Module_Extension;
836 if (Module_Path[0]!=0) prefix_path = Module_Path;
841 switch(version_number)
842 { case 3: extension = Code_Extension; break;
843 case 4: extension = V4Code_Extension; break;
844 case 5: extension = V5Code_Extension; break;
845 case 6: extension = V6Code_Extension; break;
846 case 7: extension = V7Code_Extension; break;
847 case 8: extension = V8Code_Extension; break;
851 extension = GlulxCode_Extension;
853 if (Code_Path[0]!=0) prefix_path = Code_Path;
856 #ifdef FILE_EXTENSIONS
857 extension = check_extension(old_name, extension);
860 write_translated_name(new_name, old_name, prefix_path, 0, extension);
863 static char *name_or_unset(char *p)
864 { if (p[0]==0) return "(unset)";
868 static void help_on_filenames(void)
869 { char old_name[PATHLEN];
870 char new_name[PATHLEN];
871 int save_mm = module_switch, x;
873 module_switch = FALSE;
875 printf("Help information on filenames:\n\n");
878 "The command line can take one of two forms:\n\n\
879 inform [commands...] <file1>\n\
880 inform [commands...] <file1> <file2>\n\n\
881 Inform translates <file1> into a source file name (see below) for its input.\n\
882 <file2> is usually omitted: if so, the output filename is made from <file1>\n\
883 by cutting out the name part and translating that (see below).\n\
884 If <file2> is given, however, the output filename is set to just <file2>\n\
885 (not altered in any way).\n\n");
888 "Filenames given in the game source (with commands like Include \"name\" and\n\
889 Link \"name\") are also translated by the rules below.\n\n");
892 "Rules of translation:\n\n\
893 Inform translates plain filenames (such as \"xyzzy\") into full pathnames\n\
894 (such as \"adventure%cgames%cxyzzy\") according to the following rules.\n\n\
895 1. If the name contains a '%c' character (so it's already a pathname), it\n\
896 isn't changed.\n\n", FN_SEP, FN_SEP, FN_SEP);
899 " [Exception: when the name is given in an Include command using the >\n\
900 form (such as Include \">prologue\"), the \">\" is replaced by the path\n\
901 of the file doing the inclusion");
902 #ifdef FILE_EXTENSIONS
903 printf(" and a suitable file extension is added");
908 " Filenames must never contain double-quotation marks \". To use filenames\n\
909 which contain spaces, write them in double-quotes: for instance,\n\n\
910 \"inform +code_path=\"Jigsaw Final Version\" jigsaw\".\n\n");
913 "2. The file is looked for at a particular \"path\" (the filename of a\n\
914 directory), depending on what kind of file it is.\n\n\
915 File type Name Current setting\n\n\
916 Source code (in) source_path %s\n\
917 Include file (in) include_path %s\n\
918 Story file (out) code_path %s\n",
919 name_or_unset(Source_Path), name_or_unset(Include_Path),
920 name_or_unset(Code_Path));
923 " Temporary file (out) temporary_path %s\n\
924 ICL command file (in) icl_path %s\n\
925 Module (in & out) module_path %s\n\n",
926 name_or_unset(Temporary_Path),
927 name_or_unset(ICL_Path), name_or_unset(Module_Path));
930 " If the path is unset, then the current working directory is used (so\n\
931 the filename doesn't change): if, for instance, include_path is set to\n\
932 \"backup%coldlib\" then when \"parser\" is included it is looked for at\n\
933 \"backup%coldlib%cparser\".\n\n\
934 The paths can be set or unset on the Inform command line by, eg,\n\
935 \"inform +code_path=finished jigsaw\" or\n\
936 \"inform +include_path= balances\" (which unsets include_path).\n\n",
937 FN_SEP, FN_SEP, FN_SEP);
940 " The four input path variables can be set to lists of alternative paths\n\
941 separated by '%c' characters: these alternatives are always tried in\n\
942 the order they are specified in, that is, left to right through the text\n\
943 in the path variable.\n\n",
946 " If two '+' signs are used (\"inform ++include_path=dir jigsaw\") then\n\
947 the path or paths are added to the existing list.\n\n");
949 " (Modules are written to the first alternative in the module_path list;\n\
950 it is an error to give alternatives at all for purely output paths.)\n\n");
952 #ifdef FILE_EXTENSIONS
953 printf("3. The following file extensions are added:\n\n\
956 Story files: %s (Version 3), %s (v4), %s (v5, the default),\n\
957 %s (v6), %s (v7), %s (v8), %s (Glulx)\n\
958 Temporary files: .tmp\n\
960 Source_Extension, Include_Extension,
961 Code_Extension, V4Code_Extension, V5Code_Extension, V6Code_Extension,
962 V7Code_Extension, V8Code_Extension, GlulxCode_Extension,
965 except that any extension you give (on the command line or in a filename\n\
966 used in a program) will override these. If you give the null extension\n\
967 \".\" then Inform uses no file extension at all (removing the \".\").\n\n");
970 printf("Names of four individual files can also be set using the same\n\
971 + command notation (though they aren't really pathnames). These are:\n\n\
972 transcript_name (text written by -r switch): now \"%s\"\n\
973 debugging_name (data written by -k switch): now \"%s\"\n\
974 language_name (library file defining natural language of game):\n\
976 charset_map (file for character set mapping): now \"%s\"\n\n",
977 Transcript_Name, Debugging_Name, Language_Name, Charset_Map);
979 translate_in_filename(0, new_name, "rezrov", 0, 1);
980 printf("Examples: 1. \"inform rezrov\"\n\
981 the source code is read from \"%s\"\n",
983 convert_filename_flag = TRUE;
984 translate_out_filename(new_name, "rezrov");
985 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
987 translate_in_filename(0, new_name, "frotz", 0, 1);
988 printf("2. \"inform -M frotz\"\n\
989 the source code is read from \"%s\"\n",
991 module_switch = TRUE;
992 convert_filename_flag = TRUE;
993 translate_out_filename(new_name, "frotz");
994 printf(" and a module is compiled to \"%s\".\n\n", new_name);
996 module_switch = FALSE;
998 sprintf(old_name, "demos%cplugh", FN_SEP);
999 printf("3. \"inform %s\"\n", old_name);
1000 translate_in_filename(0, new_name, old_name, 0, 1);
1001 printf(" the source code is read from \"%s\"\n", new_name);
1002 sprintf(old_name, "demos%cplugh", FN_SEP);
1003 convert_filename_flag = TRUE;
1004 translate_out_filename(new_name, old_name);
1005 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1007 printf("4. \"inform plover my_demo\"\n");
1008 translate_in_filename(0, new_name, "plover", 0, 1);
1009 printf(" the source code is read from \"%s\"\n", new_name);
1010 convert_filename_flag = FALSE;
1011 translate_out_filename(new_name, "my_demo");
1012 printf(" and a story file is compiled to \"%s\".\n\n", new_name);
1014 strcpy(old_name, Source_Path);
1015 sprintf(new_name, "%cnew%cold%crecent%cold%cancient",
1016 FN_ALT, FN_ALT, FN_SEP, FN_ALT, FN_SEP);
1017 printf("5. \"inform +source_path=%s zooge\"\n", new_name);
1019 " Note that four alternative paths are given, the first being the empty\n\
1020 path-name (meaning: where you are now). Inform looks for the source code\n\
1021 by trying these four places in turn, stopping when it finds anything:\n\n");
1023 set_path_value(Source_Path, new_name);
1026 { x = translate_in_filename(x, new_name, "zooge", 0, 1);
1027 printf(" \"%s\"\n", new_name);
1029 strcpy(Source_Path, old_name);
1030 module_switch = save_mm;
1033 /* ------------------------------------------------------------------------- */
1034 /* Naming temporary files */
1035 /* (Arguably temporary files should be made using "tmpfile" in */
1036 /* the ANSI C library, but many supposed ANSI libraries lack it.) */
1037 /* ------------------------------------------------------------------------- */
1039 extern void translate_temp_filename(int i)
1042 { case 1: p=Temp1_Name; break;
1043 case 2: p=Temp2_Name; break;
1044 case 3: p=Temp3_Name; break;
1046 if (strlen(Temporary_Path)+strlen(Temporary_File)+6 >= PATHLEN) {
1047 printf ("Temporary_Path is too long.\n");
1050 sprintf(p,"%s%s%d", Temporary_Path, Temporary_File, i);
1051 #ifdef INCLUDE_TASK_ID
1052 sprintf(p+strlen(p), "_proc%08lx", (long int) unique_task_id());
1054 #ifdef FILE_EXTENSIONS
1055 sprintf(p+strlen(p), ".tmp");
1060 static char riscos_ft_buffer[4];
1062 extern char *riscos_file_type(void)
1064 if (riscos_file_type_format == 1)
1065 { if (module_switch) return("data");
1069 if (module_switch) return("075");
1071 sprintf(riscos_ft_buffer, "%03x", 0x60 + version_number);
1072 return(riscos_ft_buffer);
1076 /* ------------------------------------------------------------------------- */
1077 /* The compilation pass */
1078 /* ------------------------------------------------------------------------- */
1080 static void run_pass(void)
1082 lexer_begin_prepass();
1083 files_begin_prepass();
1084 load_sourcefile(Source_Name, 0);
1088 parse_program(NULL);
1091 issue_unused_warnings();
1095 if (module_switch) linker_endpass();
1098 if (hash_switch && hash_printed_since_newline) printf("\n");
1100 if (temporary_files_switch)
1101 { if (module_switch) flush_link_data();
1105 if (track_unused_routines)
1106 locate_dead_functions();
1107 construct_storyfile();
1110 int output_has_occurred;
1112 static void rennab(int32 time_taken)
1113 { /* rennab = reverse of banner */
1115 int t = no_warnings + no_suppressed_warnings;
1117 if (memout_switch) print_memory_usage();
1119 if ((no_errors + t)!=0)
1120 { printf("Compiled with ");
1122 { printf("%d error%s", no_errors,(no_errors==1)?"":"s");
1123 if (t > 0) printf(" and ");
1125 if (no_warnings > 0)
1126 printf("%d warning%s", t, (t==1)?"":"s");
1127 if (no_suppressed_warnings > 0)
1128 { if (no_warnings > 0)
1129 printf(" (%d suppressed)", no_suppressed_warnings);
1131 printf("%d suppressed warning%s", no_suppressed_warnings,
1132 (no_suppressed_warnings==1)?"":"s");
1134 if (output_has_occurred == FALSE) printf(" (no output)");
1138 if (no_compiler_errors > 0) print_sorry_message();
1140 if (statistics_switch)
1141 printf("Completed in %ld seconds\n", (long int) time_taken);
1144 /* ------------------------------------------------------------------------- */
1145 /* The compiler abstracted to a routine. */
1146 /* ------------------------------------------------------------------------- */
1148 static int execute_icl_header(char *file1);
1150 static int compile(int number_of_files_specified, char *file1, char *file2)
1153 if (execute_icl_header(file1))
1156 select_target(glulx_mode);
1158 if (define_INFIX_switch && glulx_mode) {
1159 printf("Infix (-X) facilities are not available in Glulx: \
1160 disabling -X switch\n");
1161 define_INFIX_switch = FALSE;
1164 if (module_switch && glulx_mode) {
1165 printf("Modules are not available in Glulx: \
1166 disabling -M switch\n");
1167 module_switch = FALSE;
1170 if (define_INFIX_switch && module_switch)
1171 { printf("Infix (-X) facilities are not available when compiling \
1172 modules: disabling -X switch\n");
1173 define_INFIX_switch = FALSE;
1175 if (runtime_error_checking_switch && module_switch)
1176 { printf("Strict checking (-S) facilities are not available when \
1177 compiling modules: disabling -S switch\n");
1178 runtime_error_checking_switch = FALSE;
1181 time_start=time(0); no_compilations++;
1183 strcpy(Source_Name, file1); convert_filename_flag = TRUE;
1184 strcpy(Code_Name, file1);
1185 if (number_of_files_specified == 2)
1186 { strcpy(Code_Name, file2); convert_filename_flag = FALSE;
1191 if (debugfile_switch) begin_debug_file();
1195 if (transcript_switch) open_transcript_file(Source_Name);
1199 if (transcript_switch)
1200 { write_dictionary_to_transcript();
1201 close_transcript_file();
1204 if (no_errors==0) { output_file(); output_has_occurred = TRUE; }
1205 else { output_has_occurred = FALSE; }
1207 if (debugfile_switch)
1211 if (temporary_files_switch && (no_errors>0)) remove_temp_files();
1215 rennab((int32) (time(0)-time_start));
1217 if (optimise_switch) optimise_abbreviations();
1219 if (store_the_text) my_free(&all_text,"transcription text");
1221 return (no_errors==0)?0:1;
1224 /* ------------------------------------------------------------------------- */
1225 /* The command line interpreter */
1226 /* ------------------------------------------------------------------------- */
1228 static void cli_print_help(int help_level)
1231 "\nThis program is a compiler of Infocom format (also called \"Z-machine\")\n\
1232 story files: copyright (c) Graham Nelson 1993 - 2016.\n\n");
1234 /* For people typing just "inform", a summary only: */
1239 #ifndef PROMPT_INPUT
1240 printf("Usage: \"inform [commands...] <file1> [<file2>]\"\n\n");
1242 printf("When run, Inform prompts you for commands (and switches),\n\
1243 which are optional, then an input <file1> and an (optional) output\n\
1248 "<file1> is the Inform source file of the game to be compiled. <file2>,\n\
1249 if given, overrides the filename Inform would normally use for the\n\
1250 compiled output. Try \"inform -h1\" for file-naming conventions.\n\n\
1251 One or more words can be supplied as \"commands\". These may be:\n\n\
1252 -switches a list of compiler switches, 1 or 2 letter\n\
1253 (see \"inform -h2\" for the full range)\n\n\
1254 +dir set Include_Path to this directory\n\
1255 +PATH=dir change the PATH to this directory\n\n\
1256 $... one of the following memory commands:\n");
1258 " $list list current memory allocation settings\n\
1259 $huge make standard \"huge game\" settings %s\n\
1260 $large make standard \"large game\" settings %s\n\
1261 $small make standard \"small game\" settings %s\n\
1262 $?SETTING explain briefly what SETTING is for\n\
1263 $SETTING=number change SETTING to given number\n\n\
1264 (filename) read in a list of commands (in the format above)\n\
1265 from this \"setup file\"\n\n",
1266 (DEFAULT_MEMORY_SIZE==HUGE_SIZE)?"(default)":"",
1267 (DEFAULT_MEMORY_SIZE==LARGE_SIZE)?"(default)":"",
1268 (DEFAULT_MEMORY_SIZE==SMALL_SIZE)?"(default)":"");
1270 #ifndef PROMPT_INPUT
1271 printf("For example: \"inform -dexs $huge curses\".\n\n");
1277 /* The -h1 (filenaming) help information: */
1279 if (help_level == 1) { help_on_filenames(); return; }
1281 /* The -h2 (switches) help information: */
1283 printf("Help on the full list of legal switch commands:\n\n\
1284 a trace assembly-language (without hex dumps; see -t)\n\
1285 c more concise error messages\n\
1286 d contract double spaces after full stops in text\n\
1287 d2 contract double spaces after exclamation and question marks, too\n\
1288 e economy mode (slower): make use of declared abbreviations\n");
1291 f frequencies mode: show how useful abbreviations are\n\
1292 g traces calls to functions (except in the library)\n\
1293 g2 traces calls to all functions\n\
1294 h print this information\n");
1297 i ignore default switches set within the file\n\
1298 j list objects as constructed\n\
1299 k output Infix debugging information to \"%s\" (and switch -D on)\n\
1300 l list every statement run through Inform\n\
1301 m say how much memory has been allocated\n\
1302 n print numbers of properties, attributes and actions\n",
1305 o print offset addresses\n\
1306 p give percentage breakdown of story file\n\
1307 q keep quiet about obsolete usages\n\
1308 r record all the text to \"%s\"\n\
1309 s give statistics\n\
1310 t trace assembly-language (with full hex dumps; see -a)\n",
1314 u work out most useful abbreviations (very very slowly)\n\
1315 v3 compile to version-3 (\"Standard\") story file\n\
1316 v4 compile to version-4 (\"Plus\") story file\n\
1317 v5 compile to version-5 (\"Advanced\") story file: the default\n\
1318 v6 compile to version-6 (graphical) story file\n\
1319 v8 compile to version-8 (expanded \"Advanced\") story file\n\
1320 w disable warning messages\n\
1321 x print # for every 100 lines compiled\n\
1322 y trace linking system\n\
1323 z print memory map of the Z-machine\n\n");
1326 B use big memory model (for large V6/V7 files)\n\
1327 C0 text character set is plain ASCII only\n\
1328 Cu text character set is UTF-8\n\
1329 Cn text character set is ISO 8859-n (n = 1 to 9)\n\
1330 (1 to 4, Latin1 to Latin4; 5, Cyrillic; 6, Arabic;\n\
1331 7, Greek; 8, Hebrew; 9, Latin5. Default is -C1.)\n");
1332 printf(" D insert \"Constant DEBUG;\" automatically\n");
1333 printf(" E0 Archimedes-style error messages%s\n",
1334 (error_format==0)?" (current setting)":"");
1335 printf(" E1 Microsoft-style error messages%s\n",
1336 (error_format==1)?" (current setting)":"");
1337 printf(" E2 Macintosh MPW-style error messages%s\n",
1338 (error_format==2)?" (current setting)":"");
1339 #ifdef USE_TEMPORARY_FILES
1340 printf(" F0 use extra memory rather than temporary files\n");
1342 printf(" F1 use temporary files to reduce memory consumption\n");
1344 printf(" G compile a Glulx game file\n");
1345 printf(" H use Huffman encoding to compress Glulx strings\n");
1346 printf(" M compile as a Module for future linking\n");
1350 R0 use filetype 060 + version number for games (default)\n\
1351 R1 use official Acorn filetype 11A for all games\n");
1353 printf(" S compile strict error-checking at run-time (on by default)\n");
1354 #ifdef ARC_THROWBACK
1355 printf(" T enable throwback of errors in the DDE\n");
1357 printf(" U insert \"Constant USE_MODULES;\" automatically\n");
1358 printf(" V print the version and date of this program\n");
1359 printf(" Wn header extension table is at least n words (n = 3 to 99)\n");
1360 printf(" X compile with INFIX debugging facilities present\n");
1364 extern void switches(char *p, int cmode)
1365 { int i, s=1, state;
1366 /* Here cmode is 0 if switches list is from a "Switches" directive
1367 and 1 if from a "-switches" command-line or ICL list */
1372 "Ignoring second word which should be a -list of switches.\n");
1376 for (i=cmode; p[i]!=0; i+=s, s=1)
1384 case 'a': asm_trace_setting = 1; break;
1385 case 'b': bothpasses_switch = state; break;
1386 case 'c': concise_switch = state; break;
1387 case 'd': switch(p[i+1])
1388 { case '1': double_space_setting=1; s=2; break;
1389 case '2': double_space_setting=2; s=2; break;
1390 default: double_space_setting=1; break;
1393 case 'e': economy_switch = state; break;
1394 case 'f': frequencies_switch = state; break;
1395 case 'g': switch(p[i+1])
1396 { case '1': trace_fns_setting=1; s=2; break;
1397 case '2': trace_fns_setting=2; s=2; break;
1398 default: trace_fns_setting=1; break;
1401 case 'h': switch(p[i+1])
1402 { case '1': cli_print_help(1); s=2; break;
1403 case '2': cli_print_help(2); s=2; break;
1405 default: cli_print_help(0); break;
1408 case 'i': ignore_switches_switch = state; break;
1409 case 'j': listobjects_switch = state; break;
1410 case 'k': if (cmode == 0)
1411 error("The switch '-k' can't be set with 'Switches'");
1413 { debugfile_switch = state;
1414 if (state) define_DEBUG_switch = TRUE;
1417 case 'l': listing_switch = state; break;
1418 case 'm': memout_switch = state; break;
1419 case 'n': printprops_switch = state; break;
1420 case 'o': offsets_switch = state; break;
1421 case 'p': percentages_switch = state; break;
1422 case 'q': obsolete_switch = state; break;
1423 case 'r': if (cmode == 0)
1424 error("The switch '-r' can't be set with 'Switches'");
1426 transcript_switch = state; break;
1427 case 's': statistics_switch = state; break;
1428 case 't': asm_trace_setting=2; break;
1429 case 'u': if (cmode == 0) {
1430 error("The switch '-u' can't be set with 'Switches'");
1433 optimise_switch = state; break;
1434 case 'v': if (glulx_mode) { s = select_glulx_version(p+i+1)+1; break; }
1435 if ((cmode==0) && (version_set_switch)) { s=2; break; }
1436 version_set_switch = TRUE; s=2;
1438 { case '3': select_version(3); break;
1439 case '4': select_version(4); break;
1440 case '5': select_version(5); break;
1441 case '6': select_version(6); break;
1442 case '7': select_version(7); break;
1443 case '8': select_version(8); break;
1444 default: printf("-v must be followed by 3 to 8\n");
1445 version_set_switch=0; s=1;
1448 if ((version_number < 5) && (r_e_c_s_set == FALSE))
1449 runtime_error_checking_switch = FALSE;
1451 case 'w': nowarnings_switch = state; break;
1452 case 'x': hash_switch = state; break;
1453 case 'y': s=2; linker_trace_setting=p[i+1]-'0'; break;
1454 case 'z': memory_map_switch = state; break;
1455 case 'B': oddeven_packing_switch = state; break;
1457 if (p[i+1] == 'u') {
1458 character_set_unicode = TRUE;
1459 /* Leave the set_setting on Latin-1, because that
1460 matches the first block of Unicode. */
1461 character_set_setting = 1;
1464 { character_set_setting=p[i+1]-'0';
1465 if ((character_set_setting < 0)
1466 || (character_set_setting > 9))
1467 { printf("-C must be followed by 'u' or 0 to 9. Defaulting to ISO-8859-1.\n");
1468 character_set_unicode = FALSE;
1469 character_set_setting = 1;
1472 if (cmode == 0) change_character_set();
1474 case 'D': define_DEBUG_switch = state; break;
1475 case 'E': switch(p[i+1])
1476 { case '0': s=2; error_format=0; break;
1477 case '1': s=2; error_format=1; break;
1478 case '2': s=2; error_format=2; break;
1479 default: error_format=1; break;
1482 case 'F': if (cmode == 0) {
1483 error("The switch '-F' can't be set with 'Switches'");
1487 { case '0': s=2; temporary_files_switch = FALSE; break;
1488 case '1': s=2; temporary_files_switch = TRUE; break;
1489 default: temporary_files_switch = state; break;
1492 case 'M': module_switch = state;
1493 if (state && (r_e_c_s_set == FALSE))
1494 runtime_error_checking_switch = FALSE;
1497 case 'R': switch(p[i+1])
1498 { case '0': s=2; riscos_file_type_format=0; break;
1499 case '1': s=2; riscos_file_type_format=1; break;
1500 default: riscos_file_type_format=1; break;
1504 #ifdef ARC_THROWBACK
1505 case 'T': throwback_switch = state; break;
1507 case 'S': runtime_error_checking_switch = state;
1508 r_e_c_s_set = TRUE; break;
1509 case 'G': if (cmode == 0)
1510 error("The switch '-G' can't be set with 'Switches'");
1512 { glulx_mode = state;
1513 adjust_memory_sizes();
1516 case 'H': compression_switch = state; break;
1517 case 'U': define_USE_MODULES_switch = state; break;
1518 case 'V': exit(0); break;
1519 case 'W': if ((p[i+1]>='0') && (p[i+1]<='9'))
1520 { s=2; ZCODE_HEADER_EXT_WORDS = p[i+1]-'0';
1521 if ((p[i+2]>='0') && (p[i+2]<='9'))
1522 { s=3; ZCODE_HEADER_EXT_WORDS *= 10;
1523 ZCODE_HEADER_EXT_WORDS += p[i+2]-'0';
1527 case 'X': define_INFIX_switch = state; break;
1529 printf("Switch \"-%c\" unknown (try \"inform -h2\" for the list)\n",
1535 if (optimise_switch && (!store_the_text))
1536 { store_the_text=TRUE;
1539 printf("Allocation %ld bytes for transcription text\n",
1540 (long) MAX_TRANSCRIPT_SIZE);
1541 all_text = halloc(MAX_TRANSCRIPT_SIZE,1);
1542 malloced_bytes += MAX_TRANSCRIPT_SIZE;
1544 fatalerror("Can't hallocate memory for transcription text. Darn.");
1546 all_text=my_malloc(MAX_TRANSCRIPT_SIZE,"transcription text");
1551 static int icl_command(char *p)
1552 { if ((p[0]=='+')||(p[0]=='-')||(p[0]=='$')
1553 || ((p[0]=='(')&&(p[strlen(p)-1]==')')) ) return TRUE;
1557 static void icl_error(char *filename, int line)
1558 { printf("Error in ICL file '%s', line %d:\n", filename, line);
1561 static void icl_header_error(char *filename, int line)
1562 { printf("Error in ICL header of file '%s', line %d:\n", filename, line);
1565 static int copy_icl_word(char *from, char *to, int max)
1567 /* Copies one token from 'from' to 'to', null-terminated:
1568 returns the number of chars in 'from' read past (possibly 0). */
1570 int i, j, quoted_mode, truncated;
1572 i = 0; truncated = 0;
1573 while ((from[i] == ' ') || (from[i] == TAB_CHARACTER)
1574 || (from[i] == (char) 10) || (from[i] == (char) 13)) i++;
1577 { while (from[i] != 0) i++;
1578 to[0] = 0; return i;
1581 for (quoted_mode = FALSE, j=0;;)
1582 { if (from[i] == 0) break;
1583 if (from[i] == 10) break;
1584 if (from[i] == 13) break;
1585 if (from[i] == TAB_CHARACTER) break;
1586 if ((from[i] == ' ') && (!quoted_mode)) break;
1587 if (from[i] == '\"') { quoted_mode = !quoted_mode; i++; }
1588 else to[j++] = from[i++];
1596 printf("The following parameter has been truncated:\n%s\n", to);
1600 static void execute_icl_command(char *p);
1602 static int execute_icl_header(char *argname)
1605 char cli_buff[256], fw[256];
1609 char filename[PATHLEN];
1613 { x = translate_in_filename(x, filename, argname, 0, 1);
1614 command_file = fopen(filename,"r");
1615 } while ((command_file == NULL) && (x != 0));
1616 if (!command_file) {
1617 /* Fail silently. The regular compiler will try to open the file
1618 again, and report the problem. */
1622 while (feof(command_file)==0) {
1623 if (fgets(cli_buff,256,command_file)==0) break;
1625 if (!(cli_buff[0] == '!' && cli_buff[1] == '%'))
1627 i = copy_icl_word(cli_buff+2, fw, 256);
1628 if (icl_command(fw)) {
1629 execute_icl_command(fw);
1630 copy_icl_word(cli_buff+2 + i, fw, 256);
1631 if ((fw[0] != 0) && (fw[0] != '!')) {
1632 icl_header_error(filename, line);
1634 printf("expected comment or nothing but found '%s'\n", fw);
1639 icl_header_error(filename, line);
1641 printf("Expected command or comment but found '%s'\n", fw);
1645 fclose(command_file);
1647 return (errcount==0)?0:1;
1651 static void run_icl_file(char *filename, FILE *command_file)
1652 { char cli_buff[256], fw[256];
1654 printf("[Running ICL file '%s']\n", filename);
1656 while (feof(command_file)==0)
1657 { if (fgets(cli_buff,256,command_file)==0) break;
1659 i = copy_icl_word(cli_buff, fw, 256);
1660 if (icl_command(fw))
1661 { execute_icl_command(fw);
1662 copy_icl_word(cli_buff + i, fw, 256);
1663 if ((fw[0] != 0) && (fw[0] != '!'))
1664 { icl_error(filename, line);
1665 printf("expected comment or nothing but found '%s'\n", fw);
1669 { if (strcmp(fw, "compile")==0)
1670 { char story_name[PATHLEN], code_name[PATHLEN];
1671 i += copy_icl_word(cli_buff + i, story_name, PATHLEN);
1672 i += copy_icl_word(cli_buff + i, code_name, PATHLEN);
1674 if (code_name[0] != 0) x=2;
1675 else if (story_name[0] != 0) x=1;
1679 { case 0: icl_error(filename, line);
1680 printf("No filename given to 'compile'\n");
1682 case 1: printf("[Compiling <%s>]\n", story_name);
1683 compile(x, story_name, code_name);
1685 case 2: printf("[Compiling <%s> to <%s>]\n",
1686 story_name, code_name);
1687 compile(x, story_name, code_name);
1688 copy_icl_word(cli_buff + i, fw, 256);
1690 { icl_error(filename, line);
1691 printf("Expected comment or nothing but found '%s'\n",
1699 { icl_error(filename, line);
1700 printf("Expected command or comment but found '%s'\n", fw);
1706 static void execute_icl_command(char *p)
1707 { char filename[PATHLEN], cli_buff[256];
1711 { case '+': set_path_command(p+1); break;
1712 case '-': switches(p,1); break;
1713 case '$': memory_command(p+1); break;
1714 case '(': strcpy(cli_buff,p+1); cli_buff[strlen(cli_buff)-1]=0;
1717 { x = translate_icl_filename(x, filename, cli_buff);
1718 command_file = fopen(filename,"r");
1719 } while ((command_file == NULL) && (x != 0));
1721 if (command_file == NULL)
1722 printf("Error in ICL: Couldn't open command file '%s'\n",
1725 { run_icl_file(filename, command_file);
1726 fclose(command_file);
1732 /* ------------------------------------------------------------------------- */
1733 /* Opening and closing banners */
1734 /* ------------------------------------------------------------------------- */
1736 char banner_line[80];
1738 static void banner(void)
1740 sprintf(banner_line, "Inform %d.%d%d",
1741 (VNUMBER/100)%10, (VNUMBER/10)%10, VNUMBER%10);
1742 #ifdef RELEASE_SUFFIX
1743 strcat(banner_line, RELEASE_SUFFIX);
1745 #ifdef MACHINE_STRING
1746 sprintf(banner_line+strlen(banner_line), " for %s", MACHINE_STRING);
1748 sprintf(banner_line+strlen(banner_line), " (%s)", RELEASE_DATE);
1749 printf("%s\n", banner_line);
1752 /* ------------------------------------------------------------------------- */
1753 /* Input from the outside world */
1754 /* ------------------------------------------------------------------------- */
1757 static void read_command_line(int argc, char **argv)
1759 char buffer1[PATHLEN], buffer2[PATHLEN], buffer3[PATHLEN];
1761 printf("Source filename?\n> ");
1762 while (gets(buffer1)==NULL); cli_file1=buffer1;
1763 printf("Output filename (RETURN for the same)?\n> ");
1764 while (gets(buffer2)==NULL); cli_file2=buffer2;
1765 cli_files_specified=1;
1766 if (buffer2[0]!=0) cli_files_specified=2;
1768 { printf("List of commands (RETURN to finish; \"-h\" for help)?\n> ");
1769 while (gets(buffer3)==NULL); execute_icl_command(buffer3);
1770 } while (buffer3[0]!=0);
1773 static void read_command_line(int argc, char **argv)
1775 if (argc==1) switches("-h",1);
1777 for (i=1, cli_files_specified=0; i<argc; i++)
1778 if (icl_command(argv[i]))
1779 execute_icl_command(argv[i]);
1781 switch(++cli_files_specified)
1782 { case 1: cli_file1 = argv[i]; break;
1783 case 2: cli_file2 = argv[i]; break;
1785 printf("Command line error: unknown parameter '%s'\n",
1791 /* ------------------------------------------------------------------------- */
1792 /* M A I N : An outer shell for machine-specific quirks */
1793 /* Omitted altogether if EXTERNAL_SHELL is defined, as for instance is */
1794 /* needed for the Macintosh front end. */
1795 /* ------------------------------------------------------------------------- */
1797 #ifdef EXTERNAL_SHELL
1798 extern int sub_main(int argc, char **argv);
1801 static int sub_main(int argc, char **argv);
1803 int main(int argc, char **argv, char *envp[])
1805 int main(int argc, char **argv)
1809 InitCursorCtl((acurHandle)NULL); Show_Cursor(WATCH_CURSOR);
1811 rcode = sub_main(argc, argv);
1812 #ifdef ARC_THROWBACK
1820 /* ------------------------------------------------------------------------- */
1821 /* M A I N II: Starting up ICL with the command line */
1822 /* ------------------------------------------------------------------------- */
1824 #ifdef EXTERNAL_SHELL
1825 extern int sub_main(int argc, char **argv)
1827 static int sub_main(int argc, char **argv)
1832 ProcessEvents (&g_proc);
1835 if (store_the_text) my_free(&all_text,"transcription text");
1836 longjmp (g_fallback, 1);
1842 set_memory_sizes(DEFAULT_MEMORY_SIZE); set_default_paths();
1843 reset_switch_settings(); select_version(5);
1845 cli_files_specified = 0; no_compilations = 0;
1846 cli_file1 = "source"; cli_file2 = "output";
1848 read_command_line(argc, argv);
1850 if (cli_files_specified > 0)
1851 { return_code = compile(cli_files_specified, cli_file1, cli_file2);
1853 if (return_code != 0) return(return_code);
1856 if (no_compilations == 0)
1857 printf("\n[No compilation requested]\n");
1858 if (no_compilations > 1)
1859 printf("[%d compilations completed]\n", no_compilations);
1864 /* ========================================================================= */