185a7810500c431e27ce5d4d75ee283cdfbadddf
[projects/chimara/chimara.git] / interpreters / nitfol / objects.c
1 /*  Nitfol - z-machine interpreter using Glk for output.
2     Copyright (C) 1999  Evin Robertson
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
17
18     The author can be reached at nitfol@deja.com
19 */
20 #include "nitfol.h"
21
22 /* attribute/8 will be the byte offset into the object entry for attribute */
23 #define ATTRIBUTE(n, a) z_memory[z_objecttable + n * OBJSIZE + a / 8]
24
25 #define OBJ_ADDR(n)     (z_objecttable + (n) * OBJSIZE)
26
27 #define PARENTP(n)      (OBJ_ADDR(n) + oPARENT)
28 #define SIBLINGP(n)     (OBJ_ADDR(n) + oSIBLING)
29 #define CHILDP(n)       (OBJ_ADDR(n) + oCHILD)
30 #define PROPSP(n)       (OBJ_ADDR(n) + oPROPS)
31
32 #define PARENT(n)  LOWO(PARENTP(n))
33 #define SIBLING(n) LOWO(SIBLINGP(n))
34 #define CHILD(n)   LOWO(CHILDP(n))
35 #define PROPS(n)   LOWORD(PROPSP(n))
36
37
38 static zword OBJSIZE;
39 static zword oPARENT, oSIBLING, oCHILD, oPROPS;
40 static zword PROP_NUM_MASK, ATTR_COUNT;
41
42 static BOOL object_property_loop(zword object, zword *propnum,
43                                  zword *location, zword *len);
44
45
46 zword LOWO(zword p)
47 {
48   if(zversion >= 4)
49     return LOWORD(p);
50   return LOBYTE(p);
51 }
52
53 void LOWOcopy(zword a, zword b)
54 {
55   if(zversion >= 4) {
56     LOWORDcopy(a, b);
57   } else {
58     LOBYTEcopy(a, b);
59   }
60 }
61
62 void LOWOwrite(zword p, zword n)
63 {
64   if(zversion >= 4) {
65     LOWORDwrite(p, n);
66   } else {
67     LOBYTEwrite(p, n);
68   }
69 }
70
71
72 #ifdef FAST
73 #define check_obj_valid(obj) TRUE
74 #define check_attr_valid(attr) TRUE
75 #else
76 static BOOL check_obj_valid(zword object)
77 {
78   if(object > object_count) { /* Object past the first property table entry */
79     if(object > maxobjs) {    /* Object past the end of dynamic memory */
80       n_show_error(E_OBJECT, "object number too large", object);
81       return FALSE;
82     }
83     n_show_warn(E_OBJECT, "object number probably too large", object);
84   }
85   if(object == 0) {
86     n_show_error(E_OBJECT, "vile object 0 error from hell", object);
87     return FALSE;
88   }
89   return TRUE;
90 }
91
92 static BOOL check_attr_valid(zword attr)
93 {
94   if(attr > ATTR_COUNT) {
95     n_show_error(E_OBJECT, "attempt to access illegal attribute", attr);
96     return FALSE;
97   }
98   return TRUE;
99 }
100 #endif
101
102
103
104 void objects_init(void)
105 {
106   zword object;
107
108   if(zversion >= 4) {
109     OBJSIZE = 14;
110     oPARENT = 6;
111     oSIBLING = 8;
112     oCHILD = 10;
113     oPROPS = 12;
114     PROP_NUM_MASK = 0x3f;
115     ATTR_COUNT = 47;
116   } else {
117     OBJSIZE = 9;
118     oPARENT = 4;
119     oSIBLING = 5;
120     oCHILD = 6;
121     oPROPS = 7;
122     PROP_NUM_MASK = 0x1f;
123     ATTR_COUNT = 31;
124   }
125
126   /* Address of objects; we want this to be one before the first object 
127      because there's no object 0 */
128   z_objecttable = z_propdefaults + PROP_NUM_MASK * ZWORD_SIZE - OBJSIZE;
129   maxobjs = (dynamic_size - z_objecttable) / OBJSIZE;
130
131   obj_first_prop_addr = ZWORD_MASK;
132   obj_last_prop_addr = 0;
133
134   prop_table_start = ZWORD_MASK;
135   prop_table_end = 0;
136
137   /* Count objects in tree, assuming objects end where proptables begin */
138   for(object = 1; OBJ_ADDR(object) < prop_table_start; object++) {
139     zword propnum, location, len;
140
141     if(PROPS(object) < prop_table_start)
142       prop_table_start = PROPS(object);
143
144     location = 0;
145     while(object_property_loop(object, &propnum, &location, &len)) {
146       if(location < obj_first_prop_addr)
147         obj_first_prop_addr = location;
148       if(location > obj_last_prop_addr)
149         obj_last_prop_addr = location;
150
151       if(location + len > prop_table_end)
152         prop_table_end = location + len;
153     }
154   }
155
156   object_count = object - 1;
157
158   if(object_count > maxobjs)
159     object_count = maxobjs;
160 }
161
162
163
164
165 void op_get_child(void)
166 {
167   zword child;
168   if(!check_obj_valid(operand[0])) {
169     mop_store_result(0);
170     mop_skip_branch();
171     return;
172   }
173
174   debug_object(operand[0], OBJ_GET_INFO);
175
176   child = CHILD(operand[0]);
177   mop_store_result(child);
178   mop_cond_branch(child != 0);
179 }
180
181
182 void op_get_parent(void)
183 {
184   zword parent;
185   if(!check_obj_valid(operand[0])) {
186     mop_store_result(0);
187     return;
188   }
189
190   debug_object(operand[0], OBJ_GET_INFO);
191
192   parent = PARENT(operand[0]);
193   mop_store_result(parent);
194 }
195
196
197 void op_get_sibling(void)
198 {
199   zword sibling;
200   if(!check_obj_valid(operand[0])) {
201     mop_store_result(0);
202     mop_skip_branch();
203     return;
204   }
205
206   debug_object(operand[0], OBJ_GET_INFO);
207
208   sibling = SIBLING(operand[0]);
209   mop_store_result(sibling);
210   mop_cond_branch(sibling != 0);
211 }
212
213
214
215 void op_insert_obj(void)
216 {
217   zword object = operand[0], dest = operand[1];
218
219   if(!check_obj_valid(object) || !check_obj_valid(dest))
220     return;
221
222 #ifndef FAST
223   {
224     zword child = object;
225     int depth = 0;
226     while(child) {
227       if(child == dest) {
228         n_show_error(E_OBJECT, "attempt to place an object inside itself", object);
229         return;
230       }
231       child = CHILD(child);
232       depth++;
233       if(depth > maxobjs) {
234         n_show_error(E_OBJECT, "found objects inside themselves", child);
235         break;
236       }
237     }
238   }
239 #endif
240
241
242   op_remove_obj(); /* Remove object operand[0] from object tree */
243
244   debug_object(operand[1], OBJ_RECEIVE);
245
246   LOWOwrite(PARENTP(object), dest);
247   LOWOcopy(SIBLINGP(object), CHILDP(dest));
248   LOWOwrite(CHILDP(dest), object);
249 }
250
251
252 void op_jin(void)
253 {
254   if(!check_obj_valid(operand[0])) {
255     mop_skip_branch();
256     return;
257   }
258
259   debug_object(operand[0], OBJ_GET_INFO);
260   debug_object(operand[1], OBJ_GET_INFO);
261
262   mop_cond_branch(PARENT(operand[0]) == operand[1]);
263 }
264
265
266 void op_remove_obj(void)
267 {
268   zword parent, sibling, nextsibling;
269   zword object = operand[0];
270
271   if(!check_obj_valid(object))
272     return;
273   parent = PARENT(object);
274
275   if(!parent)   /* If no parent, do nothing with no error message */
276     return;
277   if(!check_obj_valid(parent))
278     return;
279
280   debug_object(operand[0], OBJ_MOVE);
281
282   nextsibling = CHILD(parent);
283
284   if(nextsibling == object) {  /* if it's the first child */
285     LOWOcopy(CHILDP(parent), SIBLINGP(object));
286   } else {
287     unsigned width = 0;
288     do { /* Loops until the SIBLING(sibling)==object so we can skip over it */
289       sibling = nextsibling;
290       
291       if(!check_obj_valid(sibling))
292         return;
293
294       nextsibling = SIBLING(sibling);
295
296 #ifndef FAST
297       if(width++ > maxobjs) {  /* If we've looped more times than reasonable */
298         n_show_error(E_OBJECT, "looped sibling list", parent);
299         return;
300       }
301 #endif
302     } while(nextsibling != object);
303     
304     LOWOcopy(SIBLINGP(sibling), SIBLINGP(object));/*make the list skip object*/
305   }
306
307   LOWOwrite(PARENTP(object), 0);  
308   LOWOwrite(SIBLINGP(object), 0);
309 }
310
311 offset object_name(zword object)
312 {
313   if(LOBYTE(PROPS(object)))
314     return PROPS(object) + 1;
315   else
316     return 0;
317 }
318
319
320 void op_print_obj(void)
321 {
322   offset short_name_off = object_name(operand[0]);
323   if(short_name_off)
324     decodezscii(short_name_off, output_char);
325   else
326     n_show_error(E_OBJECT, "attempt to print name of nameless object", operand[0]);
327 }
328
329
330 /* attribute opcodes: */
331
332 void op_clear_attr(void)
333 {
334   if(!check_obj_valid(operand[0]) || !check_attr_valid(operand[1]))
335     return;
336   debug_attrib(operand[1], operand[0]);
337
338   ATTRIBUTE(operand[0], operand[1]) &= ~(b10000000 >> (operand[1] & b0111));
339   /* shift top bit right to select the appropriate bit and make
340      a mask from the complement */
341 }
342
343
344 void op_set_attr(void)
345 {
346   if(!check_obj_valid(operand[0]) || !check_attr_valid(operand[1]))
347     return;
348   debug_attrib(operand[1], operand[0]);
349                                       /* select the bit to be set */
350   ATTRIBUTE(operand[0], operand[1]) |= (b10000000 >> (operand[1] & b0111));
351 }
352
353
354 void op_test_attr(void)
355 {
356   if(!check_obj_valid(operand[0]) || !check_attr_valid(operand[1])) {
357     mop_skip_branch();
358     return;
359   }
360   debug_attrib(operand[1], operand[0]);
361                                        /* select the bit to be tested */
362   if(ATTRIBUTE(operand[0], operand[1]) & (b10000000 >> (operand[1] & b0111)))
363     mop_take_branch();
364   else
365     mop_skip_branch();
366 }
367
368
369 /* property table opcodes: */
370
371 /* Helper functions */
372
373 /*
374  * Given the location of the sizebyte, returns the length of the following
375  *  property and sets *size_length to the size of the sizebyte
376  */
377 static zword get_prop_length(zword propoffset, int *size_length)
378 {
379   zword prop_length;
380
381   zbyte size_byte = LOBYTE(propoffset);
382   if(zversion >= 4) {
383     if(size_byte & b10000000) {  /* top bit set means two bytes of size info */
384       *size_length = 2;
385       prop_length = LOBYTE(propoffset + 1) & b00111111;
386       if(prop_length == 0)
387         prop_length = 64;
388     } else {  /* one byte of size info */
389       *size_length = 1;
390       if(size_byte & b01000000)
391         prop_length = 2;
392       else
393         prop_length = 1;
394     }
395   } else {
396     prop_length = (size_byte >> 5) + 1;
397     *size_length = 1;
398   }
399   return prop_length;
400 }
401
402
403 /*
404  * Loops over all properties of an object, returning FALSE if no more
405  * Before first call, set *location = 0;
406  */
407 static BOOL object_property_loop(zword object, zword *propnum,
408                                  zword *location, zword *len)
409 {
410   zword proptable;
411   int size_byte, size_length;
412
413   if(!*location) {
414     *location = proptable = PROPS(object);
415     *len = LOBYTE(proptable) * 2 + 1;   /* skip the header */
416   }
417
418   proptable  = *location;
419   proptable += *len;
420  
421   size_byte  = LOBYTE(proptable);
422   *propnum   = size_byte & PROP_NUM_MASK;
423   if(*propnum) {
424     *len       = get_prop_length(proptable, &size_length);
425     proptable += size_length;
426
427     *location  = proptable;
428     return TRUE;
429   }
430   return FALSE;
431 }
432
433
434
435 static zword get_prop_offset(zword object, zword property, zword *length)
436 {
437   zword propnum, location;
438   location = 0;
439   while(object_property_loop(object, &propnum, &location, length)) {
440     if(propnum == property)
441       return location;
442   }
443   return 0;
444 }
445
446
447 void op_get_next_prop(void)
448 {
449   zword proptable = 0;
450   zword prop_len;
451   zword prop_num;
452
453   if(!check_obj_valid(operand[0])) {
454     mop_store_result(0);
455     return;
456   }
457
458   if(operand[1] == 0) {
459     if(object_property_loop(operand[0], &prop_num, &proptable, &prop_len))
460       mop_store_result(prop_num);
461     else
462       mop_store_result(0);
463     return;
464   }
465   
466   while(object_property_loop(operand[0], &prop_num, &proptable, &prop_len)) {
467     if(prop_num == operand[1]) {
468       if(object_property_loop(operand[0], &prop_num, &proptable, &prop_len))
469         mop_store_result(prop_num);
470       else
471         mop_store_result(0);
472       return;
473     }
474   }
475
476   n_show_error(E_OBJECT, "get_next_prop on nonexistent property", operand[1]);
477   mop_store_result(0);
478   return;
479 }
480
481
482 void op_get_prop_addr(void)
483 {
484   zword proptable;
485   zword prop_len;
486   if(!check_obj_valid(operand[0])) {
487     mop_store_result(0);
488     return;
489   }
490
491   proptable = get_prop_offset(operand[0], operand[1], &prop_len);
492
493   mop_store_result(proptable);
494 }
495
496
497 void op_get_prop_len(void)
498 {
499   int size_length;
500   zword prop_length;
501
502 #ifndef FAST
503   if(operand[0] < obj_first_prop_addr ||
504      operand[0] > obj_last_prop_addr) {
505     if(operand[0] < 64) {
506       n_show_error(E_OBJECT, "get property length in header", operand[0]);
507       mop_store_result(0);
508       return;
509     }
510     n_show_warn(E_OBJECT, "get property length at probably bad address", operand[0]);
511   }
512 #endif
513
514   operand[0]--;                    /* Skip back a byte for the size byte */
515
516   if(zversion >= 4)
517     if(LOBYTE(operand[0]) & 0x80) {  /* test top bit - two bytes of size info */
518       operand[0]--;                  /* Skip back another byte */
519     }
520
521   prop_length = get_prop_length(operand[0], &size_length);
522
523   mop_store_result(prop_length);
524 }
525
526
527 void op_get_prop(void)
528 {
529   zword prop_length;
530   zword proptable;
531   if(!check_obj_valid(operand[0])) {
532     mop_store_result(0);
533     return;
534   }
535
536   proptable = get_prop_offset(operand[0], operand[1], &prop_length);
537
538   if(proptable == 0) {  /* property not provided; use property default */
539     mop_store_result(LOWORD(z_propdefaults + (operand[1]-1) * ZWORD_SIZE));
540     return;
541   }
542
543   switch(prop_length) {
544   case 1:
545     mop_store_result(LOBYTE(proptable)); break;
546 #ifndef FAST
547   default:
548     n_show_port(E_OBJECT, "get_prop on property with bad length", operand[1]);
549 #endif
550   case ZWORD_SIZE:
551     mop_store_result(LOWORD(proptable));
552   }
553 }
554
555
556 void op_put_prop(void)
557 {
558   zword prop_length;
559   zword proptable;
560   if(!check_obj_valid(operand[0])) {
561     mop_store_result(0);
562     return;
563   }
564
565   proptable = get_prop_offset(operand[0], operand[1], &prop_length);
566
567 #ifndef FAST
568   if(proptable == 0) {
569     n_show_error(E_OBJECT, "attempt to write to nonexistent property", operand[1]);
570     return;
571   }
572 #endif
573   
574   switch(prop_length) {
575   case 1:
576     LOBYTEwrite(proptable, operand[2]); break;
577 #ifndef FAST
578   default:
579     n_show_port(E_OBJECT, "put_prop on property with bad length", operand[1]);
580 #endif
581   case ZWORD_SIZE:
582     LOWORDwrite(proptable, operand[2]); break;
583   }
584 }
585
586
587
588 #ifdef DEBUGGING
589
590
591 BOOL infix_property_loop(zword object, zword *propnum, zword *location, zword *len, zword *nonindivloc, zword *nonindivlen)
592 {
593   BOOL status;
594
595   if(*location && *propnum > PROP_NUM_MASK) {
596     *location += *len;
597     *propnum = LOWORD(*location);
598     *location += ZWORD_SIZE;
599     *len = LOBYTE(*location);
600     (*location)++;
601
602     if(*propnum)
603       return TRUE;
604
605     *propnum = 0;
606     *location = *nonindivloc;
607     *len = *nonindivlen;
608     *nonindivloc = 0;
609     *nonindivlen = 0;
610   }
611
612   status = object_property_loop(object, propnum, location, len);
613   if(!status)
614     return FALSE;
615
616   if(*propnum == 3) { /* Individual property table */
617     zword iproptable = LOWORD(*location);
618     zword ilen;
619
620     *propnum = LOWORD(iproptable);
621     iproptable += ZWORD_SIZE;
622     ilen = LOBYTE(iproptable);
623     iproptable++;
624     if(!*propnum)
625       return infix_property_loop(object, propnum, location, len, nonindivloc, nonindivlen);
626
627     *nonindivloc = *location;
628     *nonindivlen = *len;
629     *location = iproptable;
630     *len = ilen;
631   }
632
633   return TRUE;
634 }
635
636
637 void infix_move(zword dest, zword object)
638 {
639   zword to1 = operand[0], to2 = operand[1];
640   operand[0] = object; operand[1] = dest;
641   op_insert_obj();
642   operand[0] = to1; operand[1] = to2;
643 }
644
645 void infix_remove(zword object)
646 {
647   zword to1 = operand[0];
648   operand[0] = object;
649   op_remove_obj();
650   operand[0] = to1;
651 }
652
653 zword infix_parent(zword object)
654 {
655   return PARENT(object);
656 }
657
658 zword infix_child(zword object)
659 {
660   return CHILD(object);
661 }
662
663 zword infix_sibling(zword object)
664 {
665   return SIBLING(object);
666 }
667
668 void infix_set_attrib(zword object, zword attrib)
669 {
670   zword to1 = operand[0], to2 = operand[1];
671   operand[0] = object; operand[1] = attrib;
672   op_set_attr();
673   operand[0] = to1; operand[1] = to2;
674 }
675
676 void infix_clear_attrib(zword object, zword attrib)
677 {
678   zword to1 = operand[0], to2 = operand[1];
679   operand[0] = object; operand[1] = attrib;
680   op_clear_attr();
681   operand[0] = to1; operand[1] = to2;
682 }
683
684
685
686 static void infix_property_display(unsigned prop,
687                                    offset proptable, unsigned prop_length)
688 {
689   BOOL do_number = TRUE;
690   BOOL do_name = TRUE;
691
692   unsigned i;
693
694   /* things we know to be objects/strings/routines */
695   static const char *decode_me_names[] = { 
696     "n_to", "nw_to", "w_to", "sw_to", "s_to", "se_to", "e_to", "ne_to",
697     "in_to", "out_to", "u_to", "d_to",
698     "add_to_scope", "after", "article", "articles", "before", "cant_go",
699     "daemon", "describe", "door_dir", "door_to", "each_turn", "found_in",
700     "grammar", "initial", "inside_description", "invent", "life", "orders",
701     "parse_name", "plural", "react_after", "react_before",
702     "short_name", "short_name_indef", "time_out", "when_closed", "when_open",
703     "when_on", "when_off", "with_key",
704     "obj",
705     "description",
706     "ofclass"
707   };
708
709   /* things we know to be just plain numbers */
710   static const char *dont_decode_names[] = {
711     "capacity", "number", "time_left"
712   };
713
714   z_typed p;
715   const char *propname;
716
717   p.v = prop; p.t = Z_PROP;
718   propname = infix_get_name(p);
719
720   if(prop == 2)
721     propname = "ofclass";
722     
723   infix_print_string(", ");
724   
725   if(propname)
726     infix_print_string(propname);
727   else {
728     infix_print_string("P");
729     infix_print_number(prop);
730   }
731   infix_print_string(" =");
732
733   if(prop == 2) {
734     for(i = 0; i < prop_length; i+=ZWORD_SIZE) {
735       offset short_name_off = object_name(LOWORD(proptable + i));
736       if(short_name_off) {
737         infix_print_char(' ');
738         decodezscii(short_name_off, infix_print_char);
739       } else {
740         infix_print_string(" <badclass>");
741       }
742     }
743     return;
744   }
745
746   if(propname) {
747     if(n_strcmp(propname, "name") == 0) {
748       for(i = 0; i < prop_length; i+=ZWORD_SIZE) {
749         infix_print_string(" '");
750         decodezscii(LOWORD(proptable + i), infix_print_char);
751         infix_print_char('\'');
752       }
753       return;
754     }
755
756     for(i = 0; i < sizeof(decode_me_names) / sizeof(*decode_me_names); i++)
757       if(n_strcmp(decode_me_names[i], propname) == 0)
758         do_number = FALSE;
759       
760     for(i = 0; i < sizeof(dont_decode_names) / sizeof(*dont_decode_names); i++)
761       if(n_strcmp(dont_decode_names[i], propname) == 0)
762         do_name = FALSE;
763   }
764
765   if(prop_length % ZWORD_SIZE || LOWORD(proptable) == 0) {
766     do_number = TRUE;
767     do_name = FALSE;
768   }
769
770   if(do_number) {
771     switch(prop_length) {
772     case 1:
773       infix_print_char(' ');
774       infix_print_znumber(LOBYTE(proptable));
775       break;
776     case ZWORD_SIZE:
777       infix_print_char(' ');
778       infix_print_znumber(LOWORD(proptable));
779       break;
780     default:
781       for(i = 0; i < prop_length; i++) {
782         infix_print_char(' ');
783         infix_print_znumber(LOBYTE(proptable + i));
784       }
785     }
786   }
787
788   if(do_name) {
789     for(i = 0; i < prop_length; i += ZWORD_SIZE) {
790       zword val = LOWORD(proptable + i);
791       const char *name = debug_decode_number(val);
792
793       if(name) {
794         infix_print_char(' ');
795         if(do_number)
796           infix_print_char('(');
797         infix_print_string(name);
798         if(do_number)
799           infix_print_char(')');
800       } else {
801         if(!do_number) {
802           infix_print_char(' ');
803           infix_print_znumber(val);
804         }
805         if(val <= object_count) {
806           offset short_name_off = object_name(val);
807           if(short_name_off) {
808             infix_print_char(' ');
809             infix_print_char('(');
810             decodezscii(short_name_off, infix_print_char);
811             infix_print_char(')');
812           }
813         }
814       }
815     }
816   }
817 }
818
819
820 static void infix_show_object(zword object)
821 {
822   const char *name;
823   if(!object) {
824     infix_print_string("0");
825   } else {
826     offset short_name_off;
827     z_typed o;
828     o.t = Z_OBJECT; o.v = object;
829     name = infix_get_name(o);
830     if(name) {
831       infix_print_string(name);
832     } else {
833       infix_print_number(object);
834     }
835
836     short_name_off = object_name(object);
837     if(short_name_off) {
838       infix_print_string(" \"");
839       decodezscii(short_name_off, infix_print_char);
840       infix_print_char('"');
841     } else if(!name) {
842       infix_print_string(" <nameless>");
843     }
844   }
845 }
846
847 zword infix_get_proptable(zword object, zword prop, zword *length)
848 {
849   zword propnum, location, nloc, nlen;
850
851   location = 0;
852   while(infix_property_loop(object, &propnum, &location, length, &nloc, &nlen)) {
853     if(propnum == prop)
854       return location;
855   }
856
857   return 0;
858 }
859
860
861 zword infix_get_prop(zword object, zword prop)
862 {
863   zword prop_length;
864   zword proptable = infix_get_proptable(object, prop, &prop_length);
865
866   if(!proptable) {
867     if(prop < PROP_NUM_MASK) { /* property defaults */
868       proptable = z_propdefaults + (prop - 1) * ZWORD_SIZE;
869       prop_length = ZWORD_SIZE;
870     } else {
871       return 0;
872     }
873   }
874
875   switch(prop_length) {
876   case 1:
877     return LOBYTE(proptable);
878   default:
879   case ZWORD_SIZE: 
880     return LOWORD(proptable);
881   }
882 }
883
884
885 void infix_put_prop(zword object, zword prop, zword val)
886 {
887   zword prop_length;
888   zword proptable = infix_get_proptable(object, prop, &prop_length);
889
890   if(!proptable)
891     return;
892
893   switch(prop_length) {
894   case 1:
895     LOBYTEwrite(proptable, val);
896   default:
897   case ZWORD_SIZE: 
898     LOWORDwrite(proptable, val);
899   }
900 }
901
902
903 BOOL infix_test_attrib(zword object, zword attrib)
904 {
905   if(!check_obj_valid(object) || !check_attr_valid(attrib)) {
906     return FALSE;
907   }
908
909                                        /* select the bit to be tested */
910   if(ATTRIBUTE(object, attrib) & (b10000000 >> (attrib & b0111)))
911     return TRUE;
912   else
913     return FALSE;
914 }
915
916
917 static char *trunk = NULL;
918 static int trunksize = 128;
919
920 static void infix_draw_trunk(int depth)
921 {
922   int i;
923   for(i = 1; i < depth; i++) {
924     if(trunk[i])
925       infix_print_fixed_string(" |  ");
926     else
927       infix_print_fixed_string("    ");
928   }
929 }
930
931 static void infix_draw_branch(int depth)
932 {
933   infix_draw_trunk(depth);
934   if(depth)
935     infix_print_fixed_string(" +->");
936 }
937
938
939 static void infix_draw_object(zword object, int depth)
940 {
941   zword c;
942   unsigned width;
943
944   if(depth >= trunksize) {
945     trunksize *= 2;
946     trunk = (char *) n_realloc(trunk, trunksize);
947   }
948
949   infix_draw_branch(depth);
950   infix_show_object(object);
951   infix_print_char(10);
952
953   /* Do a sanity check before we print anything to avoid screenfulls of junk */
954   width = 0;
955   for(c = CHILD(object); c; c = SIBLING(c)) {
956     if(width++ > maxobjs) {
957       infix_print_string("looped sibling list.\n");
958       return;
959     }
960   }
961
962   for(c = CHILD(object); c; c = SIBLING(c)) {
963     if(PARENT(c) != object) { /* Section 12.5 (b) */
964       infix_print_string("object ");
965       infix_print_number(c);
966       infix_print_string(" is a child of object ");
967       infix_print_number(object);
968       infix_print_string(" but has ");
969       infix_print_number(PARENT(c));
970       infix_print_string(" listed as its parent.\n");
971       return;
972     }
973
974     trunk[depth+1] = (SIBLING(c) != 0);
975
976     infix_draw_object(c, depth+1);
977   }
978 }
979
980 void infix_object_tree(zword object)
981 {
982   trunk = (char *) n_malloc(trunksize);
983
984   if(object != 0) {
985     infix_draw_object(object, 0);
986     n_free(trunk);
987     return;
988   }
989
990   for(object = 1; object <= object_count; object++) {
991     if(!PARENT(object)) {
992       if(SIBLING(object)) {  /* Section 12.5 (a) */
993         infix_print_string("Object ");
994         infix_print_number(object);
995         infix_print_string(" has no parent, but has sibling ");
996         infix_print_number(SIBLING(object));
997         infix_print_string(".\n");
998         return;
999       }
1000       infix_draw_object(object, 0);
1001     }
1002   }  
1003
1004   n_free(trunk);
1005
1006 }
1007
1008
1009 /* Contrary to the zspec, short names may be arbitrarily long because of
1010    abbreviations, so use realloc */
1011
1012 static char *short_name;
1013 static unsigned short_name_length;
1014 static unsigned short_name_i;
1015
1016 static void infix_copy_short_name(int ch)
1017 {
1018   if(short_name_i + 1 >= short_name_length ) {
1019     char *p;
1020     short_name_length *= 2;
1021     p = (char *) n_realloc(short_name, short_name_length);
1022     short_name = p;
1023   }
1024   short_name[short_name_i++] = ch;
1025 }
1026
1027 void infix_object_find(const char *description)
1028 {
1029   zword object;
1030   char *desc = n_strdup(description);
1031   n_strlower(desc);
1032   for(object = 1; object <= object_count; object++) {
1033     offset short_name_off = object_name(object);
1034     if(short_name_off) {
1035       short_name_length = 512;
1036       short_name = (char *) n_malloc(short_name_length);
1037       short_name_i = 0;
1038       decodezscii(short_name_off, infix_copy_short_name);
1039       short_name[short_name_i] = 0;
1040       n_strlower(short_name);
1041       if(n_strstr(short_name, desc)) {
1042         infix_show_object(object);
1043         if(PARENT(object)) {
1044           infix_print_string(" in ");
1045           infix_show_object(PARENT(object));
1046         }
1047         infix_print_char(10);
1048       }
1049       n_free(short_name);
1050     }
1051   }
1052 }
1053
1054
1055 void infix_object_display(zword object)
1056 {
1057   offset short_name_off;
1058   zword propnum, location, length, nloc, nlen;
1059   unsigned n;
1060   BOOL did;
1061
1062   if(object == 0) {
1063     infix_print_string("nothing");
1064     return;
1065   }
1066
1067   if(!check_obj_valid(object)) {
1068     infix_print_string("invalid object");
1069     return;
1070   }
1071
1072   infix_print_char('{');
1073
1074   short_name_off = object_name(object);
1075   if(short_name_off) {
1076     infix_print_string("short_name = \"");
1077     decodezscii(short_name_off, infix_print_char);
1078     infix_print_string("\", attrib =");
1079   }
1080
1081   did = FALSE;
1082   for(n = 0; n < ATTR_COUNT; n++) {
1083     if(infix_test_attrib(object, n)) {
1084       z_typed a;
1085       const char *attrname;
1086       a.t = Z_ATTR; a.v = n;
1087       attrname = infix_get_name(a);
1088       infix_print_char(' ');
1089       if(attrname)
1090         infix_print_string(attrname);
1091       else
1092         infix_print_number(n);
1093       did = TRUE;
1094     }
1095   }
1096   if(!did)
1097     infix_print_string(" <none>");
1098
1099   infix_print_string(", parent = ");
1100   infix_show_object(PARENT(object));
1101
1102   infix_print_string(", sibling = ");
1103   infix_show_object(SIBLING(object));
1104
1105   infix_print_string(", child = ");
1106   infix_show_object(CHILD(object));
1107
1108
1109   location = 0;
1110   while(infix_property_loop(object, &propnum, &location, &length, &nloc, &nlen)) {
1111     infix_property_display(propnum, location, length);
1112   }
1113
1114   infix_print_char('}');
1115 }
1116
1117 #endif /* DEBUGGING */