#include "copyright.h" #include "config.h" #include "db.h" #include "tune.h" #include "props.h" #include "inst.h" #include "externs.h" #include "interface.h" #include "fbstrings.h" #include "interp.h" #undef DEBUGARRAYS /* these arrays MUST agree with what's in inst.h */ const char *base_inst[] = { "JMP", "READ", "SLEEP", "CALL", "EXECUTE", "EXIT", "EVENT_WAITFOR", "CATCH", "CATCH_DETAILED", PRIMS_CONNECTS_NAMES, PRIMS_DB_NAMES, PRIMS_MATH_NAMES, PRIMS_MISC_NAMES, PRIMS_PROPS_NAMES, PRIMS_STACK_NAMES, PRIMS_STRINGS_NAMES, PRIMS_ARRAY_NAMES, PRIMS_FLOAT_NAMES, PRIMS_ERROR_NAMES, PRIMS_MCP_NAMES, PRIMS_REGEX_NAMES, PRIMS_INTERNAL_NAMES }; /* converts an instruction into a printable string, stores the string in buffer and returns a pointer to it. the first byte of the return value will be NULL if a buffer overflow would have occured. */ char * insttotext(struct frame *fr, int lev, struct inst *theinst, char *buffer, int buflen, int strmax, dbref program, int expandarrs) { const char* ptr; char buf2[BUFFER_LEN]; struct inst temp1; struct inst *oper2; int length = -2; /* unset mark. We don't use -1 since some snprintf() version will return that if we would've overflowed. */ int firstflag = 1; int arrcount = 0; assert(buflen > 0); strmax = (strmax > buflen - 3) ? buflen - 3 : strmax; switch (theinst->type) { case PROG_PRIMITIVE: if (theinst->data.number >= BASE_MIN && theinst->data.number <= BASE_MAX) { ptr = base_inst[theinst->data.number - BASE_MIN]; if (strlen(ptr) >= buflen) *buffer = '\0'; else strcpy(buffer, ptr); } else { if (buflen > 3) strcpy(buffer, "???"); else *buffer = '\0'; } break; case PROG_STRING: if (!theinst->data.string) { if (buflen > 2) strcpy(buffer, "\"\""); else *buffer = '\0'; break; } if (strmax <= 0) { *buffer = '\0'; break; } /* we know we won't overflow, so don't set length */ snprintf(buffer, buflen, "\"%1.*s\"", (strmax - 1), theinst->data.string->data); if (theinst->data.string->length > strmax) strcatn(buffer, buflen, "_"); break; case PROG_MARK: if (buflen > 4) strcpy(buffer, "MARK"); else *buffer = '\0'; break; case PROG_ARRAY: if (!theinst->data.array) { if (buflen > 3) strcpy(buffer, "0{}"); else *buffer = '\0'; break; } if (tp_expanded_debug && expandarrs) { #ifdef DEBUGARRAYS length = snprintf(buffer, buflen, "R%dC%d{", theinst->data.array->links, theinst->data.array->items); #else length = snprintf(buffer, buflen, "%d{", theinst->data.array->items); #endif if (length >= buflen || length == -1) { /* >= because we need room for one more charctor at the end */ *buffer = '\0'; break; } /* - 1 for the "\0" at the end. */ length = buflen - length - 1; firstflag = 1; arrcount = 0; if (array_first(theinst->data.array, &temp1)) { do { char *inststr; if (arrcount++ >= 8) { strcatn(buffer, buflen, "_"); break; } if (!firstflag) { strcatn(buffer, buflen, " "); length--; } firstflag = 0; oper2 = array_getitem(theinst->data.array, &temp1); if (length <= 2) { /* no space left, let's not pass a buflen of 0 */ strcatn(buffer, buflen, "_"); break; } /* length - 2 so we have room for the ":_" */ inststr = insttotext(fr, lev, &temp1, buf2, length - 2, strmax, program, 0); if (!*inststr) { /* overflow problem. */ strcatn(buffer, buflen, "_"); break; } length -= strlen(inststr) + 1; strcatn(buffer, buflen, inststr); strcatn(buffer, buflen, ":"); if (length <= 2) { /* no space left, let's not pass a buflen of 0 */ strcatn(buffer, buflen, "_"); break; } inststr = insttotext(fr, lev, oper2, buf2, length, strmax, program, 0); if (!*inststr) { /* we'd overflow if we did that */ /* as before add a "_" and let it be. */ strcatn(buffer, buflen, "_"); break; } length -= strlen(inststr); strcatn(buffer, buflen, inststr); if (length < 2) { /* we should have a length of exactly 1, if we get here. * So we just have enough room for a '_' now. * Just append the "_" and stop this madness. */ strcatn(buffer, buflen, "_"); length--; break; } } while (array_next(theinst->data.array, &temp1)); } strcatn(buffer, buflen, "}"); } else { length = snprintf(buffer, buflen, "%d{...}", theinst->data.array->items); } break; case PROG_INTEGER: length = snprintf(buffer, buflen, "%d", theinst->data.number); break; case PROG_FLOAT: length = snprintf(buffer, buflen, "%.16g", theinst->data.fnumber); if (!strchr(buffer, '.') && !strchr(buffer, 'n') && !strchr(buffer, 'e')) { strcatn(buffer, buflen, ".0"); } break; case PROG_ADD: if (theinst->data.addr->data->type == PROG_FUNCTION && theinst->data.addr->data->data.mufproc != NULL) { if (theinst->data.addr->progref != program) length = snprintf(buffer, buflen, "'#%d'%s", theinst->data.addr->progref, theinst->data.addr->data->data.mufproc->procname); else length = snprintf(buffer, buflen, "'%s", theinst->data.addr->data->data.mufproc->procname); } else { if (theinst->data.addr->progref != program) length = snprintf(buffer, buflen, "'#%d'line%d?", theinst->data.addr->progref, theinst->data.addr->data->line); else length = snprintf(buffer, buflen, "'line%d?", theinst->data.addr->data->line); } break; case PROG_TRY: length = snprintf(buffer, buflen, "TRY->line%d", theinst->data.call->line); break; case PROG_IF: length = snprintf(buffer, buflen, "IF->line%d", theinst->data.call->line); break; case PROG_EXEC: if (theinst->data.call->type == PROG_FUNCTION) { length = snprintf(buffer, buflen, "EXEC->%s", theinst->data.call->data.mufproc->procname); } else { length = snprintf(buffer, buflen, "EXEC->line%d", theinst->data.call->line); } break; case PROG_JMP: if (theinst->data.call->type == PROG_FUNCTION) { length = snprintf(buffer, buflen, "JMP->%s", theinst->data.call->data.mufproc->procname); } else { length = snprintf(buffer, buflen, "JMP->line%d", theinst->data.call->line); } break; case PROG_OBJECT: length = snprintf(buffer, buflen, "#%d", theinst->data.objref); break; case PROG_VAR: length = snprintf(buffer, buflen, "V%d", theinst->data.number); break; case PROG_SVAR: if (fr) { length = snprintf(buffer, buflen, "SV%d:%s", theinst->data.number, scopedvar_getname(fr, lev, theinst->data.number)); } else { length = snprintf(buffer, buflen, "SV%d", theinst->data.number); } break; case PROG_SVAR_AT: case PROG_SVAR_AT_CLEAR: if (fr) { length = snprintf(buffer, buflen, "SV%d:%s @", theinst->data.number, scopedvar_getname(fr, lev, theinst->data.number)); } else { length = snprintf(buffer, buflen, "SV%d @", theinst->data.number); } break; case PROG_SVAR_BANG: if (fr) { length = snprintf(buffer, buflen, "SV%d:%s !", theinst->data.number, scopedvar_getname(fr, lev, theinst->data.number)); } else { length = snprintf(buffer, buflen, "SV%d !", theinst->data.number); } break; case PROG_LVAR: length = snprintf(buffer, buflen, "LV%d", theinst->data.number); break; case PROG_LVAR_AT: case PROG_LVAR_AT_CLEAR: length = snprintf(buffer, buflen, "LV%d @", theinst->data.number); break; case PROG_LVAR_BANG: length = snprintf(buffer, buflen, "LV%d !", theinst->data.number); break; case PROG_FUNCTION: length = snprintf(buffer, buflen, "INIT FUNC: %s (%d arg%s)", theinst->data.mufproc->procname, theinst->data.mufproc->args, theinst->data.mufproc->args == 1? "" : "s" ); break; case PROG_LOCK: if (theinst->data.lock == TRUE_BOOLEXP) { /* 12345678901234 */ /* 14 */ if (buflen > 14) strcpy(buffer, "[TRUE_BOOLEXP]"); else *buffer = '\0'; break; } length = snprintf(buffer, buflen, "[%1.*s]", (strmax - 1), unparse_boolexp(0, theinst->data.lock, 0)); break; case PROG_CLEARED: length = snprintf(buffer, buflen, "?<%s:%d>", (char *) theinst->data.addr, theinst->line); break; default: if (buflen > 3) strcpy(buffer, "?"); else *buffer = '\0'; break; } if (length == -1 || length > buflen) *buffer = '\0'; return buffer; } /* produce one line summary of current state. Note that sp is the next * space on the stack -- 0..sp-1 is the current contents. */ #define DEBUG_DEPTH 8 /* how far to give a stack list, at most */ char * debug_inst(struct frame *fr, int lev, struct inst *pc, int pid, struct inst *stack, char *buffer, int buflen, int sp, dbref program) { char* bend; char* bstart; char* ptr; int length; char buf2[BUFFER_LEN]; /* To hold Debug> ... at the beginning */ char buf3[64]; int count; assert(buflen > 1); buffer[buflen - 1] = '\0'; #ifdef DEBUGARRAYS length = snprintf(buf3, sizeof(buf3), "Debug> (%d Insts.) #%d %d (", PROGRAM_INSTANCES(2), program, pc->line); #else length = snprintf(buf3, sizeof(buf3), "Debug> Pid %d: #%d %d (", pid, program, pc->line); #endif if (length == -1) { length = sizeof(buf3) - 1; } bstart = buffer + length; /* start far enough away so we can fit Debug> #xxx xxx ( thingy. */ length = buflen - length - 1; /* - 1 for the '\0' */ bend = buffer + (buflen - 1); /* - 1 for the '\0' */ /* + 10 because we must at least be able to store " ... ) ..." after that. */ if (bstart + 10 > bend) { /* we have no room. Eeek! */ /* 123456789012345678 */ memcpy((void*)buffer, (const void*)"Need buffer space!", (buflen - 1 > 18) ? 18 : buflen - 1 ); return buffer; } /* We use this if-else structure to handle errors and such nicely. */ /* We use length - 7 so we KNOW we'll have room for " ... ) " */ /* 1234567890 */ ptr = insttotext(fr, lev, pc, buf2, length - 7, 30, program, 1); if (*ptr) { length -= prepend_string(&bend, bstart, ptr); } else { strcpy(buffer, buf3); strcatn(buffer, buflen, " ... ) ..."); return buffer; } length -= prepend_string(&bend, bstart, ") "); count = sp - 1; if (count >= 0) { for(;;) { if (count && length <= 5) { length -= prepend_string(&bend, bstart, "..."); break; } /* we use length - 5 to leave room for "..., " * ... except if we're outputing the last item (count == 0) */ ptr = insttotext(fr, lev, stack + count, buf2, (count) ? length - 5 : length, 30, program, 1); if (*ptr) { length -= prepend_string(&bend, bstart, ptr); } else { length -= prepend_string(&bend, bstart, "..."); break; /* done because we couldn't display all that */ } if (count > 0 && count > sp - 8) { length -= prepend_string(&bend, bstart, ", "); } else { if (count) length -= prepend_string(&bend, bstart, "..., "); break; /* all done! */ } count--; } } /* we don't use bstart, because it's compensated for the length of this. */ prepend_string(&bend, buffer, buf3); /* and return the pointer to the beginning of our backwards grown string. */ return bend; }