1 #ifdef RCSID
2 static char RCSid[] =
3 "$Header: d:/cvsroot/tads/TADS2/unix/dbgu.c,v 1.3 1999/07/11 00:46:39 MJRoberts Exp $";
4 #endif
5 
6 /* Copyright (c) 1992, 2002 Michael J. Roberts.  All Rights Reserved. */
7 /*
8 Name
9   dbgu.c - user interface for debugger
10 Function
11   Implements the user interface for the command-line version of the
12   debugger.  This portion of the debugger performs only user-interface
13   functions; the UI-indenpendent section actually performs most of
14   the work of debugging.
15 Notes
16   None
17 Modified
18   04/12/92 MJRoberts     - creation
19 */
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <ctype.h>
25 #include "os.h"
26 #include "dbg.h"
27 #include "tio.h"
28 #include "obj.h"
29 #include "prp.h"
30 #include "tok.h"
31 #include "run.h"
32 #include "lin.h"
33 #include "linf.h"
34 #include "osdbg.h"
35 
36 #ifdef UNIX
37 #include "osdbg_un.h"
38 
39 #define sdesc_color text_normal_color
40 extern int watchpoint_color;
41 extern int breakpoint_color;
42 extern int filename_color;
43 #endif
44 
45 #ifdef MICROSOFT
46 extern char   **__argv;
47 #define ARG0    __argv[0]
48 #endif /* MICROSOFT */
49 
50 #ifdef __IBMC__
51 extern char   **_argv;
52 void osdbgpt( oswdef *, char *fmt, ... );
53 #endif /* __IBMC__ */
54 
55 #ifdef __EMX__
56 # define ARG0   (char *)0
57 #endif /* __EMX__ */
58 
59 #ifndef ARG0
60 # define ARG0   _argv[0]
61 #endif /* !ARG0 */
62 
63 /* some special characters for PC's */
64 #define DBGUHLIN   196                                   /* horizontal line */
65 #define DBGARROW    16                              /* right-pointing arrow */
66 #define DBGBPCHAR   15                    /* breakpoint indicator character */
67 
68 #ifdef UNIX
69 #undef DBGUHLIN
70 #undef DBGARROW
71 #undef DBGBPCHAR
72 
73 #define DBGUHLIN '-'
74 #define DBGARROW '>'
75 #define DBGBPCHAR '*'
76 #endif
77 
78 /* debug text, status, source, and watch windows */
79 static oswdef dbgtxtwin;
80 static oswdef dbgstawin;
81 static oswdef dbgsrcwin;
82 static oswdef dbgwatwin;
83 
84 /* space for lindef containing arbitrary file not part of game source */
85 static char filesrc[sizeof(linfdef) + 128];
86 
87 /* location in source file of top current source window and current line */
88 static uchar dbgsrctop[LINLLNMAX];         /* current display seek location */
89 static uchar dbgsrccsr[LINLLNMAX];                   /* current cursor line */
90 static uchar dbgsrcloc[LINLLNMAX];           /* current source line in file */
91 static uchar dbgsrchome[LINLLNMAX];       /* seek location of "home" screen */
92 
93 /* lindef for current source file */
94 static lindef *dbgsrclin;            /* lindef for currently displayed file */
95 static lindef *dbgsrclinhome;                      /* lindef of "home" file */
96 
97 /* color for current line/breakpoint line in source window */
98 static int dbgcurcolor = 30;
99 static int dbgbpcolor = 0x47;
100 
101 /* private breakpoint data - maintained with dbgbpeach() */
102 static int dbgbpcnt;                           /* number of breakpoints set */
103 typedef struct dbgubpdef dbgubpdef;
104 static struct dbgubpdef
105 {
106     int  dbgubpid;                                        /* line source id */
107     char dbgubpseek[LINLLNMAX];              /* seek position of breakpoint */
108 }
109 dbgbps[DBGBPMAX];                          /* seek locations of breakpoints */
110 
111 /* forward declarations */
112 static void dbgugetbp(/*_ dbgcxdef *ctx _*/);
113 
114 
115 /* move back or forward by one or more lines within a file */
116 static char *dbgbuf;
117 static size_t dbgbufsiz;
dbgmove(lin,cur,destlinep)118 static void dbgmove(lin, cur, destlinep)
119 lindef   *lin;
120 int       cur;
121 int      *destlinep;
122 {
123    int     dest = *destlinep;
124    char   *p;
125    size_t  max;
126    long    backup;
127    long    delta;
128    uchar   pos[LINLLNMAX];
129    uchar   prvpos[LINLLNMAX];
130 
131    /* find current location in line source */
132    lintell(lin, pos);
133 
134    if (cur < dest)
135    {
136       while (cur != dest)
137       {
138          max = linread(lin, dbgbuf, (int)dbgbufsiz);
139          if (max == 0) break;
140          for (delta = 0, p = dbgbuf ; p < dbgbuf + max && cur != dest ;
141               ++p, ++delta)
142             if (*p == '\n') ++cur;
143          linpadd(lin, pos, delta);
144       }
145    }
146    else
147    {
148       ++cur;
149       while (cur != dest)
150       {
151          backup = linofs(lin);
152          if (backup > dbgbufsiz) backup = dbgbufsiz;
153          memcpy(prvpos, pos, (size_t)sizeof(prvpos));
154          linpadd(lin, prvpos, -(long)dbgbufsiz);
155          linseek(lin, prvpos);
156          max = linread(lin, dbgbuf, backup);
157          if (max == 0) break;
158          p = dbgbuf + max;
159          delta = 0;
160          do
161          {
162             --p;
163             --delta;
164             if (*p == '\n') --cur;
165          } while (p > dbgbuf && cur != dest);
166          linpadd(lin, pos, delta);
167          if (linqtop(lin, prvpos)) break;
168       }
169       if (!linqtop(lin, pos)) linpadd(lin, pos, (long)1);
170       else cur = 1;
171    }
172 
173    linseek(lin, pos);
174    *destlinep = cur;
175 }
176 
177 /* show current source page */
dbgsrcshow(lin,win,top,loc)178 static void dbgsrcshow(lin, win, top, loc)
179 lindef   *lin;
180 oswdef   *win;
181 uchar    *top;
182 uchar    *loc;
183 {
184     int        y;
185     char       outbuf[128];
186     int        wid = dbgsrcwin.oswx2 - dbgsrcwin.oswx1 + 1;
187     extern     int sdesc_color, text_color;
188     char       prefix[3];
189     uchar      curloc[LINLLNMAX];
190     int        i;
191     dbgubpdef *bp;
192     int        thisid;
193 
194     thisid = lin ? lin->linid : 0;
195     prefix[2] = '\0';
196     osdbgclr(win);
197     for (y = win->oswy1 ; y <= win->oswy2 ; ++y)
198     {
199         win->oswx = win->oswx1;
200         win->oswy = y;
201         linftell(lin, curloc);
202 
203         /* special display if this is the currently executing line */
204         if (!memcmp(curloc, loc, lin->linlln) && lin == dbgsrclin)
205         {
206             win->oswcolor = dbgcurcolor;
207             prefix[1] = DBGARROW;
208         }
209         else
210         {
211             win->oswcolor = sdesc_color;
212             prefix[1] =  ' ';
213         }
214 
215         /* special display if there is a breakpoint at this line */
216         prefix[0] = ' ';         /* presume nothing special about this line */
217         for (i = dbgbpcnt, bp = dbgbps ; i ; ++bp, --i)
218         {
219 
220             if (!memcmp(bp->dbgubpseek, curloc, lin->linlln)
221                 && (bp->dbgubpid == thisid))
222             {
223                 prefix[0] = DBGBPCHAR;
224                 win->oswcolor = dbgbpcolor;
225                 break;
226             }
227         }
228 
229         /* display prefix with current execution/breakpoint info */
230         osdbgpt(win, "%s", prefix);
231 
232         /* display the line itself */
233         if (!lingets(lin, outbuf, sizeof(outbuf)))
234             break;
235         else
236         {
237             int l;
238 
239             l = strlen(outbuf);
240             while (l && outbuf[l-1] == '\n' || outbuf[l-1] == '\r')
241                 --l;
242 
243             if (l + 2 >= wid)
244                 outbuf[wid - 2] = '\0';
245             else
246             {
247                 outbuf[l] = '\0';
248                 if (prefix[0] == DBGBPCHAR)
249                 {
250                     /* breakpoint - pad line out with spaces to full width */
251                     while (l < wid - 2) outbuf[l++] = ' ';
252                     outbuf[l] = '\0';
253                 }
254             }
255             osdbgpt(win, "%s", outbuf);
256             if (y != win->oswy2) osdbgpt(win, "\n");
257         }
258     }
259 
260     /* restore normal color */
261     win->oswcolor = sdesc_color;
262 }
263 
264 /* search for a string in a line source */
dbgusearch(lin,pos,toppos,win,str)265 static void dbgusearch(lin, pos, toppos, win, str)
266 lindef   *lin;
267 uchar    *pos;                /* in: starting position; out: final position */
268 uchar    *toppos;                              /* location of top of screen */
269 oswdef   *win;
270 char     *str;
271 {
272     int dest;
273 
274     /* search is case-insensitive - convert search string to lowercase */
275     os_strlwr(str);
276 
277     /* seek to starting location, and read and discard current line */
278     linseek(lin, pos);
279     if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) return;
280 
281     for (;;)
282     {
283         lintell(lin, pos);
284         if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) break;
285         os_strlwr(dbgbuf);
286         if (strstr(dbgbuf, str)) break;
287     }
288 
289     /* move search expression to middle of screen */
290     linseek(lin, pos);
291     dest = 1;
292     dbgmove(lin, (win->oswy2 - win->oswy1)/2, &dest);
293     lintell(lin, toppos);
294     dbgsrcshow(lin, win, toppos, dbgsrcloc);
295 }
296 
297 /* redraw status line */
298 static char dbg_curstat[81];
dbgustat(ctx)299 static void dbgustat(ctx)
300 dbgcxdef *ctx;
301 {
302     VARUSED(ctx);
303 
304     dbgstawin.oswx = dbgstawin.oswx1;
305     dbgstawin.oswy = dbgstawin.oswy1;
306     osdbgpt(&dbgstawin, dbg_curstat);
307 }
308 
309 /* show the current line */
dbgulsho(ctx,buf)310 static void dbgulsho(ctx, buf)
311 dbgcxdef *ctx;
312 uchar    *buf;                     /* line information retrieved by dbglget */
313 {
314     lindef    *lin;
315     char       outbuf[128];
316     int        cur;
317     int        dest;
318     uchar      loc[LINLLNMAX];
319     int        y;
320     char       fnam[182];
321     int        i;
322 
323     /* search for a line source with this id */
324     for (lin = ctx->dbgcxlin ; lin ; lin = lin->linnxt)
325         if (lin->linid == *buf) break;
326     dbgswitch(&dbgsrclin, lin);
327     dbgsrclinhome = lin;
328 
329     if (!lin)
330     {
331         osdbgclr(&dbgsrcwin);
332         osdbgpt(&dbgsrcwin, "*** source line not found ***\n");
333         return;
334     }
335 
336     /* seek to line in file, then back up by half a screen to center line */
337     memcpy(loc, buf+1, lin->linlln);
338     linseek(lin, loc);
339     cur = (dbgsrcwin.oswy2 - dbgsrcwin.oswy1) / 2;
340     dest = 1;
341     dbgmove(lin, cur, &dest);
342     lintell(lin, dbgsrctop);
343     memcpy(dbgsrchome, dbgsrctop, (size_t)lin->linlln);
344     memcpy(dbgsrcloc, loc, (size_t)lin->linlln);
345     memcpy(dbgsrccsr, loc, (size_t)lin->linlln);
346     dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
347 
348     /* update status line to show current file */
349     for (i = 0 ; i < 5 ; ++i) dbg_curstat[i] = DBGUHLIN;
350 
351     linnam(lin, fnam);
352     sprintf(&dbg_curstat[5], "%s    ", fnam);
353     dbgwhere(ctx, &dbg_curstat[strlen(dbg_curstat)]);
354 
355     for (i = strlen(dbg_curstat) ;
356          i <= dbgstawin.oswx2 - dbgstawin.oswx1 ; ++i)
357         dbg_curstat[i] = DBGUHLIN;
358     dbg_curstat[i] = '\0';
359 
360     /* actually display the status line now */
361     dbgustat(ctx);
362 }
363 
364 /* display callback */
dbgudisp(ctx,str,strl)365 static void dbgudisp(ctx, str, strl)
366 void     *ctx;
367 char     *str;
368 int       strl;
369 {
370     oswdef *win = (oswdef *)ctx;
371     int     oldpg;
372 
373     oldpg = ossvpg(1);             /* display values on debugger video page */
374     osdbgpt(win, "%.*s", strl, str);
375     ossvpg(oldpg);                                 /* restore previous page */
376 }
377 
378 /* display an error message */
dbguerr(ctx,errno,msg)379 void dbguerr(ctx, errno, msg)
380 dbgcxdef *ctx;
381 int       errno;
382 char     *msg;
383 {
384     int oldpg;
385     char err_prefix[64];
386 
387     VARUSED(ctx);
388 
389     oldpg = ossvpg(1);                     /* figure out what page we're on */
390     ossvpg(oldpg);                                 /* restore previous page */
391     if (oldpg == 0) {                       /* if we're on the game page... */
392         sprintf(err_prefix, "[TADS-%d: ", errno);
393         os_printz(err_prefix);
394         os_printz(msg);
395         os_printz("]\n");
396     }
397     else
398         osdbgpt(&dbgtxtwin, "TADS-%d: %s\n", errno, msg);
399 }
400 
401 /* show user screen, wait for key hit, then change back to dbg screen */
dbguuser(ctx)402 static void dbguuser(ctx)
403 dbgcxdef *ctx;
404 {
405     VARUSED(ctx);
406 
407     ossvpg(0);
408     os_waitc();
409     ossvpg(1);
410 }
411 
412 /* execute a special keyboard command key */
dbgukbc(ctx,cmd)413 static int dbgukbc(ctx, cmd)
414 dbgcxdef *ctx;
415 char      cmd;
416 {
417     int       cur = dbgsrcwin.oswy2;
418     int       dest = cur;
419     uchar     newpos[LINLLNMAX];
420     int       scrht = dbgsrcwin.oswy2 - dbgsrcwin.oswy1 - 2;
421 
422     switch(cmd)
423     {
424     case CMD_F5:       /* show user screen */
425         dbguuser(ctx);
426         return(0);
427 
428     case CMD_TAB:      /* other screen */
429     case CMD_F7:       /* step into */
430     case CMD_F8:       /* step over */
431     case CMD_F9:       /* go */
432     case CMD_SCR:      /* help */
433         return(cmd);
434     }
435 
436     /* the remaining commands pertain to the source display only */
437     switch(cmd)
438     {
439     case CMD_CHOME:
440         if (dbgsrclinhome
441             && (dbgsrctop != dbgsrchome || dbgsrclin != dbgsrclinhome))
442         {
443             dbgswitch(&dbgsrclin, dbgsrclinhome);
444 
445             linseek(dbgsrclin, dbgsrchome);
446             memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln);
447             memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln);
448             dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
449         }
450         break;
451 
452     case CMD_TOP:
453         if (dbgsrctop != 0)
454         {
455             lingoto(dbgsrclin, LINGOTOP);
456             lintell(dbgsrclin, dbgsrccsr);
457             memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln);
458             dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
459         }
460         break;
461 
462     case CMD_BOT:
463         lingoto(dbgsrclin, LINGOEND);
464         dest -= scrht;
465         goto move_noseek;
466 
467     case CMD_UP:
468         --dest;
469         goto move_source;
470 
471     case CMD_DOWN:
472         ++dest;
473         goto move_source;
474 
475     case CMD_PGUP:
476         dest -= scrht;
477         goto move_source;
478 
479     case CMD_PGDN:
480         dest += scrht;
481     move_source:
482         linseek(dbgsrclin, dbgsrctop);
483     move_noseek:
484         dbgmove(dbgsrclin, cur, &dest);
485         lintell(dbgsrclin, newpos);
486         if (memcmp(dbgsrctop, newpos, dbgsrclin->linlln))
487         {
488             memcpy(dbgsrccsr, newpos, dbgsrclin->linlln);
489             memcpy(dbgsrctop, newpos, dbgsrclin->linlln);
490             dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
491         }
492         break;
493     }
494     return(0);
495 }
496 
497 /* get commands in source window */
dbgurunsrc(ctx)498 static int dbgurunsrc(ctx)
499 dbgcxdef *ctx;
500 {
501     int       y;
502     char      c;
503     oswdef   *win = &dbgsrcwin;
504     uchar     pos[LINLLNMAX];
505     uchar     newpos[LINLLNMAX];
506     int       dest;
507     int       cur;
508     int       scrht = win->oswy2 - win->oswy1 - 2;
509     char     *cond;
510     char      condbuf[128];
511 
512     /* figure out where we are in the source window */
513 find_srcpos:
514     linseek(dbgsrclin, dbgsrctop);
515     memcpy(pos, dbgsrctop, dbgsrclin->linlln);
516     for (y = win->oswy1 ; y < win->oswy2 ; ++y)
517     {
518         lintell(dbgsrclin, newpos);
519         if (!memcmp(dbgsrccsr, newpos, dbgsrclin->linlln)) break;
520         lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz);
521     }
522 
523     /* get and process commands */
524     for (;;)
525     {
526         ossdbgloc(y, 2);                          /* locate cursor properly */
527         cond = (char *)0;       /* no condition for breakpoints, by default */
528 
529         c = os_getc();
530         switch(c)
531         {
532         case '/':
533             return(CMD_UP);
534 
535         case '\0':
536             c = os_getc();
537             switch(c)
538             {
539             case CMD_TAB:
540             case CMD_EOF:
541                 return(0);
542 
543             case CMD_CHOME:
544                 if (dbgsrclinhome
545                     && (memcmp(dbgsrctop, dbgsrchome, dbgsrclin->linlln)
546                         || dbgsrclin != dbgsrclinhome))
547                 {
548                     dbgswitch(&dbgsrclin, dbgsrclinhome);
549 
550                     linseek(dbgsrclin, dbgsrchome);
551                     memcpy(pos, dbgsrchome, dbgsrclin->linlln);
552                     memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln);
553                     memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln);
554                     dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
555                     goto find_srcpos;
556                 }
557                 break;
558 
559             case CMD_UP:
560                 if (y > win->oswy1)
561                 {
562                     --y;
563                     goto find_cur_y;
564                 }
565                 else
566                 {
567                     dest = 1;
568                     cur = 2;
569                     goto move_source;
570                 }
571 
572             case CMD_DOWN:
573                 if (y < win->oswy2)
574                 {
575                     ++y;
576                     goto find_cur_y;
577                 }
578                 else
579                 {
580                     dest = 2;
581                     cur = 1;
582                     goto move_source;
583                 }
584 
585             case CMD_PGUP:
586                 cur = scrht;
587                 dest = 1;
588                 goto move_source;
589 
590             case CMD_PGDN:
591                 cur = 1;
592                 dest = scrht;
593             move_source:
594                 linseek(dbgsrclin, pos);
595             move_noseek:
596                 dbgmove(dbgsrclin, cur, &dest);
597                 lintell(dbgsrclin, newpos);
598                 if (memcmp(pos, newpos, dbgsrclin->linlln))
599                 {
600                     int i;
601 
602                     memcpy(dbgsrctop, newpos, dbgsrclin->linlln);
603                     memcpy(pos, newpos, dbgsrclin->linlln);
604                     dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc);
605 
606                     /* figure out where dbgsrccsr is */
607                 find_cur_y:
608                     linseek(dbgsrclin, pos);
609                     for (i = win->oswy1 ; i < y ; ++i)
610                         lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz);
611                     lintell(dbgsrclin, dbgsrccsr);
612                 }
613                 break;
614 
615             case CMD_TOP:
616                 if (pos != 0)
617                 {
618                     lingoto(dbgsrclin, LINGOTOP);
619                     lintell(dbgsrclin, pos);
620                     memcpy(dbgsrccsr, pos, dbgsrclin->linlln);
621                     memcpy(dbgsrctop, pos, dbgsrclin->linlln);
622                     y = win->oswy1;
623                     dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc);
624                 }
625                 break;
626 
627             case CMD_BOT:
628                 lingoto(dbgsrclin, LINGOEND);
629                 dest = 1;
630                 cur = scrht;
631                 goto move_noseek;
632 
633             case CMD_HOME:
634                 y = win->oswy1;
635                 goto find_cur_y;
636 
637             case CMD_END:
638                 y = win->oswy2;
639                 goto find_cur_y;
640 
641             case CMD_SF2:
642                 if (dbgsrclin)
643                 {
644                     osdbgpt(&dbgtxtwin, "\nCondition: ");
645                     while (osdbggts(&dbgtxtwin, condbuf, dbgukbc, ctx));
646                     cond = condbuf;
647                     /* FALLTHROUGH */
648                 }
649                 else break;
650 
651             case CMD_F2:
652                 if (dbgsrclin)
653                 {
654                     objnum objn;
655                     uint   ofs;
656                     int    newbp;
657                     char   nambuf[128];
658                     int    len;
659 
660                     linfind(dbgsrclin, dbgsrccsr, &objn, &ofs);
661 
662                     memcpy(nambuf, "<F2> ", (size_t)4);
663                     len = dbgnam(ctx, nambuf+4, TOKSTOBJ, objn);
664                     if (!memcmp(nambuf+4, "<UNKNOWN>", (size_t)len))
665                         len = dbgnam(ctx, nambuf+4, TOKSTFUNC, objn);
666                     if (cond)
667                     {
668                         memcpy(nambuf + 4 + len, " when ", (size_t)6);
669                         strcpy(nambuf + 10 + len, cond);
670                     }
671                     else
672                         nambuf[len + 4] = '\0';
673 
674                     (void)dbgbpat(ctx, objn, MCMONINV, ofs, &newbp, nambuf,
675                                   TRUE, cond, NULL);
676                     dbgugetbp(ctx);
677                 }
678                 break;
679 
680             case CMD_F5:
681                 dbguuser(ctx);
682                 break;
683 
684             case CMD_F7:
685             case CMD_F8:
686             case CMD_F9:
687                 return(c);
688             }
689         }
690     }
691 }
692 
693 /* callback for dbgugetp */
dbgubp(ctx,id,buf,bufsiz)694 static void dbgubp(ctx, id, buf, bufsiz)
695 dbgcxdef *ctx;
696 int       id;
697 uchar    *buf;
698 uint      bufsiz;
699 {
700     dbgubpdef *bp = &dbgbps[dbgbpcnt++];
701 
702     bp->dbgubpid = id;
703     memcpy(bp->dbgubpseek, buf, (size_t)bufsiz);
704 }
705 
706 /* reset private breakpoint data - call after setting/clearing bp */
dbgugetbp(ctx)707 static void dbgugetbp(ctx)
708 dbgcxdef *ctx;
709 {
710     dbgbpcnt = 0;
711     dbgbpeach(ctx, dbgubp, ctx);
712     if (dbgsrclin)
713     {
714         linseek(dbgsrclin, dbgsrctop);
715         dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
716     }
717 }
718 
719 /* output support for hidden output */
trchid()720 void trchid()
721 {
722     outflushn(1);
723     os_printz("---Hidden output---\n");
724 }
trcsho()725 void trcsho()
726 {
727     outflushn(1);
728     os_printz("---End of hidden output, normal output resumes---\n");
729 }
730 
731 /* update display of watchpoints */
dbguwxupd(ctx)732 static void dbguwxupd(ctx)
733 dbgcxdef *ctx;
734 {
735     dbgwatwin.oswy = dbgwatwin.oswy1;
736     dbgwatwin.oswx = dbgwatwin.oswx1;
737     if (dbgwatwin.oswy1 <= dbgwatwin.oswy2) osdbgclr(&dbgwatwin);
738     dbgwxupd(ctx, dbgudisp, &dbgwatwin);
739 }
740 
741 /* update window sizes to account for more/fewer watches */
dbguwxadd(ctx,cnt)742 static void dbguwxadd(ctx, cnt)
743 dbgcxdef *ctx;
744 int       cnt;
745 {
746     /* move status line up by the number of new watch lines */
747     dbgstawin.oswy1 -= cnt;
748     dbgstawin.oswy2 -= cnt;
749 
750     /* move bottom of code window up by the number of watch lines */
751     dbgsrcwin.oswy2 -= cnt;
752 
753     /* move top of watch window up by the number of new watch lines */
754     dbgwatwin.oswy1 -= cnt;
755 
756     /* update the status, source and watch windows */
757     dbgustat(ctx);
758     if (dbgsrclin)
759     {
760         linseek(dbgsrclin, dbgsrctop);
761         dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
762     }
763     dbguwxupd(ctx);
764 }
765 
766 /* flags whether a prompt is needed the next time we run command line */
767 static int  needprompt = TRUE;
768 
769 /* main command loop */
dbgucmd1(ctx)770 static void dbgucmd1(ctx)
771 dbgcxdef *ctx;
772 {
773     uchar       cmdbuf[128];
774     int         linid;
775     int         err;
776     int         disable;
777     int         bpnum;
778     char       *buf;
779     int         cmd;
780     int         i;
781     lindef     *lin;
782     static int  stay_in_src;
783     static int  first_search;
784     static char lastsearch[128];
785     int         level = ctx->dbgcxfcn - 1;
786 
787     dbglget(ctx, cmdbuf);                /* get information on current line */
788     dbgulsho(ctx, cmdbuf);                          /* display current line */
789     dbguwxupd(ctx);                            /* udpate watchpoint display */
790 
791     for (;;)
792     {
793         if (stay_in_src)
794             goto run_other_window;
795         else
796         {
797             if (needprompt) osdbgpt(&dbgtxtwin, "tdb> ");
798             cmd = osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx);
799         }
800 
801     process_cmd:
802         needprompt = (cmd == 0);   /* ordinary command ==> prompt next time */
803         switch(cmd)
804         {
805         case 0:
806             for (buf = cmdbuf ; isspace(*buf) ; ++buf);
807             break;
808 
809         case CMD_F7:
810             buf = "t";
811             break;
812 
813         case CMD_F8:
814             buf = "p";
815             break;
816 
817         case CMD_F9:
818             buf = "g";
819             break;
820 
821         case CMD_TAB:
822             buf = "\t";
823             break;
824 
825         case CMD_SCR:
826             buf = "?";
827             break;
828 
829         default:
830             continue;
831         }
832 
833         if (isupper(buf[0])) buf[0] = tolower(buf[0]);
834         switch(buf[0])
835         {
836         case 'g':
837             ctx->dbgcxflg &= ~(DBGCXFSS + DBGCXFSO);
838             return;
839 
840         case 't':
841             ctx->dbgcxflg |= DBGCXFSS;
842             ctx->dbgcxflg &= ~DBGCXFSO;
843             return;
844 
845         case 'p':
846             ctx->dbgcxsof = ctx->dbgcxdep; /* step until back at this depth */
847             ctx->dbgcxflg |= (DBGCXFSS + DBGCXFSO);
848             return;
849 
850         case 'e':
851             ossvpg(0);                /* eval expression on game video page */
852             err = dbgeval(ctx, buf+1, dbgudisp, &dbgtxtwin, level, TRUE);
853             ossvpg(1);                     /* return to debugger video page */
854             if (err) errlog(ctx->dbgcxerr, err);
855             dbguwxupd(ctx);                    /* udpate watchpoint display */
856             break;
857 
858         case 'c':                                           /* Call history */
859             if (isupper(buf[1])) buf[1] = tolower(buf[1]);
860             switch(buf[1])
861             {
862             case '+':
863                 ctx->dbgcxhstf = 0;
864                 ctx->dbgcxflg |= DBGCXFTRC;
865                 osdbgpt(&dbgtxtwin, "Call tracing activated.\n");
866                 break;
867 
868             case '-':
869                 ctx->dbgcxflg &= ~DBGCXFTRC;
870                 osdbgpt(&dbgtxtwin, "Call tracing disactivated.\n");
871                 break;
872 
873             case 'c':
874                 ctx->dbgcxhstf = 0;
875                 osdbgpt(&dbgtxtwin, "Call trace log cleared.\n");
876                 break;
877 
878             case '\0':
879             case ' ':
880                 {
881                     char *p;
882                     char *endp;
883 
884                     for (p = ctx->dbgcxhstp, endp = p + ctx->dbgcxhstf
885                          ; p < endp ; p += strlen(p) + 1)
886                         osdbgpt(&dbgtxtwin, "%s\n", p);
887                 }
888             }
889             break;
890 
891         case 'b':
892             if (isupper(buf[1])) buf[1] = tolower(buf[1]);
893             switch(buf[1])
894             {
895             case 'p':
896                 if (err = dbgbpset(ctx, buf+2, &bpnum))
897                     errlog(ctx->dbgcxerr, err);
898                 else
899                 {
900                     osdbgpt(&dbgtxtwin, "breakpoint %d set\n", bpnum);
901                     dbgugetbp(ctx);
902                 }
903                 break;
904 
905             case 'c':
906                 if (err = dbgbpdel(ctx, atoi(buf + 2)))
907                     errlog(ctx->dbgcxerr, err);
908                 else
909                 {
910                     osdbgpt(&dbgtxtwin, "breakpoint cleared\n");
911                     dbgugetbp(ctx);
912                 }
913                 break;
914 
915             case 'd':
916                 disable = TRUE;
917                 goto bp_enable_or_disable;
918             case 'e':
919                 disable = FALSE;
920             bp_enable_or_disable:
921                 if (err = dbgbpdis(ctx, atoi(buf+2), disable))
922                     errlog(ctx->dbgcxerr, err);
923                 else
924                     osdbgpt(&dbgtxtwin, "breakpoint %sabled\n",
925                             disable ? "dis" : "en");
926                 break;
927 
928             case 'l':
929                 dbgbplist(ctx, dbgudisp, &dbgtxtwin);
930                 break;
931 
932             default:
933                 osdbgpt(&dbgtxtwin, "invalid breakpoint command\n");
934                 break;
935             }
936             break;
937 
938         case 'w':
939             if (isupper(buf[1])) buf[1] = tolower(buf[1]);
940             switch(buf[1])
941             {
942             case 's':
943                 if (err = dbgwxset(ctx, buf+2, &bpnum, level))
944                     errlog(ctx->dbgcxerr, err);
945                 else
946                     dbguwxadd(ctx, 1);       /* adjust windows for a new wx */
947                 break;
948 
949             case 'd':
950                 if (err = dbgwxdel(ctx, atoi(buf + 2)))
951                     errlog(ctx->dbgcxerr, err);
952                 else
953                     dbguwxadd(ctx, -1);   /* adjust windows for one less wx */
954                 break;
955 
956             default:
957                 osdbgpt(&dbgtxtwin, "invalid watch command\n");
958                 break;
959             }
960             break;
961 
962         case 'f':
963             if (isupper(buf[1])) buf[1] = tolower(buf[1]);
964             switch(buf[1])
965             {
966             case 'l':
967                 for (i = 1, lin = ctx->dbgcxlin ; lin ;
968                      lin = lin->linnxt, ++i)
969                 {
970                     osdbgpt(&dbgtxtwin, "#%-2d   %s", i,
971                             ((linfdef *)lin)->linfnam);
972                     osdbgpt(&dbgtxtwin, "\n");
973                 }
974                 break;
975 
976             case 'v':
977                 if (atoi(buf + 2) == 0)
978                 {
979                     osfildef *fp;
980 
981                     /* file by name - read the filename */
982                     for (buf += 2 ; isspace(*buf) ; ++buf);
983                     if (!(fp = osfoprb(buf, OSFTBIN)))  /* dbj: right type?? */
984                         osdbgpt(&dbgtxtwin, "file not found\n");
985                     else
986                     {
987                         dbgswitch(&dbgsrclin, (lindef *)0);
988                         *(buf-1) = '\0';
989                         linfini2(ctx->dbgcxmem, (linfdef *)filesrc, buf-1,
990                                  (int)strlen(buf) + 2, fp, FALSE);
991                         dbgsrclin = (lindef *)filesrc;
992                         lintell(dbgsrclin, dbgsrctop);
993                         lintell(dbgsrclin, dbgsrccsr);
994                         dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop,
995                                    dbgsrcloc);
996                     }
997                     break;
998                 }
999 
1000                 /* file by number - look up in line source list */
1001                 for (i = 1, lin = ctx->dbgcxlin ; lin ;
1002                      lin = lin->linnxt, ++i)
1003                 {
1004                     if (i == atoi(buf + 2)) break;
1005                 }
1006 
1007                 if (lin)
1008                 {
1009                     lindef   *oldlin;
1010 
1011                     oldlin = dbgsrclin;
1012                     dbgswitch(&dbgsrclin, lin);
1013                     lingoto(dbgsrclin, LINGOTOP);
1014                     lintell(dbgsrclin, dbgsrccsr);
1015                     memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln);
1016                     dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
1017                 }
1018                 else
1019                     osdbgpt(&dbgtxtwin, "invalid file number\n");
1020                 break;
1021 
1022             default:
1023                 osdbgpt(&dbgtxtwin, "invalid file command\n");
1024                 break;
1025             }
1026             break;
1027 
1028         case '\0':
1029             break;
1030 
1031         case 'q':
1032             if (strcmp(buf, "quit"))
1033                 osdbgpt(&dbgtxtwin, "invalid debugger command\n");
1034             else
1035             {
1036                 ossvpg(0);                           /* return to game page */
1037                 errsig(ctx->dbgcxerr, ERR_RUNQUIT);          /* signal QUIT */
1038             }
1039             break;
1040 
1041         case 'k':
1042             dbgstktr(ctx, dbgudisp, &dbgtxtwin, level, FALSE, TRUE);
1043             break;
1044 
1045         case 'h':
1046             if (!stricmp(buf, "help"))
1047                 goto do_help;
1048             else
1049             {
1050                 extern int dbghid;
1051 
1052                 dbghid = !dbghid;
1053                 osdbgpt(&dbgtxtwin, "Hidden text will be %s.\n",
1054                         dbghid ? "displayed" : "suppressed");
1055             }
1056             break;
1057 
1058         case '\\':
1059             dbguuser(ctx);
1060             break;
1061 
1062         case '/':
1063             ++buf;                                     /* skip the '/' part */
1064             strcpy(lastsearch, buf);
1065         another_search:
1066             dbgusearch(dbgsrclin, dbgsrccsr, dbgsrctop,
1067                        &dbgsrcwin, buf);
1068             needprompt = TRUE;
1069             goto run_other_window;
1070 
1071         case '\t':
1072             needprompt = FALSE;
1073             first_search = TRUE;
1074         run_other_window:
1075             cmd = dbgurunsrc(ctx);
1076             if (cmd == CMD_UP)               /* special return for "search" */
1077             {
1078                 char newbuf[128];
1079 
1080                 if (first_search)
1081                 {
1082                     osdbgpt(&dbgtxtwin, "\n");
1083                     first_search = FALSE;
1084                 }
1085 
1086                 /* present default if there is one */
1087                 if (lastsearch[0])
1088                     osdbgpt(&dbgtxtwin, "search [%s]: ", lastsearch);
1089                 else
1090                     osdbgpt(&dbgtxtwin, "search: ");
1091 
1092                 /* ignore command keys from search call */
1093                 while (osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx)) ;
1094 
1095                 /* apply default if appropriate */
1096                 if (cmdbuf[0])
1097                 {
1098                     strcpy(lastsearch, cmdbuf);
1099                     buf = cmdbuf;
1100                 }
1101                 else
1102                     buf = lastsearch;
1103 
1104                 stay_in_src = TRUE;
1105                 goto another_search;
1106             }
1107             if (cmd == 0)
1108             {
1109                 stay_in_src = FALSE;
1110                 continue;
1111             }
1112             stay_in_src = TRUE;
1113             goto process_cmd;
1114 
1115         case 'u':
1116             if (level < ctx->dbgcxfcn - 1) ++level;
1117             osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1);
1118             break;
1119 
1120         case 'd':
1121             if (level > 0) --level;
1122             osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1);
1123             break;
1124 #ifndef UNIX
1125         case '?':
1126             {
1127                 char      buf[128];
1128                 osfildef *fp;
1129             do_help:
1130                 if ( !os_locate("help.tdb", 8, ARG0, buf, sizeof(buf))
1131                     || !(fp = osfoprb(buf)) )
1132                     osdbgpt(&dbgtxtwin, "HELP.TDB not found\n");
1133                 else
1134                 {
1135                     dbgswitch(&dbgsrclin, (lindef *)0);
1136                     linfini2(ctx->dbgcxmem, (linfdef *)filesrc,
1137                              buf, (int)strlen(buf), fp);
1138                     dbgsrclin = (lindef *)filesrc;
1139                     lintell(dbgsrclin, dbgsrctop);
1140                     lintell(dbgsrclin, dbgsrccsr);
1141                     dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc);
1142                 }
1143             }
1144 #else
1145         do_help:
1146 #endif
1147             break;
1148 
1149         default:
1150             osdbgpt(&dbgtxtwin, "invalid debugger command\n");
1151             break;
1152         }
1153     }
1154 }
1155 
dbgucmd(dbgcxdef * ctx,int bphit,int err,unsigned int * exec_ofs)1156 void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs)
1157 {
1158     ossvpg(1);                           /* change to debugger's video page */
1159 
1160     /* display cause of breakpoint if not single-stepping */
1161     if (err || bphit)
1162     {
1163         if (!needprompt) osdbgpt(&dbgtxtwin, "\n");
1164         if (err)
1165             errprelog(ctx->dbgcxerr, err);
1166         else if (bphit)
1167             osdbgpt(&dbgtxtwin, "Stop at breakpoint %d\n", bphit);
1168         needprompt = TRUE;
1169     }
1170 
1171     dbgucmd1(ctx);                              /* run the normal main loop */
1172     ossvpg(0);                         /* change back to default video page */
1173 }
1174 
dbguini(dbgcxdef * ctx,const char * game_filename)1175 void dbguini(dbgcxdef *ctx, const char *game_filename)
1176 {
1177     char       buf[150];
1178     int        i;
1179     extern int sdesc_color;
1180     extern int text_color;
1181     int        ldesc_color = 112;
1182     int        watch_color = 0x47;
1183     extern int usebios;
1184     int        maxline;
1185     int        maxcol;
1186     int        divline;
1187 
1188     ossgmx(&maxline, &maxcol);
1189     osdbgini(maxline+1, maxcol+1);
1190 
1191     usebios = 0;
1192 
1193     ossvpg(1);
1194 #ifdef UNIX
1195     ossclr(0, 0, maxline, maxcol, text_normal_color);
1196 #else
1197     ossclr(0, 0, maxline, maxcol, 7);
1198 #endif
1199 
1200     if (ossmon())
1201     {
1202         ldesc_color = 7;
1203         dbgcurcolor = 10;
1204         dbgbpcolor = 7;
1205         watch_color = 10;
1206     }
1207 
1208 #ifdef UNIX
1209         dbgcurcolor = text_bold_color;
1210         dbgbpcolor = breakpoint_color;
1211         watch_color = watchpoint_color;
1212         ldesc_color = filename_color;
1213 #endif
1214 
1215     /* initialize windows */
1216 #ifdef MSOS2
1217     divline = maxline * 2 / 3;
1218     osdbgwop(&dbgsrcwin, 0, 0, maxcol, divline-1, sdesc_color);
1219     osdbgwop(&dbgstawin, 0, divline, maxcol, divline, ldesc_color);
1220     osdbgwop(&dbgtxtwin, 0, divline+1, maxcol, maxline, text_color);
1221     osdbgwop(&dbgwatwin, 0, divline+1, maxcol, divline, watch_color);
1222 #else /* !MSOS2 */
1223     osdbgwop(&dbgsrcwin, 0, 0, 79, 15, sdesc_color);
1224     osdbgwop(&dbgstawin, 0, 16, 79, 16, ldesc_color);
1225     osdbgwop(&dbgtxtwin, 0, 17, 79, 24, text_color);
1226     osdbgwop(&dbgwatwin, 0, 17, 79, 16, watch_color);
1227 #endif /* MSOS2 */
1228     dbgsrcwin.oswflg = OSWFCLIP;
1229     dbgstawin.oswflg = OSWFCLIP;
1230     dbgwatwin.oswflg = OSWFCLIP;
1231     dbgtxtwin.oswflg = OSWFMORE;
1232 
1233     /* display status line with bar */
1234     for (i = 0 ; i <= maxcol && i+1 < sizeof(buf) ; ++i) buf[i] = DBGUHLIN;
1235     buf[i] = '\0';
1236     osdbgpt(&dbgstawin, buf);
1237 
1238     ossvpg(0);
1239 
1240     /* allocate buffer */
1241     dbgbuf = (char *)malloc(dbgbufsiz = 4096);
1242 }
1243 
1244 /*
1245  *   initialization phase two (called after the .GAM file is loaded)
1246  */
dbguini2(dbgcxdef * ctx)1247 void dbguini2(dbgcxdef *ctx)
1248 {
1249     /* we don't need to do any more setup after loading the .GAM file */
1250 }
1251 
1252 /* terminate the UI */
dbguterm(dbgcxdef * ctx)1253 void dbguterm(dbgcxdef *ctx)
1254 {
1255     /* there's nothing extra we need to do here */
1256 }
1257 
1258 /*
1259  *   Determine if we can resume from an error.  This UI doesn't allow the
1260  *   user to change the instruction pointer, so we can't resume from an
1261  *   error.
1262  */
dbgu_err_resume(dbgcxdef * ctx)1263 int dbgu_err_resume(dbgcxdef *ctx)
1264 {
1265     VARUSED(ctx);
1266     return FALSE;
1267 }
1268 
1269 /*
1270  *   Quitting - do nothing; let the system terminate
1271  */
dbguquitting(dbgcxdef * ctx)1272 void dbguquitting(dbgcxdef *ctx)
1273 {
1274     VARUSED(ctx);
1275 }
1276 
1277 
1278 /*
1279  *   Locate a source file using UI-dependent mechanisms.  We don't have
1280  *   anything more we can do beyond what the core debugger does, so we'll
1281  *   simply return a failure indication.  On some systems, this is used to
1282  *   display a "find file" dialog to allow the user to locate a source
1283  *   file manually when we can't find it in any of the places we know to
1284  *   look.
1285  *
1286  *   Refer to the HTML TADS Win32 implementation for details on how this
1287  *   can implemented for friendlier behavior on a GUI system.
1288  */
dbgu_find_src(const char * origname,int origlen,char * fullname,size_t full_len,int must_find_file)1289 int dbgu_find_src(const char *origname, int origlen,
1290                   char *fullname, size_t full_len, int must_find_file)
1291 {
1292      VARUSED(origname);
1293      VARUSED(origlen);
1294      VARUSED(fullname);
1295      VARUSED(full_len);
1296      VARUSED(must_find_file);
1297 
1298      return FALSE;
1299 }
1300 
1301