int muf_debugger(dbref player, dbref program, const char *text, struct frame *fr) { char cmd[BUFFER_LEN]; char buf[BUFFER_LEN]; char *ptr, *ptr2, *arg; struct inst *pinst; int i, j, cnt; while (isspace(*text)) text++; strcpy(cmd, text); ptr = cmd + strlen(cmd); if (ptr > cmd) ptr--; while (ptr >= cmd && isspace(*ptr)) *ptr-- = '\0'; for (arg = cmd; *arg && !isspace(*arg); arg++); if (*arg) *arg++ = '\0'; if (!*cmd && fr->brkpt.lastcmd) { strcpy(cmd, fr->brkpt.lastcmd); } else { if (fr->brkpt.lastcmd) free(fr->brkpt.lastcmd); if (*cmd) fr->brkpt.lastcmd = string_dup(cmd); } /* delete triggering breakpoint, if it's only temp. */ j = fr->brkpt.breaknum; if (j >= 0 && fr->brkpt.temp[j]) { for (j++; j < fr->brkpt.count; j++) { fr->brkpt.temp[j-1] = fr->brkpt.temp[j]; fr->brkpt.level[j-1] = fr->brkpt.level[j]; fr->brkpt.line[j-1] = fr->brkpt.line[j]; fr->brkpt.linecount[j-1] = fr->brkpt.linecount[j]; fr->brkpt.pc[j-1] = fr->brkpt.pc[j]; fr->brkpt.pccount[j-1] = fr->brkpt.pccount[j]; fr->brkpt.prog[j-1] = fr->brkpt.prog[j]; } fr->brkpt.count--; } fr->brkpt.breaknum = -1; if (!string_compare(cmd, "cont")) { } else if (!string_compare(cmd, "finish")) { if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top - 1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "stepi")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot stepi because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = i; fr->brkpt.prog[j] = NOTHING; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "step")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot step because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = i; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = NOTHING; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "nexti")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot nexti because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = i; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "next")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot next because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = i; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "exec")) { if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } if (!(pinst = funcname_to_pc(program, arg))) { anotify_nolisten(player, CINFO "I don't know a function by that name.", 1); add_muf_read_event(player, program, fr); return 0; } if (fr->system.top >= STACK_SIZE) { anotify_nolisten(player, CFAIL "That would exceed the system stack size for this program.", 1); add_muf_read_event(player, program, fr); return 0; } fr->system.st[fr->system.top].progref = program; fr->system.st[fr->system.top++].offset = fr->pc; fr->pc = pinst; j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top - 1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "prim")) { if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(player, program, fr); return 0; } if (!(i = primitive(arg))) { anotify_nolisten(player, CINFO "I don't recognize that primitive.", 1); add_muf_read_event(player, program, fr); return 0; } if (fr->system.top >= STACK_SIZE) { anotify_nolisten(player, CFAIL "That would exceed the system stack size for this program.", 1); add_muf_read_event(player, program, fr); return 0; } shstr.data[0] = '\0'; shstr.links = 1; shstr.length= strlen(shstr.data); primset[0].type = PROG_FUNCTION; primset[0].line = 0; primset[0].data.string = &shstr; primset[1].type = PROG_PRIMITIVE; primset[1].line = 0; primset[1].data.number = i; primset[2].type = PROG_PRIMITIVE; primset[2].line = 0; primset[2].data.number = primitive("EXIT"); fr->system.st[fr->system.top].progref = program; fr->system.st[fr->system.top++].offset = fr->pc; fr->pc = primset; j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top - 1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!string_compare(cmd, "break")) { add_muf_read_event(player, program, fr); if (fr->brkpt.count >= MAX_BREAKS) { anotify_nolisten(player, CFAIL "Too many breakpoints set.", 1); return 0; } if (number(arg)) { i = atoi(arg); } else { if (!(pinst = funcname_to_pc(program, arg))) { anotify_nolisten(player, CINFO "I don't know a function by that name.", 1); return 0; } else { i = pinst->line; } } if (!i) i = fr->pc->line; j = fr->brkpt.count++; fr->brkpt.temp[j] = 0; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = i; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; anotify_nolisten(player, CSUCC "Breakpoint set.", 1); return 0; } else if (!string_compare(cmd, "delete")) { add_muf_read_event(player, program, fr); i = atoi(arg); if (!i) { anotify_nolisten(player, CINFO "Which breakpoint did you want to delete?", 1); return 0; } if (i < 1 || i > fr->brkpt.count) { anotify_nolisten(player, CFAIL "No such breakpoint.", 1); return 0; } j = i - 1; for (j++; j < fr->brkpt.count; j++) { fr->brkpt.temp[j-1] = fr->brkpt.temp[j]; fr->brkpt.level[j-1] = fr->brkpt.level[j]; fr->brkpt.line[j-1] = fr->brkpt.line[j]; fr->brkpt.linecount[j-1] = fr->brkpt.linecount[j]; fr->brkpt.pc[j-1] = fr->brkpt.pc[j]; fr->brkpt.pccount[j-1] = fr->brkpt.pccount[j]; fr->brkpt.prog[j-1] = fr->brkpt.prog[j]; } fr->brkpt.count--; anotify_nolisten(player, CSUCC "Breakpoint deleted.", 1); return 0; } else if (!string_compare(cmd, "breaks")) { anotify_nolisten(player, CINFO "Breakpoints:", 1); for (i = 0; i < fr->brkpt.count; i++) { ptr = unparse_breakpoint(fr, i); notify_nolisten(player, ptr, 1); } anotify_nolisten(player, CINFO "Done.", 1); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "where")) { i = atoi(arg); muf_backtrace(player, program, i, fr); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "stack")) { anotify_nolisten(player, CINFO "*Argument stack top*", 1); i = atoi(arg); if (!i) i = STACK_SIZE; ptr = ""; for (j = fr->argument.top; j>0 && i-->0;) { cnt = 0; do { strcpy(buf, ptr); ptr = insttotext(&fr->argument.st[--j], 4000, program); cnt++; } while (!string_compare(ptr, buf) && j>0); if (cnt > 1) notify_fmt(player, " [repeats %d times]", cnt); if (string_compare(ptr, buf)) notify_fmt(player, "%3d) %s", j+1, ptr); } anotify_nolisten(player, CINFO "Done.", 1); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "list") || !string_compare(cmd, "listi")) { int startline, endline; startline = endline = 0; add_muf_read_event(player, program, fr); if ((ptr2 = (char *)index(arg, ','))) { *ptr2++ = '\0'; } else { ptr2 = ""; } if (!*arg) { if (fr->brkpt.lastlisted) { startline = fr->brkpt.lastlisted + 1; } else { startline = fr->pc->line; } endline = startline + 15; } else { if (!number(arg)) { if (!(pinst = funcname_to_pc(program, arg))) { anotify_nolisten(player, CINFO "I don't know a function by that name. (starting arg, 1)", 1); return 0; } else { startline = pinst->line; endline = startline + 15; } } else { if (*ptr2) { endline = startline = atoi(arg); } else { startline = atoi(arg) - 7; endline = startline + 15; } } } if (*ptr2) { if (!number(ptr2)) { if (!(pinst = funcname_to_pc(program, ptr2))) { anotify_nolisten(player, CINFO "I don't know a function by that name. (ending arg, 1)", 1); return 0; } else { endline = pinst->line; } } else { endline = atoi(ptr2); } } i = (DBFETCH(program)->sp.program.code + DBFETCH(program)->sp.program.siz - 1)->line; if (startline > i) { anotify_nolisten(player, CFAIL "Starting line is beyond end of program.", 1); return 0; } if (startline < 1) startline = 1; if (endline > i) endline = i; if (endline < startline) endline = startline; anotify_nolisten(player, CINFO "Listing:", 1); if (!string_compare(cmd, "listi")) { for (i = startline; i <= endline; i++) { pinst = linenum_to_pc(program, i); if (pinst) { sprintf(buf, "line %d: %s", i, (i == fr->pc->line) ? show_line_prims(program, fr->pc, STACK_SIZE, 1) : show_line_prims(program, pinst, STACK_SIZE, 0)); notify_nolisten(player, buf, 1); } } } else { list_proglines(player, program, fr, startline, endline); } fr->brkpt.lastlisted = endline; anotify_nolisten(player, CINFO "Done.", 1); return 0; } else if (!string_compare(cmd, "quit")) { anotify_nolisten(player, CINFO "Halting execution.", 1); return 1; } else if (!string_compare(cmd, "trace")) { add_muf_read_event(player, program, fr); if (!string_compare(arg, "on")) { fr->brkpt.showstack = 1; anotify_nolisten(player, CSUCC "Trace turned on.", 1); } else if (!string_compare(arg, "off")) { fr->brkpt.showstack = 0; anotify_nolisten(player, CSUCC "Trace turned off.", 1); } else { sprintf(buf, CINFO "Trace is currently %s.", fr->brkpt.showstack? "on" : "off"); anotify_nolisten(player, buf, 1); } return 0; } else if (!string_compare(cmd, "words")) { list_program_functions(player, program, arg); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "print")) { debug_printvar(player, fr, arg); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "push")) { push_arg(player, fr, arg); add_muf_read_event(player, program, fr); return 0; } else if (!string_compare(cmd, "pop")) { add_muf_read_event(player, program, fr); if (fr->argument.top < 1) { anotify_nolisten(player, CFAIL "Nothing to pop.", 1); return 0; } fr->argument.top--; CLEAR(fr->argument.st + fr->argument.top); anotify_nolisten(player, CSUCC "Stack item popped.", 1); return 0; } else if (!string_compare(cmd, "help")) { notify_nolisten(player, "cont continues execution until a breakpoint is hit.", 1); notify_nolisten(player, "finish completes execution of current function.", 1); notify_nolisten(player, "step [NUM] executes one (or NUM, 1) lines of muf.", 1); notify_nolisten(player, "stepi [NUM] executes one (or NUM, 1) muf instructions.", 1); notify_nolisten(player, "next [NUM] like step, except skips CALL and EXECUTE.", 1); notify_nolisten(player, "nexti [NUM] like stepi, except skips CALL and EXECUTE.", 1); notify_nolisten(player, "break LINE# sets breakpoint at given LINE number.", 1); notify_nolisten(player, "break FUNCNAME sets breakpoint at start of given function.", 1); notify_nolisten(player, "breaks lists all currently set breakpoints.", 1); notify_nolisten(player, "delete NUM deletes breakpoint by NUM, as listed by 'breaks'", 1); notify_nolisten(player, "where [LEVS] displays function call backtrace of up to num levels deep.", 1); notify_nolisten(player, "stack [NUM] shows the top num items on the stack.", 1); notify_nolisten(player, "print v# displays the value of given global variable #.", 1); notify_nolisten(player, "print lv# displays the value of given local variable #.", 1); notify_nolisten(player, "trace [on|off] turns on/off debug stack tracing.", 1); notify_nolisten(player, "list [L1,[L2]] lists source code of given line range.", 1); notify_nolisten(player, "list FUNCNAME lists source code of given function.", 1); notify_nolisten(player, "listi [L1,[L2]] lists instructions in given line range.", 1); notify_nolisten(player, "listi FUNCNAME lists instructions in given function.", 1); notify_nolisten(player, "words lists all function word names in program.", 1); notify_nolisten(player, "words PATTERN lists all function word names that match PATTERN.", 1); notify_nolisten(player, "exec FUNCNAME calls given function with the current stack data.", 1); notify_nolisten(player, "prim PRIMITIVE executes given primitive with current stack data.", 1); notify_nolisten(player, "push DATA pushes an int, dbref, var, or string onto the stack.", 1); notify_nolisten(player, "pop pops top data item off the stack.", 1); notify_nolisten(player, "help displays this help screen.", 1); notify_nolisten(player, "quit stop execution here.", 1); add_muf_read_event(player, program, fr); return 0; } else { anotify_nolisten(player, CINFO "I don't understand that debugger command. Type 'help' for help.", 1); add_muf_read_event(player, program, fr); return 0; } return 0; }
struct inst * interp_loop(dbref player, dbref program, struct frame *fr, int rettyp) { register struct inst *pc; register int atop; register struct inst *arg; register struct inst *temp1; register struct inst *temp2; register struct stack_addr *sys; register int instr_count; register int stop; int i = 0, tmp, writeonly, mlev; static struct inst retval; char dbuf[BUFFER_LEN]; int instno_debug_line = get_primitive("debug_line"); fr->level = ++interp_depth; /* increment interp level */ /* load everything into local stuff */ pc = fr->pc; atop = fr->argument.top; stop = fr->system.top; arg = fr->argument.st; sys = fr->system.st; writeonly = fr->writeonly; already_created = 0; fr->brkpt.isread = 0; if (!pc) { struct line *tmpline; tmpline = PROGRAM_FIRST(program); PROGRAM_SET_FIRST(program, (struct line *) read_program(program)); do_compile(-1, OWNER(program), program, 0); free_prog_text(PROGRAM_FIRST(program)); PROGRAM_SET_FIRST(program, tmpline); pc = fr->pc = PROGRAM_START(program); if (!pc) { abort_loop_hard("Program not compilable. Cannot run.", NULL, NULL); } PROGRAM_INC_PROF_USES(program); PROGRAM_INC_INSTANCES(program); } ts_useobject(program); err = 0; instr_count = 0; mlev = ProgMLevel(program); gettimeofday(&fr->proftime, NULL); /* This is the 'natural' way to exit a function */ while (stop) { /* Abort program if player/thing running it is recycled */ if ((player < 0) || (player >= db_top) || ((Typeof(player) != TYPE_PLAYER) && (Typeof(player) != TYPE_THING))) { reload(fr, atop, stop); prog_clean(fr); interp_depth--; calc_profile_timing(program,fr); return NULL; } fr->instcnt++; instr_count++; if ((fr->multitask == PREEMPT) || (FLAGS(program) & BUILDER)) { if (mlev == 4) { if (tp_max_ml4_preempt_count) { if (instr_count >= tp_max_ml4_preempt_count) abort_loop_hard("Maximum preempt instruction count exceeded", NULL, NULL); } else instr_count = 0; } else { /* else make sure that the program doesn't run too long */ if (instr_count >= tp_max_instr_count) abort_loop_hard("Maximum preempt instruction count exceeded", NULL, NULL); } } else { /* if in FOREGROUND or BACKGROUND mode, '0 sleep' every so often. */ if ((fr->instcnt > tp_instr_slice * 4) && (instr_count >= tp_instr_slice)) { fr->pc = pc; reload(fr, atop, stop); PLAYER_SET_BLOCK(player, (!fr->been_background)); add_muf_delay_event(0, fr->descr, player, NOTHING, NOTHING, program, fr, (fr->multitask == FOREGROUND) ? "FOREGROUND" : "BACKGROUND"); interp_depth--; calc_profile_timing(program,fr); return NULL; } } if (((FLAGS(program) & ZOMBIE) || fr->brkpt.force_debugging) && !fr->been_background && controls(player, program) ) { fr->brkpt.debugging = 1; } else { fr->brkpt.debugging = 0; } if (FLAGS(program) & DARK || (fr->brkpt.debugging && fr->brkpt.showstack && !fr->brkpt.bypass)) { if ((pc->type != PROG_PRIMITIVE) || (pc->data.number != instno_debug_line)) { char *m = debug_inst(fr, 0, pc, fr->pid, arg, dbuf, sizeof(dbuf), atop, program); notify_nolisten(player, m, 1); } } if (fr->brkpt.debugging) { short breakflag = 0; if (stop == 1 && !fr->brkpt.bypass && pc->type == PROG_PRIMITIVE && pc->data.number == IN_RET ) { /* Program is about to EXIT */ notify_nolisten(player, "Program is about to EXIT.", 1); breakflag = 1; } else if (fr->brkpt.count) { for (i = 0; i < fr->brkpt.count; i++) { if ((!fr->brkpt.pc[i] || pc == fr->brkpt.pc[i]) && /* pc matches */ (fr->brkpt.line[i] == -1 || (fr->brkpt.lastline != pc->line && fr->brkpt.line[i] == pc->line)) && /* line matches */ (fr->brkpt.level[i] == -1 || stop <= fr->brkpt.level[i]) && /* level matches */ (fr->brkpt.prog[i] == NOTHING || fr->brkpt.prog[i] == program) && /* program matches */ (fr->brkpt.linecount[i] == -2 || (fr->brkpt.lastline != pc->line && fr->brkpt.linecount[i]-- <= 0)) && /* line count matches */ (fr->brkpt.pccount[i] == -2 || (fr->brkpt.lastpc != pc && fr->brkpt.pccount[i]-- <= 0)) /* pc count matches */ ) { if (fr->brkpt.bypass) { if (fr->brkpt.pccount[i] == -1) fr->brkpt.pccount[i] = 0; if (fr->brkpt.linecount[i] == -1) fr->brkpt.linecount[i] = 0; } else { breakflag = 1; break; } } } } if (breakflag) { char *m; char buf[BUFFER_LEN]; if (fr->brkpt.dosyspop) { program = sys[--stop].progref; pc = sys[stop].offset; } add_muf_read_event(fr->descr, player, program, fr); reload(fr, atop, stop); fr->pc = pc; fr->brkpt.isread = 0; fr->brkpt.breaknum = i; fr->brkpt.lastlisted = 0; fr->brkpt.bypass = 0; fr->brkpt.dosyspop = 0; PLAYER_SET_CURR_PROG(player, program); PLAYER_SET_BLOCK(player, 0); interp_depth--; if (!fr->brkpt.showstack) { m = debug_inst(fr, 0, pc, fr->pid, arg, dbuf, sizeof(dbuf), atop, program); notify_nolisten(player, m, 1); } if (pc <= PROGRAM_CODE(program) || (pc - 1)->line != pc->line) { list_proglines(player, program, fr, pc->line, 0); } else { m = show_line_prims(fr, program, pc, 15, 1); snprintf(buf, sizeof(buf), " %s", m); notify_nolisten(player, buf, 1); } calc_profile_timing(program,fr); return NULL; } fr->brkpt.lastline = pc->line; fr->brkpt.lastpc = pc; fr->brkpt.bypass = 0; } if (mlev < 3) { if (fr->instcnt > (tp_max_instr_count * ((mlev == 2) ? 4 : 1))) abort_loop_hard("Maximum total instruction count exceeded.", NULL, NULL); } switch (pc->type) { case PROG_INTEGER: case PROG_FLOAT: case PROG_ADD: case PROG_OBJECT: case PROG_VAR: case PROG_LVAR: case PROG_SVAR: case PROG_STRING: case PROG_LOCK: case PROG_MARK: case PROG_ARRAY: if (atop >= STACK_SIZE) abort_loop("Stack overflow.", NULL, NULL); copyinst(pc, arg + atop); pc++; atop++; break; case PROG_LVAR_AT: case PROG_LVAR_AT_CLEAR: { struct inst *tmp; struct localvars *lv; if (atop >= STACK_SIZE) abort_loop("Stack overflow.", NULL, NULL); if (pc->data.number >= MAX_VAR || pc->data.number < 0) abort_loop("Scoped variable number out of range.", NULL, NULL); lv = localvars_get(fr, program); tmp = &(lv->lvars[pc->data.number]); copyinst(tmp, arg + atop); if (pc->type == PROG_LVAR_AT_CLEAR) { CLEAR(tmp); tmp->type = PROG_INTEGER; tmp->data.number = 0; } pc++; atop++; } break; case PROG_LVAR_BANG: { struct inst *the_var; struct localvars *lv; if (atop < 1) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); if (pc->data.number >= MAX_VAR || pc->data.number < 0) abort_loop("Scoped variable number out of range.", NULL, NULL); lv = localvars_get(fr, program); the_var = &(lv->lvars[pc->data.number]); CLEAR(the_var); temp1 = arg + --atop; *the_var = *temp1; pc++; } break; case PROG_SVAR_AT: case PROG_SVAR_AT_CLEAR: { struct inst *tmp; if (atop >= STACK_SIZE) abort_loop("Stack overflow.", NULL, NULL); tmp = scopedvar_get(fr, 0, pc->data.number); if (!tmp) abort_loop("Scoped variable number out of range.", NULL, NULL); copyinst(tmp, arg + atop); if (pc->type == PROG_SVAR_AT_CLEAR) { CLEAR(tmp); tmp->type = PROG_INTEGER; tmp->data.number = 0; } pc++; atop++; } break; case PROG_SVAR_BANG: { struct inst *the_var; if (atop < 1) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); the_var = scopedvar_get(fr, 0, pc->data.number); if (!the_var) abort_loop("Scoped variable number out of range.", NULL, NULL); CLEAR(the_var); temp1 = arg + --atop; *the_var = *temp1; pc++; } break; case PROG_FUNCTION: { int i = pc->data.mufproc->args; if (atop < i) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < i) abort_loop("Stack protection fault.", NULL, NULL); if (fr->skip_declare) fr->skip_declare = 0; else scopedvar_addlevel(fr, pc, pc->data.mufproc->vars); while (i-->0) { struct inst *tmp; temp1 = arg + --atop; tmp = scopedvar_get(fr, 0, i); if (!tmp) abort_loop_hard("Internal error: Scoped variable number out of range in FUNCTION init.", temp1, NULL); CLEAR(tmp); copyinst(temp1, tmp); CLEAR(temp1); } pc++; } break; case PROG_IF: if (atop < 1) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (false_inst(temp1)) pc = pc->data.call; else pc++; CLEAR(temp1); break; case PROG_EXEC: if (stop >= STACK_SIZE) abort_loop("System Stack Overflow", NULL, NULL); sys[stop].progref = program; sys[stop++].offset = pc + 1; pc = pc->data.call; fr->skip_declare = 0; /* Make sure we DON'T skip var decls */ break; case PROG_JMP: /* Don't need to worry about skipping scoped var decls here. */ /* JMP to a function header can only happen in IN_JMP */ pc = pc->data.call; break; case PROG_TRY: if (atop < 1) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp1->type != PROG_INTEGER || temp1->data.number < 0) abort_loop("Argument is not a positive integer.", temp1, NULL); if (fr->trys.top && atop - fr->trys.st->depth < temp1->data.number) abort_loop("Stack protection fault.", NULL, NULL); if (temp1->data.number > atop) abort_loop("Stack Underflow.", temp1, NULL); fr->trys.top++; fr->trys.st = push_try(fr->trys.st); fr->trys.st->depth = atop - temp1->data.number; fr->trys.st->call_level = stop; fr->trys.st->for_count = 0; fr->trys.st->addr = pc->data.call; pc++; CLEAR(temp1); break; case PROG_PRIMITIVE: /* * All pc modifiers and stuff like that should stay here, * everything else call with an independent dispatcher. */ switch (pc->data.number) { case IN_JMP: if (atop < 1) abort_loop("Stack underflow. Missing address.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp1->type != PROG_ADD) abort_loop("Argument is not an address.", temp1, NULL); if (temp1->data.addr->progref >= db_top || temp1->data.addr->progref < 0 || (Typeof(temp1->data.addr->progref) != TYPE_PROGRAM)) abort_loop_hard("Internal error. Invalid address.", temp1, NULL); if (program != temp1->data.addr->progref) { abort_loop("Destination outside current program.", temp1, NULL); } if (temp1->data.addr->data->type == PROG_FUNCTION) { fr->skip_declare = 1; } pc = temp1->data.addr->data; CLEAR(temp1); break; case IN_EXECUTE: if (atop < 1) abort_loop("Stack Underflow. Missing address.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp1->type != PROG_ADD) abort_loop("Argument is not an address.", temp1, NULL); if (temp1->data.addr->progref >= db_top || temp1->data.addr->progref < 0 || (Typeof(temp1->data.addr->progref) != TYPE_PROGRAM)) abort_loop_hard("Internal error. Invalid address.", temp1, NULL); if (stop >= STACK_SIZE) abort_loop("System Stack Overflow", temp1, NULL); sys[stop].progref = program; sys[stop++].offset = pc + 1; if (program != temp1->data.addr->progref) { program = temp1->data.addr->progref; fr->caller.st[++fr->caller.top] = program; mlev = ProgMLevel(program); PROGRAM_INC_INSTANCES(program); } pc = temp1->data.addr->data; CLEAR(temp1); break; case IN_CALL: if (atop < 1) abort_loop("Stack Underflow. Missing dbref argument.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; temp2 = 0; if (temp1->type != PROG_OBJECT) { temp2 = temp1; if (atop < 1) abort_loop("Stack Underflow. Missing dbref of func.", temp1, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp2->type != PROG_STRING) abort_loop("Public Func. name string required. (2)", temp1, temp2); if (!temp2->data.string) abort_loop("Null string not allowed. (2)", temp1, temp2); } if (temp1->type != PROG_OBJECT) abort_loop("Dbref required. (1)", temp1, temp2); if (!valid_object(temp1) || Typeof(temp1->data.objref) != TYPE_PROGRAM) abort_loop("Invalid object.", temp1, temp2); if (!(PROGRAM_CODE(temp1->data.objref))) { struct line *tmpline; tmpline = PROGRAM_FIRST(temp1->data.objref); PROGRAM_SET_FIRST(temp1->data.objref, (struct line *) read_program(temp1->data.objref)); do_compile(-1, OWNER(temp1->data.objref), temp1->data.objref, 0); free_prog_text(PROGRAM_FIRST(temp1->data.objref)); PROGRAM_SET_FIRST(temp1->data.objref, tmpline); if (!(PROGRAM_CODE(temp1->data.objref))) abort_loop("Program not compilable.", temp1, temp2); } if (ProgMLevel(temp1->data.objref) == 0) abort_loop("Permission denied", temp1, temp2); if (mlev < 4 && OWNER(temp1->data.objref) != ProgUID && !Linkable(temp1->data.objref)) abort_loop("Permission denied", temp1, temp2); if (stop >= STACK_SIZE) abort_loop("System Stack Overflow", temp1, temp2); sys[stop].progref = program; sys[stop].offset = pc + 1; if (!temp2) { pc = PROGRAM_START(temp1->data.objref); } else { struct publics *pbs; int tmpint; pbs = PROGRAM_PUBS(temp1->data.objref); while (pbs) { tmpint = string_compare(temp2->data.string->data, pbs->subname); if (!tmpint) break; pbs = pbs->next; } if (!pbs) abort_loop("PUBLIC or WIZCALL function not found. (2)", temp2, temp2); if (mlev < pbs->mlev) abort_loop("Insufficient permissions to call WIZCALL function. (2)", temp2, temp2); pc = pbs->addr.ptr; } stop++; if (temp1->data.objref != program) { calc_profile_timing(program,fr); gettimeofday(&fr->proftime, NULL); program = temp1->data.objref; fr->caller.st[++fr->caller.top] = program; PROGRAM_INC_INSTANCES(program); mlev = ProgMLevel(program); } PROGRAM_INC_PROF_USES(program); ts_useobject(program); CLEAR(temp1); if (temp2) CLEAR(temp2); break; case IN_RET: if (stop > 1 && program != sys[stop - 1].progref) { if (sys[stop - 1].progref >= db_top || sys[stop - 1].progref < 0 || (Typeof(sys[stop - 1].progref) != TYPE_PROGRAM)) abort_loop_hard("Internal error. Invalid address.", NULL, NULL); calc_profile_timing(program,fr); gettimeofday(&fr->proftime, NULL); PROGRAM_DEC_INSTANCES(program); program = sys[stop - 1].progref; mlev = ProgMLevel(program); fr->caller.top--; } scopedvar_poplevel(fr); pc = sys[--stop].offset; break; case IN_CATCH: case IN_CATCH_DETAILED: { int depth; if (!(fr->trys.top)) abort_loop_hard("Internal error. TRY stack underflow.", NULL, NULL); depth = fr->trys.st->depth; while (atop > depth) { temp1 = arg + --atop; CLEAR(temp1); } while (fr->trys.st->for_count-->0) { CLEAR(&fr->fors.st->cur); CLEAR(&fr->fors.st->end); fr->fors.top--; fr->fors.st = pop_for(fr->fors.st); } fr->trys.top--; fr->trys.st = pop_try(fr->trys.st); if (pc->data.number == IN_CATCH) { /* IN_CATCH */ if (fr->errorstr) { arg[atop].type = PROG_STRING; arg[atop++].data.string = alloc_prog_string(fr->errorstr); free(fr->errorstr); fr->errorstr = NULL; } else { arg[atop].type = PROG_STRING; arg[atop++].data.string = NULL; } if (fr->errorinst) { free(fr->errorinst); fr->errorinst = NULL; } } else { /* IN_CATCH_DETAILED */ stk_array *nu = new_array_dictionary(); if (fr->errorstr) { array_set_strkey_strval(&nu, "error", fr->errorstr); free(fr->errorstr); fr->errorstr = NULL; } if (fr->errorinst) { array_set_strkey_strval(&nu, "instr", fr->errorinst); free(fr->errorinst); fr->errorinst = NULL; } array_set_strkey_intval(&nu, "line", fr->errorline); array_set_strkey_refval(&nu, "program", fr->errorprog); arg[atop].type = PROG_ARRAY; arg[atop++].data.array = nu; } reload(fr, atop, stop); } pc++; break; case IN_EVENT_WAITFOR: if (atop < 1) abort_loop("Stack Underflow. Missing eventID list array argument.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp1->type != PROG_ARRAY) abort_loop("EventID string list array expected.", temp1, NULL); if (temp1->data.array && temp1->data.array->type != ARRAY_PACKED) abort_loop("Argument must be a list array of eventid strings.", temp1, NULL); if (!array_is_homogenous(temp1->data.array, PROG_STRING)) abort_loop("Argument must be a list array of eventid strings.", temp1, NULL); fr->pc = pc + 1; reload(fr, atop, stop); { int i, outcount; int count = array_count(temp1->data.array); char** events = (char**)malloc(count * sizeof(char**)); for (outcount = i = 0; i < count; i++) { char *val = array_get_intkey_strval(temp1->data.array, i); if (val != NULL) { int found = 0; int j; for (j = 0; j < outcount; j++) { if (!strcmp(events[j], val)) { found = 1; break; } } if (!found) { events[outcount++] = val; } } } muf_event_register_specific(player, program, fr, outcount, events); free(events); } PLAYER_SET_BLOCK(player, (!fr->been_background)); CLEAR(temp1); interp_depth--; calc_profile_timing(program,fr); return NULL; /* NOTREACHED */ break; case IN_READ: if (writeonly) abort_loop("Program is write-only.", NULL, NULL); if (fr->multitask == BACKGROUND) abort_loop("BACKGROUND programs are write only.", NULL, NULL); reload(fr, atop, stop); fr->brkpt.isread = 1; fr->pc = pc + 1; PLAYER_SET_CURR_PROG(player, program); PLAYER_SET_BLOCK(player, 0); add_muf_read_event(fr->descr, player, program, fr); interp_depth--; calc_profile_timing(program,fr); return NULL; /* NOTREACHED */ break; case IN_SLEEP: if (atop < 1) abort_loop("Stack Underflow.", NULL, NULL); if (fr->trys.top && atop - fr->trys.st->depth < 1) abort_loop("Stack protection fault.", NULL, NULL); temp1 = arg + --atop; if (temp1->type != PROG_INTEGER) abort_loop("Invalid argument type.", temp1, NULL); fr->pc = pc + 1; reload(fr, atop, stop); if (temp1->data.number < 0) abort_loop("Timetravel beyond scope of muf.", temp1, NULL); add_muf_delay_event(temp1->data.number, fr->descr, player, NOTHING, NOTHING, program, fr, "SLEEPING"); PLAYER_SET_BLOCK(player, (!fr->been_background)); interp_depth--; calc_profile_timing(program,fr); return NULL; /* NOTREACHED */ break; default: nargs = 0; reload(fr, atop, stop); tmp = atop; prim_func[pc->data.number - 1] (player, program, mlev, pc, arg, &tmp, fr); atop = tmp; pc++; break; } /* switch */ break; case PROG_CLEARED: log_status("WARNING: attempt to execute instruction cleared by %s:%hd in program %d", (char*)pc->data.addr, pc->line, program); pc = NULL; abort_loop_hard("Program internal error. Program erroneously freed from memory.", NULL, NULL); default: pc = NULL; abort_loop_hard("Program internal error. Unknown instruction type.", NULL, NULL); } /* switch */ if (err) { if (err != ERROR_DIE_NOW && fr->trys.top) { while (fr->trys.st->call_level < stop) { if (stop > 1 && program != sys[stop - 1].progref) { if (sys[stop - 1].progref >= db_top || sys[stop - 1].progref < 0 || (Typeof(sys[stop - 1].progref) != TYPE_PROGRAM)) abort_loop_hard("Internal error. Invalid address.", NULL, NULL); calc_profile_timing(program,fr); gettimeofday(&fr->proftime, NULL); PROGRAM_DEC_INSTANCES(program); program = sys[stop - 1].progref; mlev = ProgMLevel(program); fr->caller.top--; } scopedvar_poplevel(fr); stop--; } pc = fr->trys.st->addr; err = 0; } else { reload(fr, atop, stop); prog_clean(fr); PLAYER_SET_BLOCK(player, 0); interp_depth--; calc_profile_timing(program,fr); return NULL; } } } /* while */ PLAYER_SET_BLOCK(player, 0); if (atop) { struct inst *rv; if (rettyp) { copyinst(arg + atop - 1, &retval); rv = &retval; } else { if (!false_inst(arg + atop - 1)) { rv = (struct inst *) 1; } else { rv = NULL; } } reload(fr, atop, stop); prog_clean(fr); interp_depth--; calc_profile_timing(program,fr); return rv; } reload(fr, atop, stop); prog_clean(fr); interp_depth--; calc_profile_timing(program,fr); return NULL; }
/** * Implementation of the MUF debugger * * This implements the command parsing for the MUF debugger. It also clears * temporary bookmarks if this was triggered from a temporary one. * * This relies on some static globals, so it is not threadsafe. If the * 'prim' debugger command is ever used to trigger the MUF debugger somehow * in a recursive fashion, and then you call 'prim' again, it will probably * cause havock. * * @param descr the descriptor of the debugging player * @param player the debugging player * @param program the program we are debugging * @param text the input text from the user * @param fr the current frame pointer * @return boolean true if the program should exit, false if not */ int muf_debugger(int descr, dbref player, dbref program, const char *text, struct frame *fr) { char cmd[BUFFER_LEN]; char buf[BUFFER_LEN]; char buf2[BUFFER_LEN]; char *ptr, *ptr2, *arg; struct inst *pinst; int i, j, cnt; static struct inst primset[5]; static struct muf_proc_data temp_muf_proc_data = { "__Temp_Debugger_Proc", 0, 0, NULL }; /* * Basic massaging of the input - clearing spaces, finding the * argument. */ skip_whitespace(&text); strcpyn(cmd, sizeof(cmd), text); ptr = cmd; remove_ending_whitespace(&ptr); for (arg = cmd; *arg && !isspace(*arg); arg++) ; if (*arg) *arg++ = '\0'; /* Empty command means repeat last command, if available */ if (!*cmd && fr->brkpt.lastcmd) { strcpyn(cmd, sizeof(cmd), fr->brkpt.lastcmd); } else { free(fr->brkpt.lastcmd); if (*cmd) fr->brkpt.lastcmd = strdup(cmd); } /* delete triggering breakpoint, if it's only temp. */ j = fr->brkpt.breaknum; if (j >= 0 && fr->brkpt.temp[j]) { for (j++; j < fr->brkpt.count; j++) { fr->brkpt.temp[j - 1] = fr->brkpt.temp[j]; fr->brkpt.level[j - 1] = fr->brkpt.level[j]; fr->brkpt.line[j - 1] = fr->brkpt.line[j]; fr->brkpt.linecount[j - 1] = fr->brkpt.linecount[j]; fr->brkpt.pc[j - 1] = fr->brkpt.pc[j]; fr->brkpt.pccount[j - 1] = fr->brkpt.pccount[j]; fr->brkpt.prog[j - 1] = fr->brkpt.prog[j]; } fr->brkpt.count--; } fr->brkpt.breaknum = -1; /** * @TODO This giant if statement is pretty gnarly; we'd be better * of looping over an array of callbacks to break this up * nicer and make it more extensible. */ if (!strcasecmp(cmd, "cont")) { /* Nothing to do -- this will continue to next breakpoint */ } else if (!strcasecmp(cmd, "finish")) { if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top - 1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "stepi")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot stepi because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = i; fr->brkpt.prog[j] = NOTHING; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "step")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot step because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = i; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = NOTHING; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "nexti")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot nexti because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = i; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "next")) { i = atoi(arg); if (!i) i = 1; if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot next because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = i; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "exec")) { if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } if (!(pinst = funcname_to_pc(program, arg))) { notify_nolisten(player, "I don't know a function by that name.", 1); add_muf_read_event(descr, player, program, fr); return 0; } if (fr->system.top >= STACK_SIZE) { notify_nolisten(player, "That would exceed the system stack size for this program.", 1); add_muf_read_event(descr, player, program, fr); return 0; } fr->system.st[fr->system.top].progref = program; fr->system.st[fr->system.top++].offset = fr->pc; fr->pc = pinst; j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = fr->system.top - 1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; return 0; } else if (!strcasecmp(cmd, "prim")) { /* * @TODO The way this works is a little funky. It looks like * it would be possible to cause some weird havoc if we * manage to run a primitive that in turn triggers muf_debugger * * I am uncertain if this is possible; looks like the only * way it could happen is by typing 'prim debugger_break' * but I don't know much about about how muf_debugger is * triggered. Some digging should be done to make this * safe. * * Even better would be to not use statics for this somehow * without introducing a memory leak. (tanabi) */ if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Cannot finish because there are too many breakpoints set.", 1); add_muf_read_event(descr, player, program, fr); return 0; } if (!primitive(arg)) { notify_nolisten(player, "I don't recognize that primitive.", 1); add_muf_read_event(descr, player, program, fr); return 0; } if (fr->system.top >= STACK_SIZE) { notify_nolisten(player, "That would exceed the system stack size for this program.", 1); add_muf_read_event(descr, player, program, fr); return 0; } primset[0].type = PROG_FUNCTION; primset[0].line = 0; primset[0].data.mufproc = &temp_muf_proc_data; primset[0].data.mufproc->vars = 0; primset[0].data.mufproc->args = 0; primset[0].data.mufproc->varnames = NULL; primset[1].type = PROG_PRIMITIVE; primset[1].line = 0; primset[1].data.number = get_primitive(arg); primset[2].type = PROG_PRIMITIVE; primset[2].line = 0; primset[2].data.number = IN_RET; /* primset[3].data.number = primitive("EXIT"); */ fr->system.st[fr->system.top].progref = program; fr->system.st[fr->system.top++].offset = fr->pc; fr->pc = &primset[1]; j = fr->brkpt.count++; fr->brkpt.temp[j] = 1; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = -1; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = &primset[2]; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; fr->brkpt.bypass = 1; fr->brkpt.dosyspop = 1; return 0; } else if (!strcasecmp(cmd, "break")) { add_muf_read_event(descr, player, program, fr); if (fr->brkpt.count >= MAX_BREAKS) { notify_nolisten(player, "Too many breakpoints set.", 1); return 0; } if (number(arg)) { i = atoi(arg); } else { if (!(pinst = funcname_to_pc(program, arg))) { notify_nolisten(player, "I don't know a function by that name.", 1); return 0; } else { i = pinst->line; } } if (!i) i = fr->pc->line; j = fr->brkpt.count++; fr->brkpt.temp[j] = 0; fr->brkpt.level[j] = -1; fr->brkpt.line[j] = i; fr->brkpt.linecount[j] = -2; fr->brkpt.pc[j] = NULL; fr->brkpt.pccount[j] = -2; fr->brkpt.prog[j] = program; notify_nolisten(player, "Breakpoint set.", 1); return 0; } else if (!strcasecmp(cmd, "delete")) { add_muf_read_event(descr, player, program, fr); i = atoi(arg); if (!i) { notify_nolisten(player, "Which breakpoint did you want to delete?", 1); return 0; } if (i < 1 || i > fr->brkpt.count) { notify_nolisten(player, "No such breakpoint.", 1); return 0; } j = i - 1; for (j++; j < fr->brkpt.count; j++) { fr->brkpt.temp[j - 1] = fr->brkpt.temp[j]; fr->brkpt.level[j - 1] = fr->brkpt.level[j]; fr->brkpt.line[j - 1] = fr->brkpt.line[j]; fr->brkpt.linecount[j - 1] = fr->brkpt.linecount[j]; fr->brkpt.pc[j - 1] = fr->brkpt.pc[j]; fr->brkpt.pccount[j - 1] = fr->brkpt.pccount[j]; fr->brkpt.prog[j - 1] = fr->brkpt.prog[j]; } fr->brkpt.count--; notify_nolisten(player, "Breakpoint deleted.", 1); return 0; } else if (!strcasecmp(cmd, "breaks")) { notify_nolisten(player, "Breakpoints:", 1); for (i = 0; i < fr->brkpt.count; i++) { ptr = unparse_breakpoint(fr, i); notify_nolisten(player, ptr, 1); } notify_nolisten(player, "*done*", 1); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "where")) { i = atoi(arg); muf_backtrace(player, program, i, fr); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "stack")) { notify_nolisten(player, "*Argument stack top*", 1); i = atoi(arg); if (!i) i = STACK_SIZE; ptr = ""; for (j = fr->argument.top; j > 0 && i-- > 0;) { cnt = 0; do { strcpyn(buf, sizeof(buf), ptr); ptr = insttotext(NULL, 0, &fr->argument.st[--j], buf2, sizeof(buf2), 4000, program, 1); cnt++; } while (!strcasecmp(ptr, buf) && j > 0); if (cnt > 1) notifyf(player, " [repeats %d times]", cnt); if (strcasecmp(ptr, buf)) notifyf(player, "%3d) %s", j + 1, ptr); } notify_nolisten(player, "*done*", 1); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "list") || !strcasecmp(cmd, "listi")) { int startline, endline; add_muf_read_event(descr, player, program, fr); if ((ptr2 = (char *) strchr(arg, ','))) { *ptr2++ = '\0'; } else { ptr2 = ""; } if (!*arg) { if (fr->brkpt.lastlisted) { startline = fr->brkpt.lastlisted + 1; } else { startline = fr->pc->line; } endline = startline + 15; } else { if (!number(arg)) { if (!(pinst = funcname_to_pc(program, arg))) { notify_nolisten(player, "I don't know a function by that name. (starting arg, 1)", 1); return 0; } else { startline = pinst->line; endline = startline + 15; } } else { if (*ptr2) { endline = startline = atoi(arg); } else { startline = atoi(arg) - 7; endline = startline + 15; } } } if (*ptr2) { if (!number(ptr2)) { if (!(pinst = funcname_to_pc(program, ptr2))) { notify_nolisten(player, "I don't know a function by that name. (ending arg, 1)", 1); return 0; } else { endline = pinst->line; } } else { endline = atoi(ptr2); } } i = (PROGRAM_CODE(program) + PROGRAM_SIZ(program) - 1)->line; if (startline > i) { notify_nolisten(player, "Starting line is beyond end of program.", 1); return 0; } if (startline < 1) startline = 1; if (endline > i) endline = i; if (endline < startline) endline = startline; notify_nolisten(player, "Listing:", 1); if (!strcasecmp(cmd, "listi")) { for (i = startline; i <= endline; i++) { pinst = linenum_to_pc(program, i); if (pinst) { notifyf_nolisten(player, "line %d: %s", i, (i == fr->pc->line) ? show_line_prims(program, fr->pc, STACK_SIZE, 1) : show_line_prims(program, pinst, STACK_SIZE, 0)); } } } else { list_proglines(player, program, fr, startline, endline); } fr->brkpt.lastlisted = endline; notify_nolisten(player, "*done*", 1); return 0; } else if (!strcasecmp(cmd, "quit")) { notify_nolisten(player, "Halting execution.", 1); return 1; } else if (!strcasecmp(cmd, "trace")) { add_muf_read_event(descr, player, program, fr); if (!strcasecmp(arg, "on")) { fr->brkpt.showstack = 1; notify_nolisten(player, "Trace turned on.", 1); } else if (!strcasecmp(arg, "off")) { fr->brkpt.showstack = 0; notify_nolisten(player, "Trace turned off.", 1); } else { notifyf_nolisten(player, "Trace is currently %s.", fr->brkpt.showstack ? "on" : "off"); } return 0; } else if (!strcasecmp(cmd, "words")) { list_program_functions(player, program, arg); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "print")) { debug_printvar(player, program, fr, arg); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "push")) { push_arg(player, fr, arg); add_muf_read_event(descr, player, program, fr); return 0; } else if (!strcasecmp(cmd, "pop")) { add_muf_read_event(descr, player, program, fr); if (fr->argument.top < 1) { notify_nolisten(player, "Nothing to pop.", 1); return 0; } fr->argument.top--; CLEAR(fr->argument.st + fr->argument.top); notify_nolisten(player, "Stack item popped.", 1); return 0; } else if (!strcasecmp(cmd, "help")) { do_helpfile(player, tp_file_man_dir, tp_file_man, "debugger_commands", ""); return 0; } else { notify_nolisten(player, "I don't understand that debugger command. Type 'help' for help.", 1); add_muf_read_event(descr, player, program, fr); return 0; } return 0; }