78d06efe1fe9d2cf9fcaa152d5a407cb3fc478f0
[inform.git] / src / memory.c
1 /* ------------------------------------------------------------------------- */
2 /*   "memory" : Memory management and ICL memory setting commands            */
3 /*                                                                           */
4 /*   Part of Inform 6.41                                                     */
5 /*   copyright (c) Graham Nelson 1993 - 2022                                 */
6 /*                                                                           */
7 /* Inform is free software: you can redistribute it and/or modify            */
8 /* it under the terms of the GNU General Public License as published by      */
9 /* the Free Software Foundation, either version 3 of the License, or         */
10 /* (at your option) any later version.                                       */
11 /*                                                                           */
12 /* Inform is distributed in the hope that it will be useful,                 */
13 /* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
14 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
15 /* GNU General Public License for more details.                              */
16 /*                                                                           */
17 /* You should have received a copy of the GNU General Public License         */
18 /* along with Inform. If not, see https://gnu.org/licenses/                  */
19 /*                                                                           */
20 /* ------------------------------------------------------------------------- */
21
22 #include "header.h"
23
24 size_t malloced_bytes=0;               /* Total amount of memory allocated   */
25
26 /* Wrappers for malloc(), realloc(), etc.
27
28    Note that all of these functions call memory_out_error() on failure.
29    This is a fatal error and does not return. However, we check my_malloc()
30    return values anyway as a matter of good habit.
31  */
32
33 #ifdef PC_QUICKC
34
35 extern void *my_malloc(size_t size, char *whatfor)
36 {   char _huge *c;
37     if (memout_switch)
38         printf("Allocating %ld bytes for %s\n",size,whatfor);
39     if (size==0) return(NULL);
40     c=(char _huge *)halloc(size,1);
41     malloced_bytes+=size;
42     if (c==0) memory_out_error(size, 1, whatfor);
43     return(c);
44 }
45
46 extern void my_realloc(void *pointer, size_t oldsize, size_t size, 
47     char *whatfor)
48 {   char _huge *c;
49     if (size==0) {
50         my_free(pointer, whatfor);
51         return;
52     }
53     c=halloc(size,1);
54     malloced_bytes+=(size-oldsize);
55     if (c==0) memory_out_error(size, 1, whatfor);
56     if (memout_switch)
57         printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n",
58             (long int) oldsize, (long int) size, whatfor,
59             (long int) (*(int **)pointer), 
60             (long int) c);
61     memcpy(c, *(int **)pointer, MIN(oldsize, size));
62     hfree(*(int **)pointer);
63     *(int **)pointer = c;
64 }
65
66 extern void *my_calloc(size_t size, size_t howmany, char *whatfor)
67 {   void _huge *c;
68     if (memout_switch)
69         printf("Allocating %d bytes: array (%ld entries size %ld) for %s\n",
70             size*howmany,howmany,size,whatfor);
71     if ((size*howmany) == 0) return(NULL);
72     c=(void _huge *)halloc(howmany*size,1);
73     malloced_bytes+=size*howmany;
74     if (c==0) memory_out_error(size, howmany, whatfor);
75     return(c);
76 }
77
78 extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, 
79     int32 howmany, char *whatfor)
80 {   void _huge *c;
81     if (size*howmany==0) {
82         my_free(pointer, whatfor);
83         return;
84     }
85     c=(void _huge *)halloc(size*howmany,1);
86     malloced_bytes+=size*(howmany-oldhowmany);
87     if (c==0) memory_out_error(size, howmany, whatfor);
88     if (memout_switch)
89         printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n",
90             ((long int)size) * ((long int)oldhowmany),
91             ((long int)size) * ((long int)howmany),
92             (long int)howmany, (long int)size, whatfor,
93             (long int) *(int **)pointer, (long int) c);
94     memcpy(c, *(int **)pointer, MIN(size*oldhowmany, size*howmany));
95     hfree(*(int **)pointer);
96     *(int **)pointer = c;
97 }
98
99 #else
100
101 extern void *my_malloc(size_t size, char *whatfor)
102 {   char *c;
103     if (size==0) return(NULL);
104     c=malloc(size);
105     malloced_bytes+=size;
106     if (c==0) memory_out_error(size, 1, whatfor);
107     if (memout_switch)
108         printf("Allocating %ld bytes for %s at (%08lx)\n",
109             (long int) size,whatfor,(long int) c);
110     return(c);
111 }
112
113 extern void my_realloc(void *pointer, size_t oldsize, size_t size, 
114     char *whatfor)
115 {   void *c;
116     if (size==0) {
117         my_free(pointer, whatfor);
118         return;
119     }
120     c=realloc(*(int **)pointer,  size);
121     malloced_bytes+=(size-oldsize);
122     if (c==0) memory_out_error(size, 1, whatfor);
123     if (memout_switch)
124         printf("Increasing allocation from %ld to %ld bytes for %s was (%08lx) now (%08lx)\n",
125             (long int) oldsize, (long int) size, whatfor,
126             (long int) (*(int **)pointer), 
127             (long int) c);
128     *(int **)pointer = c;
129 }
130
131 extern void *my_calloc(size_t size, size_t howmany, char *whatfor)
132 {   void *c;
133     if (size*howmany==0) return(NULL);
134     c=calloc(howmany, size);
135     malloced_bytes+=size*howmany;
136     if (c==0) memory_out_error(size, howmany, whatfor);
137     if (memout_switch)
138         printf("Allocating %ld bytes: array (%ld entries size %ld) \
139 for %s at (%08lx)\n",
140             ((long int)size) * ((long int)howmany),
141             (long int)howmany,(long int)size,whatfor,
142             (long int) c);
143     return(c);
144 }
145
146 extern void my_recalloc(void *pointer, size_t size, size_t oldhowmany, 
147     size_t howmany, char *whatfor)
148 {   void *c;
149     if (size*howmany==0) {
150         my_free(pointer, whatfor);
151         return;
152     }
153     c=realloc(*(int **)pointer, size*howmany); 
154     malloced_bytes+=size*(howmany-oldhowmany);
155     if (c==0) memory_out_error(size, howmany, whatfor);
156     if (memout_switch)
157         printf("Increasing allocation from %ld to %ld bytes: array (%ld entries size %ld) for %s was (%08lx) now (%08lx)\n",
158             ((long int)size) * ((long int)oldhowmany),
159             ((long int)size) * ((long int)howmany),
160             (long int)howmany, (long int)size, whatfor,
161             (long int) *(int **)pointer, (long int) c);
162     *(int **)pointer = c;
163 }
164
165 #endif
166
167 extern void my_free(void *pointer, char *whatitwas)
168 {
169     if (*(int **)pointer != NULL)
170     {   if (memout_switch)
171             printf("Freeing memory for %s at (%08lx)\n",
172                 whatitwas, (long int) (*(int **)pointer));
173 #ifdef PC_QUICKC
174         hfree(*(int **)pointer);
175 #else
176         free(*(int **)pointer);
177 #endif
178         *(int **)pointer = NULL;
179     }
180 }
181
182 /* ------------------------------------------------------------------------- */
183 /*   A dynamic memory array. This grows as needed (but never shrinks).       */
184 /*   Call ensure_memory_list_available(N) before accessing array item N-1.   */
185 /*                                                                           */
186 /*   whatfor must be a static string describing the list. initalloc is       */
187 /*   (optionally) the number of items to allocate right away.                */
188 /*                                                                           */
189 /*   You typically initialise this with extpointer referring to an array of  */
190 /*   structs or whatever type you need. Whenever the memory list grows, the  */
191 /*   external array will be updated to refer to the new data.                */
192 /*                                                                           */
193 /*   Add "#define DEBUG_MEMLISTS" to allocate exactly the number of items    */
194 /*   needed, rather than increasing allocations exponentially. This is very  */
195 /*   slow but it lets us track down array overruns.                          */
196 /* ------------------------------------------------------------------------- */
197
198 void initialise_memory_list(memory_list *ML, size_t itemsize, size_t initalloc, void **extpointer, char *whatfor)
199 {
200     #ifdef DEBUG_MEMLISTS
201     initalloc = 0;          /* No initial allocation */
202     #endif
203     
204     ML->whatfor = whatfor;
205     ML->itemsize = itemsize;
206     ML->count = 0;
207     ML->data = NULL;
208     ML->extpointer = extpointer;
209
210     if (initalloc) {
211         ML->count = initalloc;
212         ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor);
213         if (ML->data == NULL) return;
214     }
215
216     if (ML->extpointer)
217         *(ML->extpointer) = ML->data;
218 }
219
220 void deallocate_memory_list(memory_list *ML)
221 {
222     ML->itemsize = 0;
223     ML->count = 0;
224     
225     if (ML->data)
226         my_free(&(ML->data), ML->whatfor);
227
228     if (ML->extpointer)
229         *(ML->extpointer) = NULL;
230     ML->extpointer = NULL;
231 }
232
233 /* After this is called, at least count items will be available in the list.
234    That is, you can freely access array[0] through array[count-1]. */
235 void ensure_memory_list_available(memory_list *ML, size_t count)
236 {
237     size_t oldcount;
238     
239     if (ML->itemsize == 0) {
240         /* whatfor is also null! */
241         compiler_error("memory: attempt to access uninitialized memory_list");
242         return;
243     }
244
245     if (ML->count >= count) {
246         return;
247     }
248
249     oldcount = ML->count;
250     ML->count = 2*count+8;  /* Allow headroom for future growth */
251     
252     #ifdef DEBUG_MEMLISTS
253     ML->count = count;      /* No headroom */
254     #endif
255     
256     if (ML->data == NULL)
257         ML->data = my_calloc(ML->itemsize, ML->count, ML->whatfor);
258     else
259         my_recalloc(&(ML->data), ML->itemsize, oldcount, ML->count, ML->whatfor);
260     if (ML->data == NULL) return;
261
262     if (ML->extpointer)
263         *(ML->extpointer) = ML->data;
264 }
265
266 /* ------------------------------------------------------------------------- */
267 /*   Where the memory settings are declared as variables                     */
268 /* ------------------------------------------------------------------------- */
269
270 int HASH_TAB_SIZE;
271 int MAX_ABBREVS;
272 int MAX_DYNAMIC_STRINGS;
273 int MAX_LOCAL_VARIABLES;
274 int DICT_WORD_SIZE; /* number of characters in a dict word */
275 int DICT_CHAR_SIZE; /* (glulx) 1 for one-byte chars, 4 for Unicode chars */
276 int DICT_WORD_BYTES; /* DICT_WORD_SIZE*DICT_CHAR_SIZE */
277 int ZCODE_HEADER_EXT_WORDS; /* (zcode 1.0) requested header extension size */
278 int ZCODE_HEADER_FLAGS_3; /* (zcode 1.1) value to place in Flags 3 word */
279 int ZCODE_LESS_DICT_DATA; /* (zcode) use 2 data bytes per dict word instead of 3 */
280 int NUM_ATTR_BYTES;
281 int GLULX_OBJECT_EXT_BYTES; /* (glulx) extra bytes for each object record */
282 int32 MAX_STACK_SIZE;
283 int32 MEMORY_MAP_EXTENSION;
284 int WARN_UNUSED_ROUTINES; /* 0: no, 1: yes except in system files, 2: yes always */
285 int OMIT_UNUSED_ROUTINES; /* 0: no, 1: yes */
286 int STRIP_UNREACHABLE_LABELS; /* 0: no, 1: yes (default) */
287 int TRANSCRIPT_FORMAT; /* 0: classic, 1: prefixed */
288
289 /* The way memory sizes are set causes great nuisance for those parameters
290    which have different defaults under Z-code and Glulx. We have to get
291    the defaults right whether the user sets "-G $HUGE" or "$HUGE -G". 
292    And an explicit value set by the user should override both defaults. */
293 static int DICT_WORD_SIZE_z, DICT_WORD_SIZE_g;
294 static int NUM_ATTR_BYTES_z, NUM_ATTR_BYTES_g;
295 static int MAX_DYNAMIC_STRINGS_z, MAX_DYNAMIC_STRINGS_g;
296
297 /* ------------------------------------------------------------------------- */
298 /*   Memory control from the command line                                    */
299 /* ------------------------------------------------------------------------- */
300
301 static void list_memory_sizes(void)
302 {   printf("+--------------------------------------+\n");
303     printf("|  %25s = %-7s |\n","Memory setting","Value");
304     printf("+--------------------------------------+\n");
305     printf("|  %25s = %-7d |\n","MAX_ABBREVS",MAX_ABBREVS);
306     printf("|  %25s = %-7d |\n","NUM_ATTR_BYTES",NUM_ATTR_BYTES);
307     printf("|  %25s = %-7d |\n","DICT_WORD_SIZE",DICT_WORD_SIZE);
308     if (glulx_mode)
309       printf("|  %25s = %-7d |\n","DICT_CHAR_SIZE",DICT_CHAR_SIZE);
310     printf("|  %25s = %-7d |\n","MAX_DYNAMIC_STRINGS",MAX_DYNAMIC_STRINGS);
311     printf("|  %25s = %-7d |\n","HASH_TAB_SIZE",HASH_TAB_SIZE);
312     if (!glulx_mode)
313       printf("|  %25s = %-7d |\n","ZCODE_HEADER_EXT_WORDS",ZCODE_HEADER_EXT_WORDS);
314     if (!glulx_mode)
315       printf("|  %25s = %-7d |\n","ZCODE_HEADER_FLAGS_3",ZCODE_HEADER_FLAGS_3);
316     if (!glulx_mode)
317       printf("|  %25s = %-7d |\n","ZCODE_LESS_DICT_DATA",ZCODE_LESS_DICT_DATA);
318     printf("|  %25s = %-7d |\n","INDIV_PROP_START", INDIV_PROP_START);
319     if (glulx_mode)
320       printf("|  %25s = %-7d |\n","MEMORY_MAP_EXTENSION",
321         MEMORY_MAP_EXTENSION);
322     if (glulx_mode)
323       printf("|  %25s = %-7d |\n","GLULX_OBJECT_EXT_BYTES",
324         GLULX_OBJECT_EXT_BYTES);
325     if (glulx_mode)
326       printf("|  %25s = %-7ld |\n","MAX_STACK_SIZE",
327            (long int) MAX_STACK_SIZE);
328     printf("|  %25s = %-7d |\n","TRANSCRIPT_FORMAT",TRANSCRIPT_FORMAT);
329     printf("|  %25s = %-7d |\n","WARN_UNUSED_ROUTINES",WARN_UNUSED_ROUTINES);
330     printf("|  %25s = %-7d |\n","OMIT_UNUSED_ROUTINES",OMIT_UNUSED_ROUTINES);
331     printf("|  %25s = %-7d |\n","STRIP_UNREACHABLE_LABELS",STRIP_UNREACHABLE_LABELS);
332     printf("+--------------------------------------+\n");
333 }
334
335 extern void set_memory_sizes(void)
336 {
337     HASH_TAB_SIZE      = 512;
338     DICT_CHAR_SIZE = 1;
339     DICT_WORD_SIZE_z = 6;
340     DICT_WORD_SIZE_g = 9;
341     NUM_ATTR_BYTES_z = 6;
342     NUM_ATTR_BYTES_g = 7;
343     MAX_ABBREVS = 64;
344     MAX_DYNAMIC_STRINGS_z = 32;
345     MAX_DYNAMIC_STRINGS_g = 100;
346     /* Backwards-compatible behavior: allow for a unicode table
347        whether we need one or not. The user can set this to zero if
348        there's no unicode table. */
349     ZCODE_HEADER_EXT_WORDS = 3;
350     ZCODE_HEADER_FLAGS_3 = 0;
351     ZCODE_LESS_DICT_DATA = 0;
352     GLULX_OBJECT_EXT_BYTES = 0;
353     MEMORY_MAP_EXTENSION = 0;
354     /* We estimate the default Glulx stack size at 4096. That's about
355        enough for 90 nested function calls with 8 locals each -- the
356        same capacity as the Z-Spec's suggestion for Z-machine stack
357        size. Note that Inform 7 wants more stack; I7-generated code
358        sets MAX_STACK_SIZE to 65536 by default. */
359     MAX_STACK_SIZE = 4096;
360     OMIT_UNUSED_ROUTINES = 0;
361     WARN_UNUSED_ROUTINES = 0;
362     STRIP_UNREACHABLE_LABELS = 1;
363     TRANSCRIPT_FORMAT = 0;
364
365     adjust_memory_sizes();
366 }
367
368 extern void adjust_memory_sizes()
369 {
370   if (!glulx_mode) {
371     DICT_WORD_SIZE = DICT_WORD_SIZE_z;
372     NUM_ATTR_BYTES = NUM_ATTR_BYTES_z;
373     MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_z;
374     INDIV_PROP_START = 64;
375   }
376   else {
377     DICT_WORD_SIZE = DICT_WORD_SIZE_g;
378     NUM_ATTR_BYTES = NUM_ATTR_BYTES_g;
379     MAX_DYNAMIC_STRINGS = MAX_DYNAMIC_STRINGS_g;
380     INDIV_PROP_START = 256;
381   }
382 }
383
384 static void explain_parameter(char *command)
385 {   printf("\n");
386     if (strcmp(command,"HASH_TAB_SIZE")==0)
387     {   printf(
388 "  HASH_TAB_SIZE is the size of the hash tables used for the heaviest \n\
389   symbols banks.\n");
390         return;
391     }
392     if (strcmp(command,"DICT_WORD_SIZE")==0)
393     {   printf(
394 "  DICT_WORD_SIZE is the number of characters in a dictionary word. In \n\
395   Z-code this is always 6 (only 4 are used in v3 games). In Glulx it \n\
396   can be any number.\n");
397         return;
398     }
399     if (strcmp(command,"DICT_CHAR_SIZE")==0)
400     {   printf(
401 "  DICT_CHAR_SIZE is the byte size of one character in the dictionary. \n\
402   (This is only meaningful in Glulx, since Z-code has compressed dictionary \n\
403   words.) It can be either 1 (the default) or 4 (to enable full Unicode \n\
404   input.)\n");
405         return;
406     }
407     if (strcmp(command,"NUM_ATTR_BYTES")==0)
408     {   printf(
409 "  NUM_ATTR_BYTES is the space used to store attribute flags. Each byte \n\
410   stores eight attributes. In Z-code this is always 6 (only 4 are used in \n\
411   v3 games). In Glulx it can be any number which is a multiple of four, \n\
412   plus three.\n");
413         return;
414     }
415     if (strcmp(command,"ZCODE_HEADER_EXT_WORDS")==0)
416     {   printf(
417 "  ZCODE_HEADER_EXT_WORDS is the number of words in the Z-code header \n\
418   extension table (Z-Spec 1.0). The -W switch also sets this. It defaults \n\
419   to 3, but can be set higher. (It can be set lower if no Unicode \n\
420   translation table is created.)\n");
421         return;
422     }
423     if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0)
424     {   printf(
425 "  ZCODE_HEADER_FLAGS_3 is the value to store in the Flags 3 word of the \n\
426   header extension table (Z-Spec 1.1).\n");
427         return;
428     }
429     if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0)
430     {   printf(
431 "  ZCODE_LESS_DICT_DATA, if set, provides each dict word with two data bytes\n\
432   rather than three. (Z-code only.)\n");
433         return;
434     }
435     if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
436     {   printf(
437 "  GLULX_OBJECT_EXT_BYTES is an amount of additional space to add to each \n\
438   object record. It is initialized to zero bytes, and the game is free to \n\
439   use it as desired. (This is only meaningful in Glulx, since Z-code \n\
440   specifies the object structure.)\n");
441         return;
442     }
443     if (strcmp(command,"MAX_ABBREVS")==0)
444     {   printf(
445 "  MAX_ABBREVS is the maximum number of declared abbreviations.  It is not \n\
446   allowed to exceed 96 in Z-code. (This is not meaningful in Glulx, where \n\
447   there is no limit on abbreviations.)\n");
448         return;
449     }
450     if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0)
451     {   printf(
452 "  MAX_DYNAMIC_STRINGS is the maximum number of string substitution variables\n\
453   (\"@00\" or \"@(0)\").  It is not allowed to exceed 96 in Z-code.\n");
454         return;
455     }
456     if (strcmp(command,"INDIV_PROP_START")==0)
457     {   printf(
458 "  Properties 1 to INDIV_PROP_START-1 are common properties; individual\n\
459   properties are numbered INDIV_PROP_START and up.\n");
460         return;
461     }
462     if (strcmp(command,"MAX_STACK_SIZE")==0)
463     {
464         printf(
465 "  MAX_STACK_SIZE is the maximum size (in bytes) of the interpreter stack \n\
466   during gameplay. (Glulx only)\n");
467         return;
468     }
469     if (strcmp(command,"MEMORY_MAP_EXTENSION")==0)
470     {
471         printf(
472 "  MEMORY_MAP_EXTENSION is the number of bytes (all zeroes) to map into \n\
473   memory after the game file. (Glulx only)\n");
474         return;
475     }
476     if (strcmp(command,"TRANSCRIPT_FORMAT")==0)
477     {
478         printf(
479 "  TRANSCRIPT_FORMAT, if set to 1, adjusts the gametext.txt transcript for \n\
480   easier machine processing; each line will be prefixed by its context.\n");
481         return;
482     }
483     if (strcmp(command,"WARN_UNUSED_ROUTINES")==0)
484     {
485         printf(
486 "  WARN_UNUSED_ROUTINES, if set to 2, will display a warning for each \n\
487   routine in the game file which is never called. (This includes \n\
488   routines called only from uncalled routines, etc.) If set to 1, will warn \n\
489   only about functions in game code, not in the system library.\n");
490         return;
491     }
492     if (strcmp(command,"OMIT_UNUSED_ROUTINES")==0)
493     {
494         printf(
495 "  OMIT_UNUSED_ROUTINES, if set to 1, will avoid compiling unused routines \n\
496   into the game file.\n");
497         return;
498     }
499     if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0)
500     {
501         printf(
502 "  STRIP_UNREACHABLE_LABELS, if set to 1, will skip labels in unreachable \n\
503   statements. Jumping to a skipped label is an error. If 0, all labels \n\
504   will be compiled, at the cost of less optimized code. The default is 1.\n");
505         return;
506     }
507     if (strcmp(command,"SERIAL")==0)
508     {
509         printf(
510 "  SERIAL, if set, will be used as the six digit serial number written into \n\
511   the header of the output file.\n");
512         return;
513     }
514
515     printf("No such memory setting as \"%s\"\n",command);
516
517     return;
518 }
519
520 /* Parse a decimal number as an int32. Return true if a valid number
521    was found; otherwise print a warning and return false.
522
523    Anything over nine digits is considered an overflow; we report a
524    warning but return +/- 999999999 (and true). This is not entirely
525    clever about leading zeroes ("0000000001" is treated as an
526    overflow) but this is better than trying to detect genuine
527    overflows in a long.
528
529    (Some Glulx settings might conceivably want to go up to $7FFFFFFF,
530    which is a ten-digit number, but we're not going to allow that
531    today.)
532
533    This used to rely on atoi(), and we retain the atoi() behavior of
534    ignoring garbage characters after a valid decimal number.
535  */
536 static int parse_memory_setting(char *str, char *label, int32 *result)
537 {
538     char *cx = str;
539     char *ex;
540     long val;
541
542     *result = 0;
543
544     while (*cx == ' ') cx++;
545
546     val = strtol(cx, &ex, 10);    
547
548     if (ex == cx) {
549         printf("Bad numerical setting in $ command \"%s=%s\"\n",
550             label, str);
551         return 0;
552     }
553
554     if (*cx == '-') {
555         if (ex > cx+10) {
556             val = -999999999;
557             printf("Numerical setting underflowed in $ command \"%s=%s\" (limiting to %ld)\n",
558                 label, str, val);
559         }
560     }
561     else {
562         if (ex > cx+9) {
563             val = 999999999;
564             printf("Numerical setting overflowed in $ command \"%s=%s\" (limiting to %ld)\n",
565                 label, str, val);
566         }
567     }
568
569     *result = (int32)val;
570     return 1;
571 }
572
573 static void add_predefined_symbol(char *command)
574 {
575     int ix;
576     
577     int value = 0;
578     char *valpos = NULL;
579     
580     for (ix=0; command[ix]; ix++) {
581         if (command[ix] == '=') {
582             valpos = command+(ix+1);
583             command[ix] = '\0';
584             break;
585         }
586     }
587     
588     for (ix=0; command[ix]; ix++) {
589         if ((ix == 0 && isdigit(command[ix]))
590             || !(isalnum(command[ix]) || command[ix] == '_')) {
591             printf("Attempt to define invalid symbol: %s\n", command);
592             return;
593         }
594     }
595
596     if (valpos) {
597         if (!parse_memory_setting(valpos, command, &value)) {
598             return;
599         };
600     }
601
602     add_config_symbol_definition(command, value);
603 }
604
605 static void set_trace_option(char *command)
606 {
607     char *cx;
608     int value;
609
610     /* Parse options of the form STRING or STRING=NUM. (The $! has already been eaten.) If the string is null or empty, show help. */
611     
612     if (!command || *command == '\0') {
613         printf("The full list of trace options:\n\n");
614         printf("  ACTIONS: show actions defined\n");
615         printf("  ASM: trace assembly (same as -a)\n");
616         printf("    ASM=2: also show hex dumps\n");
617         printf("    ASM=3: also show branch optimization info\n");
618         printf("    ASM=4: more verbose branch info\n");
619         printf("  BPATCH: show backpatch results\n");
620         printf("    BPATCH=2: also show markers added\n");
621         printf("  DICT: display the dictionary table\n");
622         printf("    DICT=2: also the byte encoding of entries\n");
623         printf("  EXPR: show expression trees\n");
624         printf("    EXPR=2: more verbose\n");
625         printf("    EXPR=3: even more verbose\n");
626         printf("  FILES: show files opened\n");
627         printf("  FINDABBREVS: show selection decisions during abbreviation optimization\n    (only meaningful with -u)\n");
628         printf("    FINDABBREVS=2: also show three-letter-block decisions\n");
629         printf("  FREQ: show how efficient abbreviations were (same as -f)\n    (only meaningful with -e)\n");
630         printf("  MAP: print memory map of the virtual machine (same as -z)\n");
631         printf("    MAP=2: also show percentage of VM that each segment occupies\n");
632         printf("  MEM: show internal memory allocations\n");
633         printf("  OBJECTS: display the object table\n");
634         printf("  PROPS: show attributes and properties defined\n");
635         printf("  RUNTIME: show game function calls at runtime (same as -g)\n");
636         printf("    RUNTIME=2: also show library calls (not supported in Glulx)\n");
637         printf("    RUNTIME=3: also show veneer calls (not supported in Glulx)\n");
638         printf("  STATS: give compilation statistics (same as -s)\n");
639         printf("  SYMBOLS: display the symbol table\n");
640         printf("    SYMBOLS=2: also show compiler-defined symbols\n");
641         printf("  SYMDEF: show when symbols are noticed and defined\n");
642         printf("  TOKENS: show token lexing\n");
643         printf("    TOKENS=2: also show token types\n");
644         printf("    TOKENS=3: also show lexical context\n");
645         printf("  VERBS: display the verb grammar table\n");
646         return;
647     }
648
649     for (cx=command; *cx && *cx != '='; cx++) {
650         if (!(*cx >= 'A' && *cx <= 'Z')) {
651             printf("Invalid $! trace command \"%s\"\n", command);
652             return;
653         }
654     }
655
656     value = 1;
657     if (*cx == '=') {
658         char *ex;
659         value = strtol(cx+1, &ex, 10);
660         
661         if (ex == cx+1 || *ex != '\0' || value < 0) {
662             printf("Bad numerical setting in $! trace command \"%s\"\n", command);
663             return;
664         }
665         
666         *cx = '\0';
667     }
668
669     /* We accept some reasonable synonyms, including plausible singular/plural confusion. */
670     
671     if (strcmp(command, "ASSEMBLY")==0 || strcmp(command, "ASM")==0) {
672         asm_trace_setting = value;
673     }
674     else if (strcmp(command, "ACTION")==0 || strcmp(command, "ACTIONS")==0) {
675         printactions_switch = value;
676     }
677     else if (strcmp(command, "BPATCH")==0 || strcmp(command, "BACKPATCH")==0) {
678         bpatch_trace_setting = value;
679     }
680     else if (strcmp(command, "DICTIONARY")==0 || strcmp(command, "DICT")==0) {
681         list_dict_setting = value;
682     }
683     else if (strcmp(command, "EXPR")==0 || strcmp(command, "EXPRESSION")==0 || strcmp(command, "EXPRESSIONS")==0) {
684         expr_trace_setting = value;
685     }
686     else if (strcmp(command, "FILE")==0 || strcmp(command, "FILES")==0) {
687         files_trace_setting = value;
688     }
689     else if (strcmp(command, "FINDABBREV")==0 || strcmp(command, "FINDABBREVS")==0) {
690         optabbrevs_trace_setting = value;
691     }
692     else if (strcmp(command, "FREQUENCY")==0 || strcmp(command, "FREQUENCIES")==0 || strcmp(command, "FREQ")==0) {
693         frequencies_setting = value;
694     }
695     else if (strcmp(command, "MAP")==0) {
696         memory_map_setting = value;
697     }
698     else if (strcmp(command, "MEM")==0 || strcmp(command, "MEMORY")==0) {
699         memout_switch = value;
700     }
701     else if (strcmp(command, "OBJECTS")==0 || strcmp(command, "OBJECT")==0 || strcmp(command, "OBJS")==0 || strcmp(command, "OBJ")==0) {
702         list_objects_setting = value;
703     }
704     else if (strcmp(command, "PROP")==0 || strcmp(command, "PROPERTY")==0 || strcmp(command, "PROPS")==0 || strcmp(command, "PROPERTIES")==0) {
705         printprops_switch = value;
706     }
707     else if (strcmp(command, "RUNTIME")==0) {
708         trace_fns_setting = value;
709     }
710     else if (strcmp(command, "STATISTICS")==0 || strcmp(command, "STATS")==0 || strcmp(command, "STAT")==0) {
711         statistics_switch = value;
712     }
713     else if (strcmp(command, "SYMBOLS")==0 || strcmp(command, "SYMBOL")==0) {
714         list_symbols_setting = value;
715     }
716     else if (strcmp(command, "SYMDEF")==0 || strcmp(command, "SYMBOLDEF")==0) {
717         symdef_trace_setting = value;
718     }
719     else if (strcmp(command, "TOKEN")==0 || strcmp(command, "TOKENS")==0) {
720         tokens_trace_setting = value;
721     }
722     else if (strcmp(command, "VERBS")==0 || strcmp(command, "VERB")==0) {
723         list_verbs_setting = value;
724     }
725     else {
726         printf("Unrecognized $! trace command \"%s\"\n", command);
727     }
728 }
729
730 /* Handle a dollar-sign command option: $LIST, $FOO=VAL, and so on.
731    The option may come from the command line, an ICL file, or a header
732    comment.
733
734    (Unix-style command-line options are converted to dollar-sign format
735    before being sent here.)
736
737    The name of this function is outdated. Many of these settings are not
738    really about memory allocation.
739 */
740 extern void memory_command(char *command)
741 {   int i, k, flag=0; int32 j;
742
743     for (k=0; command[k]!=0; k++)
744         if (islower(command[k])) command[k]=toupper(command[k]);
745
746     if (command[0]=='?') { explain_parameter(command+1); return; }
747     if (command[0]=='#') { add_predefined_symbol(command+1); return; }
748     if (command[0]=='!') { set_trace_option(command+1); return; }
749
750     if (strcmp(command, "HUGE")==0
751         || strcmp(command, "LARGE")==0
752         || strcmp(command, "SMALL")==0) {
753         if (!nowarnings_switch)
754             printf("The Inform 6 memory size commands (\"SMALL, LARGE, HUGE\") are no longer needed and has been withdrawn.\n");
755         return;
756     }
757     
758     if (strcmp(command, "LIST")==0)  { list_memory_sizes(); return; }
759     
760     for (i=0; command[i]!=0; i++)
761     {   if (command[i]=='=')
762         {   command[i]=0;
763             if (!parse_memory_setting(command+i+1, command, &j)) {
764                 return;
765             }
766             if (strcmp(command,"BUFFER_LENGTH")==0)
767                 flag=2;
768             if (strcmp(command,"MAX_QTEXT_SIZE")==0)
769                 flag=3;
770             if (strcmp(command,"MAX_SYMBOLS")==0)
771                 flag=3;
772             if (strcmp(command,"MAX_BANK_SIZE")==0)
773                 flag=2;
774             if (strcmp(command,"SYMBOLS_CHUNK_SIZE")==0)
775                 flag=3;
776             if (strcmp(command,"BANK_CHUNK_SIZE")==0)
777                 flag=2;
778             if (strcmp(command,"HASH_TAB_SIZE")==0)
779                 HASH_TAB_SIZE=j, flag=1;
780             if (strcmp(command,"MAX_OBJECTS")==0)
781                 flag=3;
782             if (strcmp(command,"MAX_ACTIONS")==0)
783                 flag=3;
784             if (strcmp(command,"MAX_ADJECTIVES")==0)
785                 flag=3;
786             if (strcmp(command,"MAX_DICT_ENTRIES")==0)
787                 flag=3;
788             if (strcmp(command,"DICT_WORD_SIZE")==0) 
789             {   DICT_WORD_SIZE=j, flag=1;
790                 DICT_WORD_SIZE_g=DICT_WORD_SIZE_z=j;
791             }
792             if (strcmp(command,"DICT_CHAR_SIZE")==0)
793                 DICT_CHAR_SIZE=j, flag=1;
794             if (strcmp(command,"NUM_ATTR_BYTES")==0) 
795             {   NUM_ATTR_BYTES=j, flag=1;
796                 NUM_ATTR_BYTES_g=NUM_ATTR_BYTES_z=j;
797             }
798             if (strcmp(command,"ZCODE_HEADER_EXT_WORDS")==0)
799                 ZCODE_HEADER_EXT_WORDS=j, flag=1;
800             if (strcmp(command,"ZCODE_HEADER_FLAGS_3")==0)
801                 ZCODE_HEADER_FLAGS_3=j, flag=1;
802             if (strcmp(command,"ZCODE_LESS_DICT_DATA")==0)
803                 ZCODE_LESS_DICT_DATA=j, flag=1;
804             if (strcmp(command,"GLULX_OBJECT_EXT_BYTES")==0)
805                 GLULX_OBJECT_EXT_BYTES=j, flag=1;
806             if (strcmp(command,"MAX_STATIC_DATA")==0)
807                 flag=3;
808             if (strcmp(command,"MAX_OLDEPTH")==0)
809                 flag=2;
810             if (strcmp(command,"MAX_ROUTINES")==0)
811                 flag=2;
812             if (strcmp(command,"MAX_GCONSTANTS")==0)
813                 flag=2;
814             if (strcmp(command,"MAX_PROP_TABLE_SIZE")==0)
815                 flag=3;
816             if (strcmp(command,"MAX_FORWARD_REFS")==0)
817                 flag=2;
818             if (strcmp(command,"STACK_SIZE")==0)
819                 flag=2;
820             if (strcmp(command,"STACK_LONG_SLOTS")==0)
821                 flag=2;
822             if (strcmp(command,"STACK_SHORT_LENGTH")==0)
823                 flag=2;
824             if (strcmp(command,"MAX_ABBREVS")==0)
825                 MAX_ABBREVS=j, flag=1;
826             if (strcmp(command,"MAX_DYNAMIC_STRINGS")==0)
827             {   MAX_DYNAMIC_STRINGS=j, flag=1;
828                 MAX_DYNAMIC_STRINGS_g=MAX_DYNAMIC_STRINGS_z=j;
829             }
830             if (strcmp(command,"MAX_ARRAYS")==0)
831                 flag=3;
832             if (strcmp(command,"MAX_EXPRESSION_NODES")==0)
833                 flag=3;
834             if (strcmp(command,"MAX_VERBS")==0)
835                 flag=3;
836             if (strcmp(command,"MAX_VERBSPACE")==0)
837                 flag=3;
838             if (strcmp(command,"MAX_LABELS")==0)
839                 flag=3;
840             if (strcmp(command,"MAX_LINESPACE")==0)
841                 flag=3;
842             if (strcmp(command,"MAX_NUM_STATIC_STRINGS")==0)
843                 flag=3;
844             if (strcmp(command,"MAX_STATIC_STRINGS")==0)
845                 flag=3;
846             if (strcmp(command,"MAX_ZCODE_SIZE")==0)
847                 flag=3;
848             if (strcmp(command,"MAX_LINK_DATA_SIZE")==0)
849                 flag=3;
850             if (strcmp(command,"MAX_LOW_STRINGS")==0)
851                 flag=3;
852             if (strcmp(command,"MAX_TRANSCRIPT_SIZE")==0)
853                 flag=3;
854             if (strcmp(command,"MAX_CLASSES")==0)
855                 flag=3;
856             if (strcmp(command,"MAX_INCLUSION_DEPTH")==0)
857                 flag=3;
858             if (strcmp(command,"MAX_SOURCE_FILES")==0)
859                 flag=3;
860             if (strcmp(command,"MAX_INDIV_PROP_TABLE_SIZE")==0)
861                 flag=3;
862             if (strcmp(command,"INDIV_PROP_START")==0)
863                 INDIV_PROP_START=j, flag=1;
864             if (strcmp(command,"MAX_OBJ_PROP_TABLE_SIZE")==0)
865                 flag=3;
866             if (strcmp(command,"MAX_OBJ_PROP_COUNT")==0)
867                 flag=3;
868             if (strcmp(command,"MAX_LOCAL_VARIABLES")==0)
869                 flag=3;
870             if (strcmp(command,"MAX_GLOBAL_VARIABLES")==0)
871                 flag=3;
872             if (strcmp(command,"ALLOC_CHUNK_SIZE")==0)
873                 flag=3;
874             if (strcmp(command,"MAX_UNICODE_CHARS")==0)
875                 flag=3;
876             if (strcmp(command,"MAX_STACK_SIZE")==0)
877             {
878                 MAX_STACK_SIZE=j, flag=1;
879                 /* Adjust up to a 256-byte boundary. */
880                 MAX_STACK_SIZE = (MAX_STACK_SIZE + 0xFF) & (~0xFF);
881             }
882             if (strcmp(command,"MEMORY_MAP_EXTENSION")==0)
883             {
884                 MEMORY_MAP_EXTENSION=j, flag=1;
885                 /* Adjust up to a 256-byte boundary. */
886                 MEMORY_MAP_EXTENSION = (MEMORY_MAP_EXTENSION + 0xFF) & (~0xFF);
887             }
888             if (strcmp(command,"TRANSCRIPT_FORMAT")==0)
889             {
890                 TRANSCRIPT_FORMAT=j, flag=1;
891                 if (TRANSCRIPT_FORMAT > 1 || TRANSCRIPT_FORMAT < 0)
892                     TRANSCRIPT_FORMAT = 1;
893             }
894             if (strcmp(command,"WARN_UNUSED_ROUTINES")==0)
895             {
896                 WARN_UNUSED_ROUTINES=j, flag=1;
897                 if (WARN_UNUSED_ROUTINES > 2 || WARN_UNUSED_ROUTINES < 0)
898                     WARN_UNUSED_ROUTINES = 2;
899             }
900             if (strcmp(command,"OMIT_UNUSED_ROUTINES")==0)
901             {
902                 OMIT_UNUSED_ROUTINES=j, flag=1;
903                 if (OMIT_UNUSED_ROUTINES > 1 || OMIT_UNUSED_ROUTINES < 0)
904                     OMIT_UNUSED_ROUTINES = 1;
905             }
906             if (strcmp(command,"STRIP_UNREACHABLE_LABELS")==0)
907             {
908                 STRIP_UNREACHABLE_LABELS=j, flag=1;
909                 if (STRIP_UNREACHABLE_LABELS > 1 || STRIP_UNREACHABLE_LABELS < 0)
910                     STRIP_UNREACHABLE_LABELS = 1;
911             }
912             if (strcmp(command,"SERIAL")==0)
913             {
914                 if (j >= 0 && j <= 999999)
915                 {
916                     sprintf(serial_code_buffer,"%06d",j);
917                     serial_code_given_in_program = TRUE;
918                     flag=1;
919                 }
920             }
921
922             if (flag==0)
923                 printf("No such memory setting as \"%s\"\n", command);
924             if (flag==2 && !nowarnings_switch)
925                 printf("The Inform 5 memory setting \"%s\" has been withdrawn.\n", command);
926             if (flag==3 && !nowarnings_switch)
927                 printf("The Inform 6 memory setting \"%s\" is no longer needed and has been withdrawn.\n", command);
928             return;
929         }
930     }
931     printf("No such memory $ command as \"%s\"\n",command);
932 }
933
934 extern void print_memory_usage(void)
935 {
936     printf("Properties table used %d\n",
937         properties_table_size);
938     printf("Allocated a total of %ld bytes of memory\n",
939         (long int) malloced_bytes);
940 }
941
942 /* ========================================================================= */
943 /*   Data structure management routines                                      */
944 /* ------------------------------------------------------------------------- */
945
946 extern void init_memory_vars(void)
947 {   malloced_bytes = 0;
948 }
949
950 extern void memory_begin_pass(void) { }
951
952 extern void memory_allocate_arrays(void) { }
953
954 extern void memory_free_arrays(void) { }
955
956 /* ========================================================================= */