#ifdef RCSID static char RCSid[] = "$Header: d:/cvsroot/tads/TADS2/unix/dbgu.c,v 1.3 1999/07/11 00:46:39 MJRoberts Exp $"; #endif /* Copyright (c) 1992, 2002 Michael J. Roberts. All Rights Reserved. */ /* Name dbgu.c - user interface for debugger Function Implements the user interface for the command-line version of the debugger. This portion of the debugger performs only user-interface functions; the UI-indenpendent section actually performs most of the work of debugging. Notes None Modified 04/12/92 MJRoberts - creation */ #include #include #include #include #include "os.h" #include "dbg.h" #include "tio.h" #include "obj.h" #include "prp.h" #include "tok.h" #include "run.h" #include "lin.h" #include "linf.h" #include "osdbg.h" #ifdef UNIX #include "osdbg_un.h" #define sdesc_color text_normal_color extern int watchpoint_color; extern int breakpoint_color; extern int filename_color; #endif #ifdef MICROSOFT extern char **__argv; #define ARG0 __argv[0] #endif /* MICROSOFT */ #ifdef __IBMC__ extern char **_argv; void osdbgpt( oswdef *, char *fmt, ... ); #endif /* __IBMC__ */ #ifdef __EMX__ # define ARG0 (char *)0 #endif /* __EMX__ */ #ifndef ARG0 # define ARG0 _argv[0] #endif /* !ARG0 */ /* some special characters for PC's */ #define DBGUHLIN 196 /* horizontal line */ #define DBGARROW 16 /* right-pointing arrow */ #define DBGBPCHAR 15 /* breakpoint indicator character */ #ifdef UNIX #undef DBGUHLIN #undef DBGARROW #undef DBGBPCHAR #define DBGUHLIN '-' #define DBGARROW '>' #define DBGBPCHAR '*' #endif /* debug text, status, source, and watch windows */ static oswdef dbgtxtwin; static oswdef dbgstawin; static oswdef dbgsrcwin; static oswdef dbgwatwin; /* space for lindef containing arbitrary file not part of game source */ static char filesrc[sizeof(linfdef) + 128]; /* location in source file of top current source window and current line */ static uchar dbgsrctop[LINLLNMAX]; /* current display seek location */ static uchar dbgsrccsr[LINLLNMAX]; /* current cursor line */ static uchar dbgsrcloc[LINLLNMAX]; /* current source line in file */ static uchar dbgsrchome[LINLLNMAX]; /* seek location of "home" screen */ /* lindef for current source file */ static lindef *dbgsrclin; /* lindef for currently displayed file */ static lindef *dbgsrclinhome; /* lindef of "home" file */ /* color for current line/breakpoint line in source window */ static int dbgcurcolor = 30; static int dbgbpcolor = 0x47; /* private breakpoint data - maintained with dbgbpeach() */ static int dbgbpcnt; /* number of breakpoints set */ typedef struct dbgubpdef dbgubpdef; static struct dbgubpdef { int dbgubpid; /* line source id */ char dbgubpseek[LINLLNMAX]; /* seek position of breakpoint */ } dbgbps[DBGBPMAX]; /* seek locations of breakpoints */ /* forward declarations */ static void dbgugetbp(/*_ dbgcxdef *ctx _*/); /* move back or forward by one or more lines within a file */ static char *dbgbuf; static size_t dbgbufsiz; static void dbgmove(lin, cur, destlinep) lindef *lin; int cur; int *destlinep; { int dest = *destlinep; char *p; size_t max; long backup; long delta; uchar pos[LINLLNMAX]; uchar prvpos[LINLLNMAX]; /* find current location in line source */ lintell(lin, pos); if (cur < dest) { while (cur != dest) { max = linread(lin, dbgbuf, (int)dbgbufsiz); if (max == 0) break; for (delta = 0, p = dbgbuf ; p < dbgbuf + max && cur != dest ; ++p, ++delta) if (*p == '\n') ++cur; linpadd(lin, pos, delta); } } else { ++cur; while (cur != dest) { backup = linofs(lin); if (backup > dbgbufsiz) backup = dbgbufsiz; memcpy(prvpos, pos, (size_t)sizeof(prvpos)); linpadd(lin, prvpos, -(long)dbgbufsiz); linseek(lin, prvpos); max = linread(lin, dbgbuf, backup); if (max == 0) break; p = dbgbuf + max; delta = 0; do { --p; --delta; if (*p == '\n') --cur; } while (p > dbgbuf && cur != dest); linpadd(lin, pos, delta); if (linqtop(lin, prvpos)) break; } if (!linqtop(lin, pos)) linpadd(lin, pos, (long)1); else cur = 1; } linseek(lin, pos); *destlinep = cur; } /* show current source page */ static void dbgsrcshow(lin, win, top, loc) lindef *lin; oswdef *win; uchar *top; uchar *loc; { int y; char outbuf[128]; int wid = dbgsrcwin.oswx2 - dbgsrcwin.oswx1 + 1; extern int sdesc_color, text_color; char prefix[3]; uchar curloc[LINLLNMAX]; int i; dbgubpdef *bp; int thisid; thisid = lin ? lin->linid : 0; prefix[2] = '\0'; osdbgclr(win); for (y = win->oswy1 ; y <= win->oswy2 ; ++y) { win->oswx = win->oswx1; win->oswy = y; linftell(lin, curloc); /* special display if this is the currently executing line */ if (!memcmp(curloc, loc, lin->linlln) && lin == dbgsrclin) { win->oswcolor = dbgcurcolor; prefix[1] = DBGARROW; } else { win->oswcolor = sdesc_color; prefix[1] = ' '; } /* special display if there is a breakpoint at this line */ prefix[0] = ' '; /* presume nothing special about this line */ for (i = dbgbpcnt, bp = dbgbps ; i ; ++bp, --i) { if (!memcmp(bp->dbgubpseek, curloc, lin->linlln) && (bp->dbgubpid == thisid)) { prefix[0] = DBGBPCHAR; win->oswcolor = dbgbpcolor; break; } } /* display prefix with current execution/breakpoint info */ osdbgpt(win, "%s", prefix); /* display the line itself */ if (!lingets(lin, outbuf, sizeof(outbuf))) break; else { int l; l = strlen(outbuf); while (l && outbuf[l-1] == '\n' || outbuf[l-1] == '\r') --l; if (l + 2 >= wid) outbuf[wid - 2] = '\0'; else { outbuf[l] = '\0'; if (prefix[0] == DBGBPCHAR) { /* breakpoint - pad line out with spaces to full width */ while (l < wid - 2) outbuf[l++] = ' '; outbuf[l] = '\0'; } } osdbgpt(win, "%s", outbuf); if (y != win->oswy2) osdbgpt(win, "\n"); } } /* restore normal color */ win->oswcolor = sdesc_color; } /* search for a string in a line source */ static void dbgusearch(lin, pos, toppos, win, str) lindef *lin; uchar *pos; /* in: starting position; out: final position */ uchar *toppos; /* location of top of screen */ oswdef *win; char *str; { int dest; /* search is case-insensitive - convert search string to lowercase */ os_strlwr(str); /* seek to starting location, and read and discard current line */ linseek(lin, pos); if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) return; for (;;) { lintell(lin, pos); if (!lingets(lin, dbgbuf, (int)dbgbufsiz)) break; os_strlwr(dbgbuf); if (strstr(dbgbuf, str)) break; } /* move search expression to middle of screen */ linseek(lin, pos); dest = 1; dbgmove(lin, (win->oswy2 - win->oswy1)/2, &dest); lintell(lin, toppos); dbgsrcshow(lin, win, toppos, dbgsrcloc); } /* redraw status line */ static char dbg_curstat[81]; static void dbgustat(ctx) dbgcxdef *ctx; { VARUSED(ctx); dbgstawin.oswx = dbgstawin.oswx1; dbgstawin.oswy = dbgstawin.oswy1; osdbgpt(&dbgstawin, dbg_curstat); } /* show the current line */ static void dbgulsho(ctx, buf) dbgcxdef *ctx; uchar *buf; /* line information retrieved by dbglget */ { lindef *lin; char outbuf[128]; int cur; int dest; uchar loc[LINLLNMAX]; int y; char fnam[182]; int i; /* search for a line source with this id */ for (lin = ctx->dbgcxlin ; lin ; lin = lin->linnxt) if (lin->linid == *buf) break; dbgswitch(&dbgsrclin, lin); dbgsrclinhome = lin; if (!lin) { osdbgclr(&dbgsrcwin); osdbgpt(&dbgsrcwin, "*** source line not found ***\n"); return; } /* seek to line in file, then back up by half a screen to center line */ memcpy(loc, buf+1, lin->linlln); linseek(lin, loc); cur = (dbgsrcwin.oswy2 - dbgsrcwin.oswy1) / 2; dest = 1; dbgmove(lin, cur, &dest); lintell(lin, dbgsrctop); memcpy(dbgsrchome, dbgsrctop, (size_t)lin->linlln); memcpy(dbgsrcloc, loc, (size_t)lin->linlln); memcpy(dbgsrccsr, loc, (size_t)lin->linlln); dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc); /* update status line to show current file */ for (i = 0 ; i < 5 ; ++i) dbg_curstat[i] = DBGUHLIN; linnam(lin, fnam); sprintf(&dbg_curstat[5], "%s ", fnam); dbgwhere(ctx, &dbg_curstat[strlen(dbg_curstat)]); for (i = strlen(dbg_curstat) ; i <= dbgstawin.oswx2 - dbgstawin.oswx1 ; ++i) dbg_curstat[i] = DBGUHLIN; dbg_curstat[i] = '\0'; /* actually display the status line now */ dbgustat(ctx); } /* display callback */ static void dbgudisp(ctx, str, strl) void *ctx; char *str; int strl; { oswdef *win = (oswdef *)ctx; int oldpg; oldpg = ossvpg(1); /* display values on debugger video page */ osdbgpt(win, "%.*s", strl, str); ossvpg(oldpg); /* restore previous page */ } /* display an error message */ void dbguerr(ctx, errno, msg) dbgcxdef *ctx; int errno; char *msg; { int oldpg; char err_prefix[64]; VARUSED(ctx); oldpg = ossvpg(1); /* figure out what page we're on */ ossvpg(oldpg); /* restore previous page */ if (oldpg == 0) { /* if we're on the game page... */ sprintf(err_prefix, "[TADS-%d: ", errno); os_printz(err_prefix); os_printz(msg); os_printz("]\n"); } else osdbgpt(&dbgtxtwin, "TADS-%d: %s\n", errno, msg); } /* show user screen, wait for key hit, then change back to dbg screen */ static void dbguuser(ctx) dbgcxdef *ctx; { VARUSED(ctx); ossvpg(0); os_waitc(); ossvpg(1); } /* execute a special keyboard command key */ static int dbgukbc(ctx, cmd) dbgcxdef *ctx; char cmd; { int cur = dbgsrcwin.oswy2; int dest = cur; uchar newpos[LINLLNMAX]; int scrht = dbgsrcwin.oswy2 - dbgsrcwin.oswy1 - 2; switch(cmd) { case CMD_F5: /* show user screen */ dbguuser(ctx); return(0); case CMD_TAB: /* other screen */ case CMD_F7: /* step into */ case CMD_F8: /* step over */ case CMD_F9: /* go */ case CMD_SCR: /* help */ return(cmd); } /* the remaining commands pertain to the source display only */ switch(cmd) { case CMD_CHOME: if (dbgsrclinhome && (dbgsrctop != dbgsrchome || dbgsrclin != dbgsrclinhome)) { dbgswitch(&dbgsrclin, dbgsrclinhome); linseek(dbgsrclin, dbgsrchome); memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln); memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } break; case CMD_TOP: if (dbgsrctop != 0) { lingoto(dbgsrclin, LINGOTOP); lintell(dbgsrclin, dbgsrccsr); memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } break; case CMD_BOT: lingoto(dbgsrclin, LINGOEND); dest -= scrht; goto move_noseek; case CMD_UP: --dest; goto move_source; case CMD_DOWN: ++dest; goto move_source; case CMD_PGUP: dest -= scrht; goto move_source; case CMD_PGDN: dest += scrht; move_source: linseek(dbgsrclin, dbgsrctop); move_noseek: dbgmove(dbgsrclin, cur, &dest); lintell(dbgsrclin, newpos); if (memcmp(dbgsrctop, newpos, dbgsrclin->linlln)) { memcpy(dbgsrccsr, newpos, dbgsrclin->linlln); memcpy(dbgsrctop, newpos, dbgsrclin->linlln); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } break; } return(0); } /* get commands in source window */ static int dbgurunsrc(ctx) dbgcxdef *ctx; { int y; char c; oswdef *win = &dbgsrcwin; uchar pos[LINLLNMAX]; uchar newpos[LINLLNMAX]; int dest; int cur; int scrht = win->oswy2 - win->oswy1 - 2; char *cond; char condbuf[128]; /* figure out where we are in the source window */ find_srcpos: linseek(dbgsrclin, dbgsrctop); memcpy(pos, dbgsrctop, dbgsrclin->linlln); for (y = win->oswy1 ; y < win->oswy2 ; ++y) { lintell(dbgsrclin, newpos); if (!memcmp(dbgsrccsr, newpos, dbgsrclin->linlln)) break; lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz); } /* get and process commands */ for (;;) { ossdbgloc(y, 2); /* locate cursor properly */ cond = (char *)0; /* no condition for breakpoints, by default */ c = os_getc(); switch(c) { case '/': return(CMD_UP); case '\0': c = os_getc(); switch(c) { case CMD_TAB: case CMD_EOF: return(0); case CMD_CHOME: if (dbgsrclinhome && (memcmp(dbgsrctop, dbgsrchome, dbgsrclin->linlln) || dbgsrclin != dbgsrclinhome)) { dbgswitch(&dbgsrclin, dbgsrclinhome); linseek(dbgsrclin, dbgsrchome); memcpy(pos, dbgsrchome, dbgsrclin->linlln); memcpy(dbgsrctop, dbgsrchome, dbgsrclin->linlln); memcpy(dbgsrccsr, dbgsrcloc, dbgsrclin->linlln); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); goto find_srcpos; } break; case CMD_UP: if (y > win->oswy1) { --y; goto find_cur_y; } else { dest = 1; cur = 2; goto move_source; } case CMD_DOWN: if (y < win->oswy2) { ++y; goto find_cur_y; } else { dest = 2; cur = 1; goto move_source; } case CMD_PGUP: cur = scrht; dest = 1; goto move_source; case CMD_PGDN: cur = 1; dest = scrht; move_source: linseek(dbgsrclin, pos); move_noseek: dbgmove(dbgsrclin, cur, &dest); lintell(dbgsrclin, newpos); if (memcmp(pos, newpos, dbgsrclin->linlln)) { int i; memcpy(dbgsrctop, newpos, dbgsrclin->linlln); memcpy(pos, newpos, dbgsrclin->linlln); dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc); /* figure out where dbgsrccsr is */ find_cur_y: linseek(dbgsrclin, pos); for (i = win->oswy1 ; i < y ; ++i) lingets(dbgsrclin, dbgbuf, (int)dbgbufsiz); lintell(dbgsrclin, dbgsrccsr); } break; case CMD_TOP: if (pos != 0) { lingoto(dbgsrclin, LINGOTOP); lintell(dbgsrclin, pos); memcpy(dbgsrccsr, pos, dbgsrclin->linlln); memcpy(dbgsrctop, pos, dbgsrclin->linlln); y = win->oswy1; dbgsrcshow(dbgsrclin, win, pos, dbgsrcloc); } break; case CMD_BOT: lingoto(dbgsrclin, LINGOEND); dest = 1; cur = scrht; goto move_noseek; case CMD_HOME: y = win->oswy1; goto find_cur_y; case CMD_END: y = win->oswy2; goto find_cur_y; case CMD_SF2: if (dbgsrclin) { osdbgpt(&dbgtxtwin, "\nCondition: "); while (osdbggts(&dbgtxtwin, condbuf, dbgukbc, ctx)); cond = condbuf; /* FALLTHROUGH */ } else break; case CMD_F2: if (dbgsrclin) { objnum objn; uint ofs; int newbp; char nambuf[128]; int len; linfind(dbgsrclin, dbgsrccsr, &objn, &ofs); memcpy(nambuf, " ", (size_t)4); len = dbgnam(ctx, nambuf+4, TOKSTOBJ, objn); if (!memcmp(nambuf+4, "", (size_t)len)) len = dbgnam(ctx, nambuf+4, TOKSTFUNC, objn); if (cond) { memcpy(nambuf + 4 + len, " when ", (size_t)6); strcpy(nambuf + 10 + len, cond); } else nambuf[len + 4] = '\0'; (void)dbgbpat(ctx, objn, MCMONINV, ofs, &newbp, nambuf, TRUE, cond, NULL); dbgugetbp(ctx); } break; case CMD_F5: dbguuser(ctx); break; case CMD_F7: case CMD_F8: case CMD_F9: return(c); } } } } /* callback for dbgugetp */ static void dbgubp(ctx, id, buf, bufsiz) dbgcxdef *ctx; int id; uchar *buf; uint bufsiz; { dbgubpdef *bp = &dbgbps[dbgbpcnt++]; bp->dbgubpid = id; memcpy(bp->dbgubpseek, buf, (size_t)bufsiz); } /* reset private breakpoint data - call after setting/clearing bp */ static void dbgugetbp(ctx) dbgcxdef *ctx; { dbgbpcnt = 0; dbgbpeach(ctx, dbgubp, ctx); if (dbgsrclin) { linseek(dbgsrclin, dbgsrctop); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } } /* output support for hidden output */ void trchid() { outflushn(1); os_printz("---Hidden output---\n"); } void trcsho() { outflushn(1); os_printz("---End of hidden output, normal output resumes---\n"); } /* update display of watchpoints */ static void dbguwxupd(ctx) dbgcxdef *ctx; { dbgwatwin.oswy = dbgwatwin.oswy1; dbgwatwin.oswx = dbgwatwin.oswx1; if (dbgwatwin.oswy1 <= dbgwatwin.oswy2) osdbgclr(&dbgwatwin); dbgwxupd(ctx, dbgudisp, &dbgwatwin); } /* update window sizes to account for more/fewer watches */ static void dbguwxadd(ctx, cnt) dbgcxdef *ctx; int cnt; { /* move status line up by the number of new watch lines */ dbgstawin.oswy1 -= cnt; dbgstawin.oswy2 -= cnt; /* move bottom of code window up by the number of watch lines */ dbgsrcwin.oswy2 -= cnt; /* move top of watch window up by the number of new watch lines */ dbgwatwin.oswy1 -= cnt; /* update the status, source and watch windows */ dbgustat(ctx); if (dbgsrclin) { linseek(dbgsrclin, dbgsrctop); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } dbguwxupd(ctx); } /* flags whether a prompt is needed the next time we run command line */ static int needprompt = TRUE; /* main command loop */ static void dbgucmd1(ctx) dbgcxdef *ctx; { uchar cmdbuf[128]; int linid; int err; int disable; int bpnum; char *buf; int cmd; int i; lindef *lin; static int stay_in_src; static int first_search; static char lastsearch[128]; int level = ctx->dbgcxfcn - 1; dbglget(ctx, cmdbuf); /* get information on current line */ dbgulsho(ctx, cmdbuf); /* display current line */ dbguwxupd(ctx); /* udpate watchpoint display */ for (;;) { if (stay_in_src) goto run_other_window; else { if (needprompt) osdbgpt(&dbgtxtwin, "tdb> "); cmd = osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx); } process_cmd: needprompt = (cmd == 0); /* ordinary command ==> prompt next time */ switch(cmd) { case 0: for (buf = cmdbuf ; isspace(*buf) ; ++buf); break; case CMD_F7: buf = "t"; break; case CMD_F8: buf = "p"; break; case CMD_F9: buf = "g"; break; case CMD_TAB: buf = "\t"; break; case CMD_SCR: buf = "?"; break; default: continue; } if (isupper(buf[0])) buf[0] = tolower(buf[0]); switch(buf[0]) { case 'g': ctx->dbgcxflg &= ~(DBGCXFSS + DBGCXFSO); return; case 't': ctx->dbgcxflg |= DBGCXFSS; ctx->dbgcxflg &= ~DBGCXFSO; return; case 'p': ctx->dbgcxsof = ctx->dbgcxdep; /* step until back at this depth */ ctx->dbgcxflg |= (DBGCXFSS + DBGCXFSO); return; case 'e': ossvpg(0); /* eval expression on game video page */ err = dbgeval(ctx, buf+1, dbgudisp, &dbgtxtwin, level, TRUE); ossvpg(1); /* return to debugger video page */ if (err) errlog(ctx->dbgcxerr, err); dbguwxupd(ctx); /* udpate watchpoint display */ break; case 'c': /* Call history */ if (isupper(buf[1])) buf[1] = tolower(buf[1]); switch(buf[1]) { case '+': ctx->dbgcxhstf = 0; ctx->dbgcxflg |= DBGCXFTRC; osdbgpt(&dbgtxtwin, "Call tracing activated.\n"); break; case '-': ctx->dbgcxflg &= ~DBGCXFTRC; osdbgpt(&dbgtxtwin, "Call tracing disactivated.\n"); break; case 'c': ctx->dbgcxhstf = 0; osdbgpt(&dbgtxtwin, "Call trace log cleared.\n"); break; case '\0': case ' ': { char *p; char *endp; for (p = ctx->dbgcxhstp, endp = p + ctx->dbgcxhstf ; p < endp ; p += strlen(p) + 1) osdbgpt(&dbgtxtwin, "%s\n", p); } } break; case 'b': if (isupper(buf[1])) buf[1] = tolower(buf[1]); switch(buf[1]) { case 'p': if (err = dbgbpset(ctx, buf+2, &bpnum)) errlog(ctx->dbgcxerr, err); else { osdbgpt(&dbgtxtwin, "breakpoint %d set\n", bpnum); dbgugetbp(ctx); } break; case 'c': if (err = dbgbpdel(ctx, atoi(buf + 2))) errlog(ctx->dbgcxerr, err); else { osdbgpt(&dbgtxtwin, "breakpoint cleared\n"); dbgugetbp(ctx); } break; case 'd': disable = TRUE; goto bp_enable_or_disable; case 'e': disable = FALSE; bp_enable_or_disable: if (err = dbgbpdis(ctx, atoi(buf+2), disable)) errlog(ctx->dbgcxerr, err); else osdbgpt(&dbgtxtwin, "breakpoint %sabled\n", disable ? "dis" : "en"); break; case 'l': dbgbplist(ctx, dbgudisp, &dbgtxtwin); break; default: osdbgpt(&dbgtxtwin, "invalid breakpoint command\n"); break; } break; case 'w': if (isupper(buf[1])) buf[1] = tolower(buf[1]); switch(buf[1]) { case 's': if (err = dbgwxset(ctx, buf+2, &bpnum, level)) errlog(ctx->dbgcxerr, err); else dbguwxadd(ctx, 1); /* adjust windows for a new wx */ break; case 'd': if (err = dbgwxdel(ctx, atoi(buf + 2))) errlog(ctx->dbgcxerr, err); else dbguwxadd(ctx, -1); /* adjust windows for one less wx */ break; default: osdbgpt(&dbgtxtwin, "invalid watch command\n"); break; } break; case 'f': if (isupper(buf[1])) buf[1] = tolower(buf[1]); switch(buf[1]) { case 'l': for (i = 1, lin = ctx->dbgcxlin ; lin ; lin = lin->linnxt, ++i) { osdbgpt(&dbgtxtwin, "#%-2d %s", i, ((linfdef *)lin)->linfnam); osdbgpt(&dbgtxtwin, "\n"); } break; case 'v': if (atoi(buf + 2) == 0) { osfildef *fp; /* file by name - read the filename */ for (buf += 2 ; isspace(*buf) ; ++buf); if (!(fp = osfoprb(buf, OSFTBIN))) /* dbj: right type?? */ osdbgpt(&dbgtxtwin, "file not found\n"); else { dbgswitch(&dbgsrclin, (lindef *)0); *(buf-1) = '\0'; linfini2(ctx->dbgcxmem, (linfdef *)filesrc, buf-1, (int)strlen(buf) + 2, fp, FALSE); dbgsrclin = (lindef *)filesrc; lintell(dbgsrclin, dbgsrctop); lintell(dbgsrclin, dbgsrccsr); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } break; } /* file by number - look up in line source list */ for (i = 1, lin = ctx->dbgcxlin ; lin ; lin = lin->linnxt, ++i) { if (i == atoi(buf + 2)) break; } if (lin) { lindef *oldlin; oldlin = dbgsrclin; dbgswitch(&dbgsrclin, lin); lingoto(dbgsrclin, LINGOTOP); lintell(dbgsrclin, dbgsrccsr); memcpy(dbgsrctop, dbgsrccsr, dbgsrclin->linlln); dbgsrcshow(lin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } else osdbgpt(&dbgtxtwin, "invalid file number\n"); break; default: osdbgpt(&dbgtxtwin, "invalid file command\n"); break; } break; case '\0': break; case 'q': if (strcmp(buf, "quit")) osdbgpt(&dbgtxtwin, "invalid debugger command\n"); else { ossvpg(0); /* return to game page */ errsig(ctx->dbgcxerr, ERR_RUNQUIT); /* signal QUIT */ } break; case 'k': dbgstktr(ctx, dbgudisp, &dbgtxtwin, level, FALSE, TRUE); break; case 'h': if (!stricmp(buf, "help")) goto do_help; else { extern int dbghid; dbghid = !dbghid; osdbgpt(&dbgtxtwin, "Hidden text will be %s.\n", dbghid ? "displayed" : "suppressed"); } break; case '\\': dbguuser(ctx); break; case '/': ++buf; /* skip the '/' part */ strcpy(lastsearch, buf); another_search: dbgusearch(dbgsrclin, dbgsrccsr, dbgsrctop, &dbgsrcwin, buf); needprompt = TRUE; goto run_other_window; case '\t': needprompt = FALSE; first_search = TRUE; run_other_window: cmd = dbgurunsrc(ctx); if (cmd == CMD_UP) /* special return for "search" */ { char newbuf[128]; if (first_search) { osdbgpt(&dbgtxtwin, "\n"); first_search = FALSE; } /* present default if there is one */ if (lastsearch[0]) osdbgpt(&dbgtxtwin, "search [%s]: ", lastsearch); else osdbgpt(&dbgtxtwin, "search: "); /* ignore command keys from search call */ while (osdbggts(&dbgtxtwin, cmdbuf, dbgukbc, ctx)) ; /* apply default if appropriate */ if (cmdbuf[0]) { strcpy(lastsearch, cmdbuf); buf = cmdbuf; } else buf = lastsearch; stay_in_src = TRUE; goto another_search; } if (cmd == 0) { stay_in_src = FALSE; continue; } stay_in_src = TRUE; goto process_cmd; case 'u': if (level < ctx->dbgcxfcn - 1) ++level; osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1); break; case 'd': if (level > 0) --level; osdbgpt(&dbgtxtwin, "evaluations are now at level %d\n", level+1); break; #ifndef UNIX case '?': { char buf[128]; osfildef *fp; do_help: if ( !os_locate("help.tdb", 8, ARG0, buf, sizeof(buf)) || !(fp = osfoprb(buf)) ) osdbgpt(&dbgtxtwin, "HELP.TDB not found\n"); else { dbgswitch(&dbgsrclin, (lindef *)0); linfini2(ctx->dbgcxmem, (linfdef *)filesrc, buf, (int)strlen(buf), fp); dbgsrclin = (lindef *)filesrc; lintell(dbgsrclin, dbgsrctop); lintell(dbgsrclin, dbgsrccsr); dbgsrcshow(dbgsrclin, &dbgsrcwin, dbgsrctop, dbgsrcloc); } } #else do_help: #endif break; default: osdbgpt(&dbgtxtwin, "invalid debugger command\n"); break; } } } void dbgucmd(dbgcxdef *ctx, int bphit, int err, unsigned int *exec_ofs) { ossvpg(1); /* change to debugger's video page */ /* display cause of breakpoint if not single-stepping */ if (err || bphit) { if (!needprompt) osdbgpt(&dbgtxtwin, "\n"); if (err) errprelog(ctx->dbgcxerr, err); else if (bphit) osdbgpt(&dbgtxtwin, "Stop at breakpoint %d\n", bphit); needprompt = TRUE; } dbgucmd1(ctx); /* run the normal main loop */ ossvpg(0); /* change back to default video page */ } void dbguini(dbgcxdef *ctx, const char *game_filename) { char buf[150]; int i; extern int sdesc_color; extern int text_color; int ldesc_color = 112; int watch_color = 0x47; extern int usebios; int maxline; int maxcol; int divline; ossgmx(&maxline, &maxcol); osdbgini(maxline+1, maxcol+1); usebios = 0; ossvpg(1); #ifdef UNIX ossclr(0, 0, maxline, maxcol, text_normal_color); #else ossclr(0, 0, maxline, maxcol, 7); #endif if (ossmon()) { ldesc_color = 7; dbgcurcolor = 10; dbgbpcolor = 7; watch_color = 10; } #ifdef UNIX dbgcurcolor = text_bold_color; dbgbpcolor = breakpoint_color; watch_color = watchpoint_color; ldesc_color = filename_color; #endif /* initialize windows */ #ifdef MSOS2 divline = maxline * 2 / 3; osdbgwop(&dbgsrcwin, 0, 0, maxcol, divline-1, sdesc_color); osdbgwop(&dbgstawin, 0, divline, maxcol, divline, ldesc_color); osdbgwop(&dbgtxtwin, 0, divline+1, maxcol, maxline, text_color); osdbgwop(&dbgwatwin, 0, divline+1, maxcol, divline, watch_color); #else /* !MSOS2 */ osdbgwop(&dbgsrcwin, 0, 0, 79, 15, sdesc_color); osdbgwop(&dbgstawin, 0, 16, 79, 16, ldesc_color); osdbgwop(&dbgtxtwin, 0, 17, 79, 24, text_color); osdbgwop(&dbgwatwin, 0, 17, 79, 16, watch_color); #endif /* MSOS2 */ dbgsrcwin.oswflg = OSWFCLIP; dbgstawin.oswflg = OSWFCLIP; dbgwatwin.oswflg = OSWFCLIP; dbgtxtwin.oswflg = OSWFMORE; /* display status line with bar */ for (i = 0 ; i <= maxcol && i+1 < sizeof(buf) ; ++i) buf[i] = DBGUHLIN; buf[i] = '\0'; osdbgpt(&dbgstawin, buf); ossvpg(0); /* allocate buffer */ dbgbuf = (char *)malloc(dbgbufsiz = 4096); } /* * initialization phase two (called after the .GAM file is loaded) */ void dbguini2(dbgcxdef *ctx) { /* we don't need to do any more setup after loading the .GAM file */ } /* terminate the UI */ void dbguterm(dbgcxdef *ctx) { /* there's nothing extra we need to do here */ } /* * Determine if we can resume from an error. This UI doesn't allow the * user to change the instruction pointer, so we can't resume from an * error. */ int dbgu_err_resume(dbgcxdef *ctx) { VARUSED(ctx); return FALSE; } /* * Quitting - do nothing; let the system terminate */ void dbguquitting(dbgcxdef *ctx) { VARUSED(ctx); } /* * Locate a source file using UI-dependent mechanisms. We don't have * anything more we can do beyond what the core debugger does, so we'll * simply return a failure indication. On some systems, this is used to * display a "find file" dialog to allow the user to locate a source * file manually when we can't find it in any of the places we know to * look. * * Refer to the HTML TADS Win32 implementation for details on how this * can implemented for friendlier behavior on a GUI system. */ int dbgu_find_src(const char *origname, int origlen, char *fullname, size_t full_len, int must_find_file) { VARUSED(origname); VARUSED(origlen); VARUSED(fullname); VARUSED(full_len); VARUSED(must_find_file); return FALSE; }