Bug fixes
[rodin/chimara.git] / interpreters / nitfol / infix.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 #ifdef STDOUT_DEBUG
22 #include <stdio.h>
23 #endif
24
25
26 #ifdef DEBUGGING
27
28
29 #ifdef HEADER
30
31 typedef enum { Z_UNKNOWN, Z_BOOLEAN, Z_NUMBER, Z_OBJECT, Z_ROUTINE, Z_STRING, Z_GLOBAL, Z_LOCAL, Z_BYTEARRAY, Z_WORDARRAY, Z_OBJPROP, Z_ATTR, Z_PROP, Z_ARRAY } z_type;
32
33 typedef struct z_typed z_typed;
34 struct z_typed {
35   zword v;      /* The value stored */
36   z_type t;
37
38   zword o, p;   /* location of value (if t is appropriate) */
39 };
40
41
42 typedef struct {
43   const char *filename;
44   strid_t stream;
45   int num_lines;
46   glui32 *line_locations;
47 } infix_file;
48
49 typedef struct {
50   infix_file *file;
51   int line_num;
52   int line_x;
53   const char *func_name;
54   unsigned func_num;
55   offset thisPC;
56 } infix_location;
57
58 #endif
59
60
61 #define EOF_DBR          0
62 #define FILE_DBR         1
63 #define CLASS_DBR        2
64 #define OBJECT_DBR       3
65 #define GLOBAL_DBR       4
66 #define ATTR_DBR         5
67 #define PROP_DBR         6
68 #define FAKE_ACTION_DBR  7
69 #define ACTION_DBR       8
70 #define HEADER_DBR       9
71 #define LINEREF_DBR     10
72 #define ROUTINE_DBR     11
73 #define ARRAY_DBR       12
74 #define MAP_DBR         13
75 #define ROUTINE_END_DBR 14
76 #define MAX_DBR 14
77
78 static unsigned record_info[][9] = {
79   {      0,      0,      0, 0, 0,      0, 0, 0, 0 }, /* eof_dbr */
80   {      1, 0x8000, 0x8000, 0, 0,      0, 0, 0, 0 }, /* file_dbr */
81   { 0x8000,      1,      2, 1, 1,      2, 1, 0, 0 }, /* class_dbr */
82   {      2, 0x8000,      1, 2, 1,      1, 2, 1, 0 }, /* object_dbr */
83   {      1, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* global_dbr */
84   {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* attr_dbr */
85   {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* prop_dbr */
86   {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* fake_action_dbr */
87   {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* action_dbr */
88   {     64,      0,      0, 0, 0,      0, 0, 0, 0 }, /* header_dbr */
89   {      2,      2,      0, 0, 0,      0, 0, 0, 0 }, /* lineref_dbr */
90   {      2,      1,      2, 1, 3, 0x8000, 0, 0, 0 }, /* routine_dbr */
91   {      2, 0x8000,      0, 0, 0,      0, 0, 0, 0 }, /* array_dbr */
92   {      0,      0,      0, 0, 0,      0, 0, 0, 0 }, /* map_dbr */
93   {      2,      1,      2, 1, 3,      0, 0, 0, 0 }  /* routine_end_dbr */
94 };
95
96
97 offset infix_get_routine_PC(zword routine)
98 {
99   offset destPC = UNPACKR(routine);
100   unsigned num_local = HIBYTE(destPC);
101   destPC++;
102   if(zversion < 5)
103     destPC += num_local * ZWORD_SIZE;
104   return destPC;
105 }
106
107
108 static infix_file infix_load_file_info(const char *filename)
109 {
110   infix_file r;
111   glsi32 c;
112   int lines_allocated = 0;
113                                           
114   r.filename = filename;
115   r.num_lines = 0;
116   r.line_locations = NULL;
117
118   r.stream = n_file_name(fileusage_Data | fileusage_TextMode,
119                          filemode_Read, filename);
120
121   if(!r.stream)
122     return r;
123
124   lines_allocated = 256;
125   r.line_locations = (glui32 *) n_malloc(lines_allocated * sizeof(*r.line_locations));
126   r.line_locations[r.num_lines] = 0;
127   r.num_lines++;
128
129   while((c = glk_get_char_stream(r.stream)) != GLK_EOF) {
130     if(c == 10) {
131       if(r.num_lines >= lines_allocated) {
132         glui32 *new_locations;
133
134         lines_allocated *= 2;
135         new_locations = (glui32 *) n_realloc(r.line_locations,
136                                   lines_allocated * sizeof(*r.line_locations));
137         r.line_locations = new_locations;
138       }
139       r.line_locations[r.num_lines] = glk_stream_get_position(r.stream);
140       r.num_lines++;
141     }
142   }
143   return r;
144 }
145
146
147 static void infix_unload_file_info(infix_file *f)
148 {
149   if(f->line_locations)
150     n_free(f->line_locations);
151   f->line_locations = 0;
152   return;
153 }
154
155
156 void infix_file_print_line(infix_file *f, int line)
157 {
158   glsi32 c;
159
160   if(!(f && f->stream && f->num_lines && f->line_locations))
161     return;
162
163   if(line <= 0)
164     line = 1;
165   if(line > f->num_lines)
166     line = f->num_lines;
167
168   if(fullname) {  /* Running as a subprocess under emacs */
169     infix_print_char(26);
170     infix_print_char(26);
171     infix_print_string(f->filename);
172     infix_print_char(':');
173     infix_print_number(line);
174     infix_print_char(':');
175     infix_print_number(f->line_locations[line-1] + 0);
176     infix_print_string(":beg:0x00000000");
177   } else {
178     infix_print_number(line);
179     infix_print_char('\t');
180     
181     glk_stream_set_position(f->stream, f->line_locations[line-1], seekmode_Start);
182
183     while((c = glk_get_char_stream(f->stream)) != GLK_EOF) {
184       if(c == 10)
185         break;
186       infix_print_char(c);
187     }
188   }
189
190   infix_print_char(10);
191 }
192
193
194 static unsigned local_names_info[] = { 0x8000, 0 };
195 static unsigned sequence_point_info[] ={ 1, 2, 1, 2, 0 };
196 static unsigned map_info[] = { 0x8000, 3, 0 };
197
198 static glui32 infix_strlen;
199 static char *infix_stringdata;
200
201 static glui32 infix_add_string(strid_t infix)
202 {
203   glui32 ch;
204   glui32 return_offset = infix_strlen;
205
206   while((ch = glk_get_char_stream(infix)) != 0) {
207     if(infix_stringdata)
208       infix_stringdata[infix_strlen] = ch;
209     infix_strlen++;
210   }
211
212   if(infix_stringdata)
213     infix_stringdata[infix_strlen] = 0;
214   infix_strlen++;
215
216   return return_offset;
217 }
218
219 static void infix_add_stringchar(int c)
220 {
221   /* Really inefficient to call realloc for every character, but oh well... */
222   infix_stringdata = n_realloc(infix_stringdata, infix_strlen+1);
223   infix_stringdata[infix_strlen++] = c;
224 }
225
226 static glui32 infix_add_zstring(zword paddr)
227 {
228   if(paddr) {
229     glui32 return_offset = infix_strlen;
230     offset addr = UNPACKS(paddr);
231     if(addr+2 < total_size) {
232       decodezscii(addr, infix_add_stringchar);
233       infix_add_stringchar(0);
234       return return_offset;
235     }
236   }
237   return 0xffffffffL;
238 }
239
240
241 static infix_file *infix_files;
242 static unsigned infix_filescount;
243
244 static char **infix_objects;
245 static unsigned infix_objectscount;
246
247 static char **infix_globals;
248 static unsigned infix_globalscount;
249
250 typedef struct {
251   zword address;
252   const char *name;
253 } infix_arrayref;
254
255 static infix_arrayref *infix_arrays;
256 static unsigned infix_arrayscount;
257
258 static char **infix_attrs;
259 static unsigned infix_attrscount;
260
261 static char **infix_props;
262 static unsigned infix_propscount;
263
264 typedef struct {
265   const char *name;
266   unsigned filenum;
267   unsigned startline;
268   unsigned start_x;
269   offset start_PC;
270   const char *localnames[15];
271   unsigned end_line;
272   unsigned end_x;
273   offset end_PC;
274 } routineref;
275
276 static routineref *infix_routines;
277 static unsigned infix_routinescount;
278
279 typedef struct {
280   unsigned routine;
281   unsigned filenum;
282   unsigned line;
283   unsigned x;
284   offset PC;
285 } infix_sequence;
286
287 static infix_sequence *infix_linerefs;
288 static unsigned infix_linerefscount;
289
290
291 static offset code_start = 0;
292 static offset string_start = 0;
293
294 static int infix_compare_linerefs(const void *a, const void *b)
295 {
296   const infix_sequence *A = (const infix_sequence *) a;
297   const infix_sequence *B = (const infix_sequence *) b;
298   if(A->PC < B->PC)
299     return -1;
300   if(A->PC > B->PC)
301     return 1;
302
303   if(A->line < B->line)
304     return -1;
305   if(A->line > B->line)
306     return 1;
307   return 0;
308 }
309
310 static BOOL infix_load_records(strid_t infix)
311 {
312   unsigned n;
313
314   for(;;) {
315     glui32 record_type = glk_get_char_stream(infix);
316     glui32 record_data[64];
317     glui32 more_data[4];
318     if(record_type > MAX_DBR) {
319       glk_stream_close(infix, NULL);
320       n_show_error(E_DEBUG, "unknown record type", record_type);
321       return FALSE;
322     }
323
324     fillstruct(infix, record_info[record_type], record_data, infix_add_string);
325     
326     switch(record_type) {
327     case EOF_DBR:
328       if(infix_linerefs && infix_routines && code_start) {
329         for(n = 0; n < infix_routinescount; n++) {
330           infix_routines[n].start_PC += code_start;
331           infix_routines[n].end_PC += code_start;
332         }
333
334         n_qsort(infix_linerefs, infix_linerefscount, sizeof(*infix_linerefs),
335                 infix_compare_linerefs);
336
337         for(n = 0; n < infix_linerefscount; n++)
338           infix_linerefs[n].PC += code_start;
339       }
340       return TRUE;
341     case FILE_DBR:
342       if(infix_files) {
343         infix_files[record_data[0]] = infix_load_file_info(infix_stringdata + record_data[2]);
344       }
345       if(record_data[0] >= infix_filescount)
346         infix_filescount = record_data[0] + 1;
347       break;
348     case CLASS_DBR:
349       break;
350     case OBJECT_DBR:
351       if(infix_objects) {
352         infix_objects[record_data[0]] = infix_stringdata + record_data[1];
353       }
354       if(record_data[0] >= infix_objectscount)
355         infix_objectscount = record_data[0] + 1;
356       break;
357     case GLOBAL_DBR:
358       if(infix_globals) {
359         infix_globals[record_data[0]] = infix_stringdata + record_data[1];
360       }
361       if(record_data[0] >= infix_globalscount)
362         infix_globalscount = record_data[0] + 1;
363       break;
364     case ARRAY_DBR:
365       if(infix_arrays) {
366       }
367       infix_arrayscount++;
368       break;
369     case ATTR_DBR:
370       if(infix_attrs) {
371         infix_attrs[record_data[0]] = infix_stringdata + record_data[1];
372       }
373       if(record_data[0] >= infix_attrscount)
374         infix_attrscount = record_data[0] + 1;
375       break;
376     case PROP_DBR:
377       if(infix_props) {
378         infix_props[record_data[0]] = infix_stringdata + record_data[1];
379       }
380       if(record_data[0] >= infix_propscount)
381         infix_propscount = record_data[0] + 1;
382       break;
383     case FAKE_ACTION_DBR:
384       break;
385     case ACTION_DBR:
386       break;
387     case HEADER_DBR:
388       glk_stream_set_position(current_zfile, 0, seekmode_Start);
389       for(n = 0; n < 64; n++) {
390         unsigned c = (unsigned char) glk_get_char_stream(current_zfile);
391         if(record_data[n] != c)  {
392           n_show_error(E_DEBUG, "infix file does not match current file", n);
393           return FALSE;
394         }
395       }
396       break;
397     case ROUTINE_DBR:
398       if(infix_routines) {
399         infix_routines[record_data[0]].filenum   = record_data[1];
400         infix_routines[record_data[0]].startline = record_data[2];
401         infix_routines[record_data[0]].start_x   = record_data[3];
402         infix_routines[record_data[0]].start_PC  = record_data[4];
403         infix_routines[record_data[0]].name = infix_stringdata +
404                                                                 record_data[5];
405       }
406
407       if(infix_routines)
408         for(n = 0; n < 15; n++)
409           infix_routines[record_data[0]].localnames[n] = NULL;
410
411       for(n = 0; n < 16; n++) {
412         if(!glk_get_char_stream(infix))
413           break;
414         if(n == 15)
415           return FALSE;
416         glk_stream_set_position(infix, -1, seekmode_Current);
417         fillstruct(infix, local_names_info, more_data, infix_add_string);
418         if(infix_routines) {
419           infix_routines[record_data[0]].localnames[n] = infix_stringdata +
420                                                                   more_data[0];
421         }
422       }
423       if(record_data[0] >= infix_routinescount)
424         infix_routinescount = record_data[0] + 1;
425       break;
426     case LINEREF_DBR:
427       for(n = 0; n < record_data[1]; n++) {
428         fillstruct(infix, sequence_point_info, more_data, NULL);
429         if(infix_linerefs) {
430           infix_linerefs[infix_linerefscount].routine = record_data[0];
431           infix_linerefs[infix_linerefscount].filenum = more_data[0];
432           infix_linerefs[infix_linerefscount].line    = more_data[1];
433           infix_linerefs[infix_linerefscount].x       = more_data[2];
434           infix_linerefs[infix_linerefscount].PC      = more_data[3] +
435                                        infix_routines[record_data[0]].start_PC;
436         }
437         infix_linerefscount++;
438       }
439       break;
440     case ROUTINE_END_DBR:
441       if(infix_routines) {
442         infix_routines[record_data[0]].end_line = record_data[2];
443         infix_routines[record_data[0]].end_x    = record_data[3];
444         infix_routines[record_data[0]].end_PC   = record_data[4];
445       }
446       if(record_data[0] >= infix_routinescount)
447         infix_routinescount = record_data[0] + 1;
448       break;
449     case MAP_DBR:
450       for(;;) {
451         if(!glk_get_char_stream(infix))
452           break;
453         glk_stream_set_position(infix, -1, seekmode_Current);
454         fillstruct(infix, map_info, more_data, infix_add_string);
455         if(infix_stringdata) {
456           if(n_strcmp(infix_stringdata + more_data[0], "code area") == 0)
457             code_start = more_data[1];
458           if(n_strcmp(infix_stringdata + more_data[0], "strings area") == 0)
459             string_start = more_data[1];
460         }
461       }
462       break;
463     }     
464   }
465 }
466
467
468 BOOL init_infix(strid_t infix)
469 {
470   if(!infix && (infix_props || infix_attrs))
471     return FALSE;
472
473   kill_infix();
474
475   /* Inform 6.10+ has run-time symbols (techman 9.6) */
476   if(!infix && z_memory && prop_table_end
477      && (z_memory[HD_INFORMVER] == '6' && z_memory[HD_INFORMVER + 2] >= '1')) {
478     zword addr;
479     unsigned i;
480     glui32 *prop_names, *attr_names;
481
482     /* Assume no strings before end of property table */
483     string_start = prop_table_end;
484     
485     for(addr = prop_table_end+1; LOWORD(addr); addr+=ZWORD_SIZE)
486       ;
487     addr+=ZWORD_SIZE;
488
489     infix_propscount = LOWORD(addr) - 1; addr+=ZWORD_SIZE;
490     prop_names = (glui32 *) n_calloc(sizeof(*prop_names), infix_propscount+1);
491     for(i = 1; i <= infix_propscount; i++) {
492       prop_names[i] = infix_add_zstring(LOWORD(addr));
493       addr += ZWORD_SIZE;
494     }
495     
496     infix_attrscount = 48;
497     attr_names = (glui32 *) n_calloc(sizeof(*prop_names), infix_propscount);
498     for(i = 0; i <= 47; i++) {
499       attr_names[i] = infix_add_zstring(LOWORD(addr));
500       addr += ZWORD_SIZE;
501     }
502
503     infix_props = (char **) n_calloc(sizeof(*infix_props), infix_propscount+1);
504     infix_attrs = (char **) n_calloc(sizeof(*infix_attrs), infix_attrscount);
505     for(i = 1; i <= infix_propscount; i++) {
506       if(prop_names[i] != 0xffffffffL)
507         infix_props[i] = infix_stringdata + prop_names[i];
508     }
509     for(i = 0; i <= 47; i++) {
510       if(attr_names[i] != 0xffffffffL)
511         infix_attrs[i] = infix_stringdata + attr_names[i];
512     }
513
514     n_free(prop_names);
515     n_free(attr_names);
516     
517     return TRUE;
518   }
519
520   if(!infix)
521     return FALSE;
522   
523   glk_stream_set_position(infix, 0, seekmode_Start);
524   if((glk_get_char_stream(infix) != 0xDE) ||
525      (glk_get_char_stream(infix) != 0xBF) ||
526      (glk_get_char_stream(infix) != 0x00) ||
527      (glk_get_char_stream(infix) != 0x00)) {
528     glk_stream_close(infix, NULL);
529     n_show_error(E_DEBUG, "unknown version or not an infix file", 0);
530     return FALSE;
531   }
532
533   /* ignore inform version number */
534   glk_stream_set_position(infix, 6, seekmode_Start);
535
536   /* Calculate space requirements */
537   infix_load_records(infix);
538
539   /* Malloc required memory */
540   infix_stringdata   = (char *) n_calloc(sizeof(*infix_stringdata),
541                                          infix_strlen);
542   infix_strlen       = 0;
543   infix_files        = (infix_file *) n_calloc(sizeof(*infix_files),
544                                                infix_filescount);
545   infix_filescount   = 0;
546   infix_objects      = (char **) n_calloc(sizeof(*infix_objects),
547                                           infix_objectscount);
548   infix_objectscount = 0;
549   infix_globals      = (char **) n_calloc(sizeof(*infix_globals),
550                                           infix_globalscount);
551   infix_globalscount = 0;
552   infix_attrs        = (char **) n_calloc(sizeof(*infix_attrs),
553                                           infix_attrscount);
554   infix_attrscount   = 0;
555   infix_props        = (char **) n_calloc(sizeof(*infix_props),
556                                           infix_propscount);
557   infix_propscount   = 0;
558   infix_routines     = (routineref *) n_calloc(sizeof(*infix_routines),
559                                                infix_routinescount);
560   infix_routinescount= 0;
561   infix_linerefs     = (infix_sequence *) n_calloc(sizeof(*infix_linerefs),
562                                                    infix_linerefscount);
563   infix_linerefscount= 0;
564
565   glk_stream_set_position(infix, 6, seekmode_Start);
566   infix_load_records(infix);
567   
568   return TRUE;
569 }
570
571
572 void kill_infix(void)
573 {
574   unsigned i;
575
576   n_free(infix_stringdata);
577   infix_stringdata = 0;
578   infix_strlen = 0;
579
580   if(infix_files) {
581     for(i = 0; i < infix_filescount; i++)
582       infix_unload_file_info(&infix_files[i]);
583     n_free(infix_files);
584   }
585   infix_files = 0;
586   infix_filescount = 0;
587
588   n_free(infix_objects);
589   infix_objects = 0;
590   infix_objectscount = 0;
591
592   n_free(infix_globals);
593   infix_globals = 0;
594   infix_globalscount = 0;
595
596   n_free(infix_attrs);
597   infix_attrs = 0;
598   infix_attrscount = 0;
599
600   n_free(infix_props);
601   infix_props = 0;
602   infix_propscount = 0;
603
604   n_free(infix_routines);
605   infix_routines = 0;
606   infix_routinescount = 0;
607
608   n_free(infix_linerefs);
609   infix_linerefs = 0;
610   infix_linerefscount = 0;
611 }
612
613
614 void infix_print_znumber(zword blah)
615 {
616   set_glk_stream_current();
617   g_print_znumber(blah);
618 }
619
620 void infix_print_offset(zword blah)
621 {
622   set_glk_stream_current();
623   g_print_number(blah);
624 }
625
626 void infix_print_number(zword blah)
627 {
628   set_glk_stream_current();
629   g_print_number(blah);
630 }
631
632 void infix_print_char(int blah)
633 {
634   if(blah == 13)
635     blah = 10;
636 #ifdef STDOUT_DEBUG
637   fputc(blah, stdout);
638 #else
639   set_glk_stream_current();
640   /* We don't do a style-set because of bugs in zarf Glks */
641   glk_put_char((unsigned char) blah);
642 #endif
643 }
644
645 void infix_print_fixed_char(int blah)
646 {
647   if(blah == 13)
648     blah = 10;
649 #ifdef STDOUT_DEBUG
650   fputc(blah, stdout);
651 #else
652   set_glk_stream_current();
653   glk_set_style(style_Preformatted);
654   glk_put_char((unsigned char) blah);
655   glk_set_style(style_Normal);
656 #endif
657 }
658
659 void infix_print_string(const char *blah)
660 {
661   while(*blah)
662     infix_print_char(*blah++);
663 }
664
665 void infix_print_fixed_string(const char *blah)
666 {
667   while(*blah)
668     infix_print_fixed_char(*blah++);
669 }
670
671 void infix_get_string(char *dest, int maxlen)
672 {
673 #ifdef STDOUT_DEBUG
674   fgets(dest, maxlen, stdin);
675 #else
676   unsigned char t;
677   int len;
678   len = z_read(0, dest, maxlen - 1, 0, 0, 0, 0, &t);
679   dest[len] = 0;
680 #endif
681 }
682
683 void infix_get_val(z_typed *val)
684 {
685   switch(val->t) {
686   case Z_LOCAL:     val->v = frame_get_var(val->o, val->p + 1); break;
687   case Z_GLOBAL:    val->v = get_var(val->o + 0x10); break;
688   case Z_BYTEARRAY: val->v = LOBYTE(val->o + val->p); break;
689   case Z_WORDARRAY: val->v = LOWORD(val->o + ZWORD_SIZE * val->p); break;
690   case Z_OBJPROP:   val->v = infix_get_prop(val->o, val->p); break;
691   default: ;
692   }
693 }
694
695 void infix_assign(z_typed *dest, zword val)
696 {
697   switch(dest->t) {
698   case Z_LOCAL:     frame_set_var(dest->o, dest->p + 1, val); break;
699   case Z_BYTEARRAY: LOBYTEwrite(dest->o + dest->p, val); break;
700   case Z_WORDARRAY: LOWORDwrite(dest->o + ZWORD_SIZE * dest->p, val); break;
701   case Z_OBJPROP:   infix_put_prop(dest->o, dest->p, val); break;
702   default:          infix_print_string("assigning to non-lvalue\n"); break;
703   }
704   dest->v = val;
705 }
706
707 void infix_display(z_typed val)
708 {
709   unsigned n, i;
710   const char *name;
711
712   switch(val.t) {
713   default:
714     infix_print_znumber(val.v);
715     break;
716
717   case Z_BOOLEAN:
718     if(val.v)
719       infix_print_string("true");
720     else
721       infix_print_string("false");
722     break;
723
724   case Z_UNKNOWN:
725     val.t = Z_NUMBER;
726     infix_display(val);
727
728     name = debug_decode_number(val.v);
729
730     if(name) {
731       infix_print_char(' ');
732       infix_print_char('(');
733       infix_print_string(name);
734       infix_print_char(')');
735     }
736     break;
737
738   case Z_OBJECT:
739     infix_object_display(val.v);
740     break;
741
742   case Z_STRING:
743     infix_print_char('\"');
744     decodezscii(UNPACKS(val.v), infix_print_char);
745     infix_print_char('\"');
746     break;
747
748   case Z_ROUTINE:
749     if(!infix_routines)
750       return;
751
752     for(i = 0; i < infix_routinescount; i++) {
753       if(infix_routines[i].start_PC == UNPACKR(val.v)) {
754
755         infix_print_char('{');
756         for(n = 0; n < 15; n++) {
757           if(infix_routines[i].localnames[n]) {
758             infix_print_string(infix_routines[i].localnames[n]);
759             if(n < 14 && infix_routines[i].localnames[n+1])
760               infix_print_string(", ");
761           }
762         }
763         infix_print_string("} ");
764
765         infix_print_offset(infix_routines[i].start_PC);
766         infix_print_string(" <");
767         infix_print_string(infix_routines[i].name);
768         infix_print_char('>');
769         break;
770       }
771     }
772   }
773   infix_print_char('\n');
774 }
775
776
777 int infix_find_file(infix_file **dest, const char *name)
778 {
779   unsigned n;
780   if(infix_files) {
781     for(n = 0; n < infix_filescount; n++) {
782       if(infix_files[n].filename) {
783         unsigned len = n_strlen(infix_files[n].filename);
784         if(len &&
785            n_strncasecmp(infix_files[n].filename, name, len) == 0 &&
786            (name[len] == ' ' || name[len] == ':' || name[len] == 0)) {
787           *dest = &infix_files[n];
788           return len;
789         }
790       }
791     }
792   }
793   return 0;
794 }
795
796
797 BOOL infix_find_symbol(z_typed *val, const char *name, int len)
798 {
799   unsigned n;
800   if(infix_routines) {
801     infix_location location;
802     if(infix_decode_PC(&location, frame_get_PC(infix_selected_frame))) {
803       for(n = 0; n < 15; n++) {
804         if(n_strmatch(infix_routines[location.func_num].localnames[n], name, len)) {
805           val->t = Z_LOCAL;
806           val->o = infix_selected_frame;
807           val->p = n;
808           infix_get_val(val);
809           return TRUE;
810         }
811       }
812     }
813   }
814   if(infix_objects)
815     for(n = 0; n < infix_objectscount; n++) {
816       if(n_strmatch(infix_objects[n], name, len)) {
817         val->t = Z_OBJECT;
818         val->v = n;
819         return TRUE;
820       }
821     }
822   if(infix_globals) 
823     for(n = 0; n < infix_globalscount; n++) {
824       if(n_strmatch(infix_globals[n], name, len)) {
825         val->t = Z_WORDARRAY;
826         val->o = z_globaltable;
827         val->p = n;
828         infix_get_val(val);
829         return TRUE;
830       }
831   }
832   if(infix_arrays)
833     for(n = 0; n < infix_arrayscount; n++) {
834       if(n_strmatch(infix_arrays[n].name, name, len)) {
835         val->t = Z_NUMBER;
836         val->v = n;
837         return TRUE;
838       }
839     }
840   if(infix_attrs)
841     for(n = 0; n < infix_attrscount; n++) {
842       if(n_strmatch(infix_attrs[n], name, len)) {
843         val->t = Z_NUMBER;
844         val->v = n;
845         return TRUE;
846       }
847     }
848   if(infix_props)
849     for(n = 0; n < infix_propscount; n++) {
850       if(n_strmatch(infix_props[n], name, len)) {
851         val->t = Z_NUMBER;
852         val->v = n;
853         return TRUE;
854       }
855     }
856   if(infix_routines)
857     for(n = 0; n < infix_routinescount; n++) {
858       if(n_strmatch(infix_routines[n].name, name, len)) {
859         val->t = Z_ROUTINE;
860         val->v = PACKR(infix_routines[n].start_PC);
861         return TRUE;
862       }
863     }
864   return FALSE;
865 }
866
867 static char infix_temp_string_buffer[45];
868 static unsigned infix_temp_strlen;
869
870 static void infix_temp_string_build(int ch)
871 {
872   infix_temp_string_buffer[infix_temp_strlen] = ch;
873   infix_temp_strlen++;
874   if(infix_temp_strlen > 40) {
875     infix_temp_strlen = 43;
876     infix_temp_string_buffer[40] = '.';
877     infix_temp_string_buffer[41] = '.';
878     infix_temp_string_buffer[42] = '.';
879   }
880 }
881
882
883 const char *infix_get_name(z_typed val)
884 {
885   unsigned i;
886   if(!infix_stringdata)
887     return NULL;
888   switch(val.t) {
889   case Z_GLOBAL:
890     if(val.o < infix_globalscount)
891       return infix_globals[val.o];
892     break;
893   case Z_OBJECT:
894     if(val.v < infix_objectscount)
895       return infix_objects[val.v];
896     break;
897   case Z_ATTR:
898     if(val.v < infix_attrscount)
899       return infix_attrs[val.v];
900     break;
901   case Z_PROP:
902     if(val.v < infix_propscount)
903       return infix_props[val.v];
904     break;
905   case Z_ROUTINE:
906     for(i = 0; i < infix_routinescount; i++) {
907       if(infix_routines[i].start_PC == UNPACKR(val.v))
908         return infix_routines[i].name;
909     }
910     break;
911   case Z_STRING:
912     if(UNPACKS(val.v) < string_start)
913       break;
914     if(string_start < code_start && UNPACKS(val.v) >= code_start)
915       break;
916     if(UNPACKS(val.v) >= total_size)
917       break;
918
919     /* Assume every string except the first is preceded by zeroed padding until
920        an end-of-string marker */
921     if(UNPACKS(val.v) != string_start) {
922       offset addr = UNPACKS(val.v) - 2;
923       zword s;
924       while((s = HIWORD(addr)) == 0)
925         addr -= 2;
926       if(!(s & 0x8000))
927         break;
928     }
929
930     infix_temp_string_buffer[0] = '\"';
931     infix_temp_strlen = 1;
932     testing_string = TRUE; string_bad = FALSE;
933     decodezscii(UNPACKS(val.v), infix_temp_string_build);
934     testing_string = FALSE;
935     if(string_bad)
936       break;
937     infix_temp_string_buffer[infix_temp_strlen] = '\"';
938     infix_temp_string_buffer[infix_temp_strlen + 1] = 0;
939     return infix_temp_string_buffer;
940   case Z_ARRAY:
941     if(val.v < infix_arrayscount)
942       return infix_arrays[val.v].name;
943     break;
944
945   default: ;
946   }
947   return NULL;
948 }
949
950
951 /* We search through linerefs very often so use binary search for speed */
952
953 static infix_sequence *infix_search_linerefs(offset thisPC)
954 {
955   unsigned n;
956   int top = 0;
957   int bottom = infix_linerefscount-1;
958   int middle = (top + bottom) / 2;
959
960   if(!infix_linerefs)
961     return NULL;
962
963   do {
964     middle = (top + bottom) / 2;
965     if(thisPC < infix_linerefs[middle].PC)
966       bottom = middle - 1;
967     else if(thisPC > infix_linerefs[middle].PC)
968       top = middle + 1;
969     else
970       break;
971   } while(top <= bottom);
972
973   /* If the PC is in the middle of a line, we want to display that line. In
974      this case, PC will be greater than infix_linerefs[middle].PC, so just let
975      it go. Otherwise, we want to look at the previous lineref, so subtract
976      one (or more)
977   */
978
979   while(middle && thisPC < infix_linerefs[middle].PC)
980     middle--;
981
982
983   /* Make sure PC is inside the function the lineref says it is; if so, then
984      we're done */
985
986   n = infix_linerefs[middle].routine;
987   if(thisPC >= infix_routines[n].start_PC &&
988      thisPC <= infix_routines[n].end_PC)
989     return &infix_linerefs[middle];
990
991   return NULL;
992 }
993
994
995 BOOL infix_decode_PC(infix_location *dest, offset thisPC)
996 {
997   infix_sequence *n = infix_search_linerefs(thisPC);
998
999   if(!n) {     /* No sequence point found - return a function */
1000     unsigned i;
1001
1002     if(!infix_routines)
1003       return FALSE;
1004
1005     for(i = 0; i < infix_routinescount; i++) {
1006       if(thisPC >= infix_routines[i].start_PC &&
1007          thisPC <= infix_routines[i].end_PC) {
1008         
1009         routineref *r = &infix_routines[i];
1010         dest->file = &infix_files[r->filenum];
1011         dest->line_num = r->startline;
1012         dest->line_x = r->start_x;
1013         dest->func_name = r->name;
1014         dest->func_num = i;
1015         dest->thisPC = r->start_PC;
1016
1017         return TRUE;
1018       }
1019     }
1020
1021     /* Not in a function. Give up. */
1022     return FALSE;
1023   }
1024
1025
1026   dest->file      = &infix_files[n->filenum];
1027   dest->line_num  = n->line;
1028   dest->line_x    = n->x;
1029   dest->func_name = infix_routines[n->routine].name;
1030   dest->func_num  = n->routine;
1031   dest->thisPC    = n->PC;
1032
1033   return TRUE;
1034 }
1035
1036
1037 BOOL infix_decode_fileloc(infix_location *dest, const char *filename,
1038                           unsigned line_num)
1039 {
1040   unsigned n;
1041   if(!infix_linerefs)
1042     return FALSE;
1043   for(n = 0; n < infix_linerefscount; n++) {
1044     if(infix_linerefs[n].line == line_num &&
1045        n_strcmp(infix_files[infix_linerefs[n].filenum].filename, filename) == 0) {
1046
1047       dest->file      = &infix_files[infix_linerefs[n].filenum];
1048       dest->line_num  = infix_linerefs[n].line;
1049       dest->line_x    = infix_linerefs[n].x;
1050       dest->func_name = infix_routines[infix_linerefs[n].routine].name;
1051       dest->func_num  = infix_linerefs[n].routine;
1052       dest->thisPC    = infix_linerefs[n].PC;
1053       return TRUE;
1054     }
1055   }
1056   dest->thisPC = 0;
1057   return FALSE;
1058 }
1059
1060
1061 BOOL infix_decode_func_name(infix_location *dest, const char *file_name,
1062                             const char *func_name)
1063 {
1064   unsigned n;
1065   if(!infix_linerefs)
1066     return FALSE;
1067   for(n = 0; n < infix_linerefscount; n++) {
1068     if(n_strcmp(infix_files[infix_linerefs[n].filenum].filename, file_name) == 0) {
1069       if(!file_name || n_strcmp(infix_routines[infix_linerefs[n].filenum].name,
1070                                 func_name) == 0) {
1071
1072         dest->file      = &infix_files[infix_linerefs[n].filenum];
1073         dest->line_num  = infix_linerefs[n].line;
1074         dest->line_x    = infix_linerefs[n].x;
1075         dest->func_name = infix_routines[infix_linerefs[n].routine].name;
1076         dest->func_num  = infix_linerefs[n].routine;
1077         dest->thisPC    = infix_linerefs[n].PC;
1078         return TRUE;
1079       }
1080     }
1081   }
1082   return FALSE;
1083 }
1084
1085
1086 void infix_gprint_loc(int frame, offset thisPC)
1087 {
1088   infix_location boo;
1089   offset loc;
1090   BOOL found_frame;
1091   unsigned numlocals;
1092   unsigned n;
1093
1094   if(!thisPC) {
1095     loc = frame_get_PC(frame);
1096     numlocals = stack_get_numlocals(frame);
1097   } else {
1098     loc = thisPC;
1099     numlocals = 0;
1100   }
1101
1102   found_frame = infix_decode_PC(&boo, loc);
1103
1104   infix_print_offset(loc);
1105
1106   if(found_frame) {
1107     infix_print_string(" in ");
1108     infix_print_string(boo.func_name);
1109   }
1110
1111   if(!thisPC) {
1112     infix_print_string(" (");
1113     
1114     for(n = 0; n < numlocals; n++) {
1115       const char *name;
1116       if(n)
1117         infix_print_string(", ");
1118       if(found_frame) {
1119         infix_print_string(infix_routines[boo.func_num].localnames[n]);
1120         infix_print_char('=');
1121       }
1122       infix_print_znumber(frame_get_var(frame, n + 1));
1123       name = debug_decode_number(frame_get_var(frame, n + 1));
1124       if(name) {
1125         infix_print_char(' ');
1126         infix_print_string(name);
1127       }
1128     }
1129
1130     infix_print_string(")");
1131   }
1132
1133   if(found_frame) {
1134     infix_print_string(" at ");
1135     if(boo.file->filename)
1136       infix_print_string(boo.file->filename);
1137     else
1138       infix_print_string("<No file>");
1139     infix_print_char(':');
1140     infix_print_number(boo.line_num);
1141   }
1142   
1143   infix_print_char('\n');
1144
1145   if(found_frame && !thisPC)
1146     infix_file_print_line(boo.file, boo.line_num);
1147 }
1148
1149
1150 void infix_list_files(void)
1151 {
1152   unsigned i;
1153   for(i = 0; i < infix_filescount; i++) {
1154     if(i)
1155       infix_print_string(", ");
1156     infix_print_string(infix_files[i].filename);
1157   }
1158 }
1159
1160 #else
1161
1162 BOOL init_infix(strid_t unused)
1163 {
1164   n_show_error(E_DEBUG, "debugging code not compiled in", 0);
1165   return FALSE;
1166 }
1167
1168
1169 #endif
1170