1 /* Dbg.c - Tcl Debugger - See cmdHelp() for commands
3 Written by: Don Libes, NIST, 3/23/93
5 Design and implementation of this program was paid for by U.S. tax
6 dollars. Therefore it is public domain. However, the author and NIST
7 would appreciate credit if this program or parts of it are used.
14 #define strchr(s,c) index(s,c)
15 #endif /* HAVE_STRCHR */
18 /* tclInt.h drags in stdlib. By claiming no-stdlib, force it to drag in */
19 /* Tcl's compat version. This avoids having to test for its presence */
20 /* which is too tricky - configure can't generate two cf files, so when */
21 /* Expect (or any app) uses the debugger, there's no way to get the info */
22 /* about whether stdlib exists or not, except pointing the debugger at */
23 /* an app-dependent .h file and I don't want to do that. */
29 /*#include <varargs.h> tclInt.h drags in varargs.h. Since Pyramid */
30 /* objects to including varargs.h twice, just */
32 /*#include "string.h" tclInt.h drags this in, too! */
40 static int simple_interactor (Tcl_Interp *interp, ClientData data);
41 static int zero (Tcl_Interp *interp, char *string);
43 /* most of the static variables in this file may be */
44 /* moved into Tcl_Interp */
46 static Dbg_InterProc *interactor = &simple_interactor;
47 static ClientData interdata = 0;
48 static Dbg_IgnoreFuncsProc *ignoreproc = &zero;
49 static Dbg_OutputProc *printproc = 0;
50 static ClientData printdata = 0;
53 static void print _ANSI_ARGS_(TCL_VARARGS(Tcl_Interp *,interp));
55 static int debugger_active = FALSE;
57 /* this is not externally documented anywhere as of yet */
58 char *Dbg_VarName = "dbg";
60 #define DEFAULT_COMPRESS 0
61 static int compress = DEFAULT_COMPRESS;
62 #define DEFAULT_WIDTH 75 /* leave a little space for printing */
64 static int buf_width = DEFAULT_WIDTH;
66 static int main_argc = 1;
67 static char *default_argv = "application";
68 static char **main_argv = &default_argv;
70 static Tcl_Trace debug_handle;
71 static int step_count = 1; /* count next/step */
73 #define FRAMENAMELEN 10 /* enough to hold strings like "#4" */
74 static char viewFrameName[FRAMENAMELEN];/* destination frame name for up/down */
76 static CallFrame *goalFramePtr; /* destination for next/return */
77 static int goalNumLevel; /* destination for Next */
79 static enum debug_cmd {
80 none, step, next, ret, cont, up, down, where, Next
83 /* info about last action to use as a default */
84 static enum debug_cmd last_action_cmd = next;
85 static int last_step_count = 1;
87 /* this acts as a strobe (while testing breakpoints). It is set to true */
88 /* every time a new debugger command is issued that is an action */
89 static int debug_new_action;
91 #define NO_LINE -1 /* if break point is not set by line number */
95 Tcl_Obj *file; /* file where breakpoint is */
96 int line; /* line where breakpoint is */
97 int re; /* 1 if this is regexp pattern */
98 Tcl_Obj *pat; /* pattern defining where breakpoint can be */
99 Tcl_Obj *expr; /* expr to trigger breakpoint */
100 Tcl_Obj *cmd; /* cmd to eval at breakpoint */
101 struct breakpoint *next, *previous;
104 static struct breakpoint *break_base = 0;
105 static int breakpoint_max_id = 0;
107 static struct breakpoint *
110 struct breakpoint *b = (struct breakpoint *)ckalloc(sizeof(struct breakpoint));
111 if (break_base) break_base->previous = b;
112 b->next = break_base;
114 b->id = breakpoint_max_id++;
127 breakpoint_print(interp,b)
129 struct breakpoint *b;
131 print(interp,"breakpoint %d: ",b->id);
134 print(interp,"-re \"%s\" ",Tcl_GetString(b->pat));
136 print(interp,"-glob \"%s\" ",Tcl_GetString(b->pat));
137 } else if (b->line != NO_LINE) {
139 print(interp,"%s:",Tcl_GetString(b->file));
141 print(interp,"%d ",b->line);
145 print(interp,"if {%s} ",Tcl_GetString(b->expr));
148 print(interp,"then {%s}",Tcl_GetString(b->cmd));
154 save_re_matches(interp, re, objPtr)
163 Tcl_RegExpGetInfo(re, &info);
164 for (i=0;i<=info.nsubs;i++) {
165 start = info.matches[i].start;
166 /* end = info.matches[i].end-1;*/
168 if (start == -1) continue;
170 sprintf(name,"%d",i);
171 Tcl_SetVar2Ex(interp, Dbg_VarName, name, Tcl_GetRange(objPtr,
172 info.matches[i].start, info.matches[i].end-1), 0);
176 /* return 1 to break, 0 to continue */
178 breakpoint_test(interp,cmd,bp)
180 char *cmd; /* command about to be executed */
181 struct breakpoint *bp; /* breakpoint to test */
186 Tcl_RegExp re = Tcl_GetRegExpFromObj(NULL, bp->pat,
188 cmdObj = Tcl_NewStringObj(cmd,-1);
189 Tcl_IncrRefCount(cmdObj);
190 if (Tcl_RegExpExecObj(NULL, re, cmdObj, 0 /* offset */,
191 -1 /* nmatches */, 0 /* eflags */) > 0) {
192 save_re_matches(interp, re, cmdObj);
195 Tcl_DecrRefCount(cmdObj);
196 if (!found) return 0;
197 } else if (bp->pat) {
198 if (0 == Tcl_StringMatch(cmd,
199 Tcl_GetString(bp->pat))) return 0;
200 } else if (bp->line != NO_LINE) {
201 /* not yet implemented - awaiting support from Tcl */
208 /* ignore errors, since they are likely due to */
209 /* simply being out of scope a lot */
210 if (TCL_OK != Tcl_ExprBooleanObj(interp,bp->expr,&value)
211 || (value == 0)) return 0;
215 Tcl_EvalObjEx(interp, bp->cmd, 0);
217 breakpoint_print(interp,bp);
223 static char *already_at_top_level = "already at top level";
225 /* similar to TclGetFrame but takes two frame ptrs and a direction.
226 If direction is up, search up stack from curFrame
227 If direction is down, simulate searching down stack by
228 seaching up stack from origFrame
232 TclGetFrame2(interp, origFramePtr, string, framePtrPtr, dir)
234 CallFrame *origFramePtr; /* frame that is true top-of-stack */
235 char *string; /* String describing frame. */
236 CallFrame **framePtrPtr; /* Store pointer to frame here (or NULL
237 * if global frame indicated). */
238 enum debug_cmd dir; /* look up or down the stack */
240 Interp *iPtr = (Interp *) interp;
242 CallFrame *framePtr; /* frame currently being searched */
244 CallFrame *curFramePtr = iPtr->varFramePtr;
247 * Parse string to figure out which level number to go to.
251 if (*string == '#') {
252 if (Tcl_GetInt(interp, string+1, &level) != TCL_OK) {
257 Tcl_AppendResult(interp, "bad level \"", string, "\"",
261 framePtr = origFramePtr; /* start search here */
263 } else if (isdigit(*string)) {
264 if (Tcl_GetInt(interp, string, &level) != TCL_OK) {
268 if (curFramePtr == 0) {
269 Tcl_SetResult(interp,already_at_top_level,TCL_STATIC);
272 level = curFramePtr->level - level;
273 framePtr = curFramePtr; /* start search here */
275 if (curFramePtr != 0) {
276 level = curFramePtr->level + level;
278 framePtr = origFramePtr; /* start search here */
281 level = curFramePtr->level - 1;
286 * Figure out which frame to use.
292 for (;framePtr != NULL; framePtr = framePtr->callerVarPtr) {
293 if (framePtr->level == level) {
297 if (framePtr == NULL) {
301 *framePtrPtr = framePtr;
306 static char *printify(s)
309 static int destlen = 0;
310 char *d; /* ptr into dest */
312 static char buf_basic[DEFAULT_WIDTH+1];
313 static char *dest = buf_basic;
316 if (s == 0) return("<null>");
318 /* worst case is every character takes 4 to printify */
320 if (need > destlen) {
321 if (dest && (dest != buf_basic)) ckfree(dest);
322 dest = (char *)ckalloc(need+1);
327 s += Tcl_UtfToUniChar(s, &ch);
329 strcpy(d,"\\b"); d += 2;
330 } else if (ch == '\f') {
331 strcpy(d,"\\f"); d += 2;
332 } else if (ch == '\v') {
333 strcpy(d,"\\v"); d += 2;
334 } else if (ch == '\r') {
335 strcpy(d,"\\r"); d += 2;
336 } else if (ch == '\n') {
337 strcpy(d,"\\n"); d += 2;
338 } else if (ch == '\t') {
339 strcpy(d,"\\t"); d += 2;
340 } else if ((unsigned)ch < 0x20) { /* unsigned strips parity */
341 sprintf(d,"\\%03o",ch); d += 4;
342 } else if (ch == 0177) {
343 strcpy(d,"\\177"); d += 4;
344 } else if ((ch < 0x80) && isprint(UCHAR(ch))) {
345 *d = (char)ch; d += 1;
347 sprintf(d,"\\u%04x",ch); d += 6;
356 print_argv(interp,argc,argv)
361 static int buf_width_max = DEFAULT_WIDTH;
362 static char buf_basic[DEFAULT_WIDTH+1]; /* basic buffer */
363 static char *buf = buf_basic;
364 int space; /* space remaining in buf */
367 int proc; /* if current command is "proc" */
370 if (buf_width > buf_width_max) {
371 if (buf && (buf != buf_basic)) ckfree(buf);
372 buf = (char *)ckalloc(buf_width + 1);
373 buf_width_max = buf_width;
376 proc = (0 == strcmp("proc",argv[0]));
377 sprintf(buf,"%.*s",buf_width,argv[0]);
379 space = buf_width - len;
384 while (argc && (space > 0)) {
385 CONST char *elementPtr;
389 /* braces/quotes have been stripped off arguments */
390 /* so put them back. We wrap everything except lists */
391 /* with one argument. One exception is to always wrap */
392 /* proc's 2nd arg (the arg list), since people are */
393 /* used to always seeing it this way. */
395 if (proc && (arg_index > 1)) wrap = TRUE;
397 (void) TclFindElement(interp,*argv,
398 #if TCL_MAJOR_VERSION >= 8
401 &elementPtr,&nextPtr,(int *)0,(int *)0);
402 if (*elementPtr == '\0') wrap = TRUE;
403 else if (*nextPtr == '\0') wrap = FALSE;
407 /* wrap lists (or null) in braces */
409 sprintf(bufp," {%.*s}",space-3,*argv);
411 sprintf(bufp," %.*s",space-1,*argv);
414 space = buf_width - len;
421 /* this copies from our static buf to printify's static buf */
422 /* and back to our static buf */
423 strncpy(buf,printify(buf),buf_width);
426 /* usually but not always right, but assume truncation if buffer is */
427 /* full. this avoids tiny but odd-looking problem of appending "}" */
428 /* to truncated lists during {}-wrapping earlier */
429 if (strlen(buf) == buf_width) {
430 buf[buf_width-1] = buf[buf_width-2] = buf[buf_width-3] = '.';
436 #if TCL_MAJOR_VERSION >= 8
439 print_objv(interp,objc,objv)
447 argv = (char **)ckalloc(objc+1 * sizeof(char *));
448 for (argc=0 ; argc<objc ; argc++) {
449 argv[argc] = Tcl_GetStringFromObj(objv[argc],&len);
452 return(print_argv(interp,argc,argv));
458 PrintStackBelow(interp,curf,viewf)
460 CallFrame *curf; /* current FramePtr */
461 CallFrame *viewf; /* view FramePtr */
463 char ptr; /* graphically indicate where we are in the stack */
465 /* indicate where we are in the stack */
466 ptr = ((curf == viewf)?'*':' ');
469 print(interp,"%c0: %s\n",
470 ptr,print_argv(interp,main_argc,main_argv));
472 PrintStackBelow(interp,curf->callerVarPtr,viewf);
473 print(interp,"%c%d: %s\n",ptr,curf->level,
474 #if TCL_MAJOR_VERSION >= 8
475 print_objv(interp,curf->objc,curf->objv)
477 print_argv(interp,curf->argc,curf->argv)
485 PrintStack(interp,curf,viewf,objc,objv,level)
487 CallFrame *curf; /* current FramePtr */
488 CallFrame *viewf; /* view FramePtr */
490 Tcl_Obj *CONST objv[]; /* Argument objects. */
493 PrintStackBelow(interp,curf,viewf);
494 print(interp," %s: %s\n",level,print_objv(interp,objc,objv));
497 /* return 0 if goal matches current frame or goal can't be found */
498 /* anywere in frame stack */
500 /* This catches things like a proc called from a Tcl_Eval which in */
501 /* turn was not called from a proc but some builtin such as source */
502 /* or Tcl_Eval. These builtin calls to Tcl_Eval lose any knowledge */
503 /* the FramePtr from the proc, so we have to search the entire */
504 /* stack frame to see if it's still there. */
510 CallFrame *cf = iptr->varFramePtr;
512 /* if at current level, return success immediately */
513 if (goal == cf) return 0;
516 cf = cf->callerVarPtr;
518 /* found, but since it's above us, fail */
526 static char *cmd_print(cmdtype)
527 enum debug_cmd cmdtype;
530 case none: return "cmd: none";
531 case step: return "cmd: step";
532 case next: return "cmd: next";
533 case ret: return "cmd: ret";
534 case cont: return "cmd: cont";
535 case up: return "cmd: up";
536 case down: return "cmd: down";
537 case where: return "cmd: where";
538 case Next: return "cmd: Next";
540 return "cmd: Unknown";
544 /* debugger's trace handler */
547 debugger_trap _ANSI_ARGS_ ((
548 ClientData clientData,
552 Tcl_Command commandInfo,
554 struct Tcl_Obj * CONST * objv));
559 debugger_trap(clientData,interp,level,command,commandInfo,objc,objv)
560 ClientData clientData; /* not used */
562 int level; /* positive number if called by Tcl, -1 if */
563 /* called by Dbg_On in which case we don't */
566 Tcl_Command commandInfo; /* Unused */
568 struct Tcl_Obj * CONST * objv;
570 char level_text[6]; /* textual representation of level */
573 Interp *iPtr = (Interp *)interp;
575 CallFrame *trueFramePtr; /* where the pc is */
576 CallFrame *viewFramePtr; /* where up/down are */
578 int print_command_first_time = TRUE;
579 static int debug_suspended = FALSE;
581 struct breakpoint *b;
585 /* skip commands that are invoked interactively */
586 if (debug_suspended) return TCL_OK;
588 thecmd = Tcl_GetString (objv[0]);
589 /* skip debugger commands */
590 if (thecmd[1] == '\0') {
599 case 'd': return TCL_OK;
603 if ((*ignoreproc)(interp,thecmd)) return TCL_OK;
605 /* if level is unknown, use "?" */
606 sprintf(level_text,(level == -1)?"?":"%d",level);
608 /* save so we can restore later */
609 trueFramePtr = iPtr->varFramePtr;
611 /* do not allow breaking while testing breakpoints */
612 debug_suspended = TRUE;
614 /* test all breakpoints to see if we should break */
615 /* if any successful breakpoints, start interactor */
616 debug_new_action = FALSE; /* reset strobe */
617 break_status = FALSE; /* no successful breakpoints yet */
618 for (b = break_base;b;b=b->next) {
619 break_status |= breakpoint_test(interp,command,b);
622 if (!debug_new_action) {
626 /* if s or n triggered by breakpoint, make "s 1" */
627 /* (and so on) refer to next command, not this one */
637 if (step_count > 0) goto finish;
640 /* check if we are back at the same level where the next */
641 /* command was issued. Also test */
642 /* against all FramePtrs and if no match, assume that */
643 /* we've missed a return, and so we should break */
644 /* if (goalFramePtr != iPtr->varFramePtr) goto finish;*/
645 if (GoalFrame(goalFramePtr,iPtr)) goto finish;
647 if (step_count > 0) goto finish;
650 /* check if we are back at the same level where the next */
651 /* command was issued. */
652 if (goalNumLevel < iPtr->numLevels) goto finish;
654 if (step_count > 0) goto finish;
657 /* same comment as in "case next" */
658 if (goalFramePtr != iPtr->varFramePtr) goto finish;
660 /* DANGER: unhandled cases! none, up, down, where */
664 if (print_command_first_time) {
665 print(interp,"%s: %s\n",
666 level_text,print_argv(interp,1,&command));
667 print_command_first_time = FALSE;
669 /* since user is typing a command, don't interrupt it immediately */
671 debug_suspended = TRUE;
673 /* interactor won't return until user gives a debugger cmd */
674 (*interactor)(interp,interdata);
677 /* save this so it can be restored after "w" command */
678 viewFramePtr = iPtr->varFramePtr;
680 if (debug_cmd == up || debug_cmd == down) {
681 /* calculate new frame */
682 if (-1 == TclGetFrame2(interp,trueFramePtr,viewFrameName,
683 &iPtr->varFramePtr,debug_cmd)) {
684 print(interp,"%s\n",Tcl_GetStringResult (interp));
685 Tcl_ResetResult(interp);
690 /* reset view back to normal */
691 iPtr->varFramePtr = trueFramePtr;
695 debug_suspended = FALSE;
703 goalFramePtr = iPtr->varFramePtr;
706 goalNumLevel = iPtr->numLevels;
709 goalFramePtr = iPtr->varFramePtr;
710 if (goalFramePtr == 0) {
711 print(interp,"nowhere to return to\n");
714 goalFramePtr = goalFramePtr->callerVarPtr;
717 PrintStack(interp,iPtr->varFramePtr,viewFramePtr,objc,objv,level_text);
721 /* restore view and restart interactor */
722 iPtr->varFramePtr = viewFramePtr;
726 debug_suspended = FALSE;
733 cmdNext(clientData, interp, objc, objv)
734 ClientData clientData;
737 Tcl_Obj *CONST objv[]; /* Argument objects. */
739 debug_new_action = TRUE;
740 debug_cmd = *(enum debug_cmd *)clientData;
742 last_action_cmd = debug_cmd;
746 } else if (TCL_OK != Tcl_GetIntFromObj (interp, objv[1], &step_count)) {
750 last_step_count = step_count;
757 cmdDir(clientData, interp, objc, objv)
758 ClientData clientData;
761 Tcl_Obj *CONST objv[]; /* Argument objects. */
764 debug_cmd = *(enum debug_cmd *)clientData;
769 frame = Tcl_GetString (objv[1]);
772 strncpy(viewFrameName,frame,FRAMENAMELEN);
779 cmdSimple(clientData, interp, objc, objv)
780 ClientData clientData;
783 Tcl_Obj *CONST objv[]; /* Argument objects. */
785 debug_new_action = TRUE;
786 debug_cmd = *(enum debug_cmd *)clientData;
787 last_action_cmd = debug_cmd;
794 breakpoint_destroy(b)
795 struct breakpoint *b;
797 if (b->file) Tcl_DecrRefCount(b->file);
798 if (b->pat) Tcl_DecrRefCount(b->pat);
799 if (b->cmd) Tcl_DecrRefCount(b->cmd);
800 if (b->expr) Tcl_DecrRefCount(b->expr);
802 /* unlink from chain */
803 if ((b->previous == 0) && (b->next == 0)) {
805 } else if (b->previous == 0) {
806 break_base = b->next;
807 b->next->previous = 0;
808 } else if (b->next == 0) {
809 b->previous->next = 0;
811 b->previous->next = b->next;
812 b->next->previous = b->previous;
823 *objPtr = Tcl_NewStringObj(str, -1);
824 Tcl_IncrRefCount(*objPtr);
830 cmdWhere(clientData, interp, objc, objv)
831 ClientData clientData;
834 Tcl_Obj *CONST objv[]; /* Argument objects. */
836 static char* options [] = {
852 /* Check and process switches */
854 for (i=1; i<objc; i++) {
858 name = Tcl_GetString(objv[i]);
859 if (name[0] != '-') {
862 if (Tcl_GetIndexFromObj(interp, objv[i], options, "flag", 0,
866 switch ((enum options) index) {
870 print(interp,"%d\n",compress);
873 if (TCL_OK != Tcl_GetBooleanFromObj (interp, objv[i], &buf_width))
879 print(interp,"%d\n",buf_width);
882 if (TCL_OK != Tcl_GetIntFromObj (interp, objv[i], &buf_width))
888 if (i < objc) goto usage;
893 print(interp,"usage: w [-width #] [-compress 0|1]\n");
897 #define breakpoint_fail(msg) {error_msg = msg; goto break_fail;}
902 cmdBreak(clientData, interp, objc, objv)
903 ClientData clientData;
906 Tcl_Obj *CONST objv[]; /* Argument objects. */
908 struct breakpoint *b;
911 static char* options [] = {
928 /* No arguments, list breakpoints */
930 for (b = break_base;b;b=b->next) breakpoint_print(interp,b);
934 /* Process breakpoint deletion (-, -x) */
936 /* Copied from exp_prog.h */
937 #define streq(x,y) (0 == strcmp((x),(y)))
942 if (streq (Tcl_GetString (objv[1]),"-")) {
944 breakpoint_destroy(break_base);
946 breakpoint_max_id = 0;
950 if ((Tcl_GetString (objv[1])[0] == '-') &&
951 (TCL_OK == Tcl_GetIntFromObj (interp, objv[1], &id))) {
954 for (b = break_base;b;b=b->next) {
956 breakpoint_destroy(b);
957 if (!break_base) breakpoint_max_id = 0;
961 Tcl_SetResult(interp,"no such breakpoint",TCL_STATIC);
966 b = breakpoint_new();
968 /* Process switches */
971 if (Tcl_GetIndexFromObj(interp, objv[i], options, "flag", 0,
973 switch ((enum options) index) {
976 if (i == objc) breakpoint_fail("no pattern?");
977 savestr(&b->pat,Tcl_GetString (objv[i]));
982 if (i == objc) breakpoint_fail("bad regular expression");
984 savestr(&b->pat,Tcl_GetString (objv[i]));
985 if (Tcl_GetRegExpFromObj(interp, b->pat, TCL_REG_ADVANCED) == NULL) {
986 breakpoint_destroy(b);
991 case BREAK_IF: break;
992 case BREAK_THEN: break;
995 /* look for [file:]line */
997 char *linep; /* pointer to beginning of line number */
998 char* ref = Tcl_GetString (objv[i]);
999 colon = strchr(ref,':');
1002 savestr(&b->file,ref);
1007 /* get file from current scope */
1008 /* savestr(&b->file, ?); */
1011 if (TCL_OK == Tcl_GetInt(interp,linep,&b->line)) {
1013 print(interp,"setting breakpoints by line number is currently unimplemented - use patterns or expressions\n");
1015 /* not an int? - unwind & assume it is an expression */
1017 if (b->file) Tcl_DecrRefCount(b->file);
1025 if (Tcl_GetIndexFromObj(interp, objv[i], options, "flag", 0,
1026 &index) == TCL_OK) {
1027 switch ((enum options) index) {
1031 /* Consider next word as expression */
1034 /* No 'if expression' guard here, do nothing */
1039 /* Consider current word as expression, without a preceding 'if' */
1043 /* Consider current word as expression, without a preceding 'if' */
1048 if (i == objc) breakpoint_fail("if what");
1049 savestr(&b->expr,Tcl_GetString (objv[i]));
1055 /* Remainder is a command */
1056 if (Tcl_GetIndexFromObj(interp, objv[i], options, "flag", 0,
1057 &index) == TCL_OK) {
1058 switch ((enum options) index) {
1069 if (i == objc) breakpoint_fail("then what?");
1071 savestr(&b->cmd,Tcl_GetString (objv[i]));
1074 Tcl_SetObjResult (interp, Tcl_NewIntObj (b->id));
1078 breakpoint_destroy(b);
1079 Tcl_SetResult(interp,error_msg,TCL_STATIC);
1083 static char *help[] = {
1084 "s [#] step into procedure",
1085 "n [#] step over procedure",
1086 "N [#] step over procedures, commands, and arguments",
1088 "r continue until return to caller",
1089 "u [#] move scope up level",
1090 "d [#] move scope down level",
1091 " go to absolute frame if # is prefaced by \"#\"",
1092 "w show stack (\"where\")",
1093 "w -w [#] show/set width",
1094 "w -c [0|1] show/set compress",
1095 "b show breakpoints",
1096 "b [-r regexp-pattern] [if expr] [then command]",
1097 "b [-g glob-pattern] [if expr] [then command]",
1098 "b [[file:]#] [if expr] [then command]",
1099 " if pattern given, break if command resembles pattern",
1100 " if # given, break on line #",
1101 " if expr given, break if expr true",
1102 " if command given, execute command at breakpoint",
1103 "b -# delete breakpoint",
1104 "b - delete all breakpoints",
1110 cmdHelp(clientData, interp, objc, objv)
1111 ClientData clientData;
1114 Tcl_Obj *CONST objv[]; /* Argument objects. */
1118 for (hp=help;*hp;hp++) {
1119 print(interp,"%s\n",*hp);
1125 /* occasionally, we print things larger buf_max but not by much */
1126 /* see print statements in PrintStack routines for examples */
1131 print TCL_VARARGS_DEF(Tcl_Interp *,arg1)
1137 interp = TCL_VARARGS_START(Tcl_Interp *,arg1,args);
1138 fmt = va_arg(args,char *);
1139 if (!printproc) vprintf(fmt,args);
1141 static int buf_width_max = DEFAULT_WIDTH+PAD;
1142 static char buf_basic[DEFAULT_WIDTH+PAD+1];
1143 static char *buf = buf_basic;
1145 if (buf_width+PAD > buf_width_max) {
1146 if (buf && (buf != buf_basic)) ckfree(buf);
1147 buf = (char *)ckalloc(buf_width+PAD+1);
1148 buf_width_max = buf_width+PAD;
1151 vsprintf(buf,fmt,args);
1152 (*printproc)(interp,buf,printdata);
1159 Dbg_Interactor(interp,inter_proc,data)
1161 Dbg_InterProc *inter_proc;
1164 Dbg_InterStruct tmp;
1166 tmp.func = interactor;
1167 tmp.data = interdata;
1168 interactor = (inter_proc?inter_proc:simple_interactor);
1174 Dbg_IgnoreFuncsProc *
1175 Dbg_IgnoreFuncs(interp,proc)
1177 Dbg_IgnoreFuncsProc *proc;
1179 Dbg_IgnoreFuncsProc *tmp = ignoreproc;
1180 ignoreproc = (proc?proc:zero);
1186 Dbg_Output(interp,proc,data)
1188 Dbg_OutputProc *proc;
1191 Dbg_OutputStruct tmp;
1193 tmp.func = printproc;
1194 tmp.data = printdata;
1205 return debugger_active;
1209 Dbg_ArgcArgv(argc,argv,copy)
1222 main_argv = alloc = (char **)ckalloc((argc+1)*sizeof(char *));
1223 while (argc-- >= 0) {
1224 *main_argv++ = *argv++;
1231 static struct cmd_list {
1233 Tcl_ObjCmdProc *cmdproc;
1234 enum debug_cmd cmdtype;
1236 {"n", cmdNext, next},
1237 {"s", cmdNext, step},
1238 {"N", cmdNext, Next},
1239 {"c", cmdSimple, cont},
1240 {"r", cmdSimple, ret},
1241 {"w", cmdWhere, none},
1242 {"b", cmdBreak, none},
1244 {"d", cmdDir, down},
1245 {"h", cmdHelp, none},
1249 /* this may seem excessive, but this avoids the explicit test for non-zero */
1250 /* in the caller, and chances are that that test will always be pointless */
1253 zero (Tcl_Interp *interp, char *string)
1258 extern int expSetBlockModeProc _ANSI_ARGS_((int fd, int mode));
1261 simple_interactor(Tcl_Interp *interp, ClientData data)
1264 char *ccmd; /* pointer to complete command */
1265 char line[BUFSIZ+1]; /* space for partial command */
1267 Interp *iPtr = (Interp *)interp;
1269 Tcl_DString dstring;
1270 Tcl_DStringInit(&dstring);
1272 /* Force blocking if necessary */
1274 if (stdinmode == TCL_MODE_NONBLOCKING) {
1275 expSetBlockModeProc(0, TCL_MODE_BLOCKING);
1283 #if TCL_MAJOR_VERSION < 8
1284 print(interp,"dbg%d.%d> ",iPtr->numLevels,iPtr->curEventNum+1);
1286 /* unncessarily tricky coding - if nextid
1287 isn't defined, maintain our own static
1290 static int nextid = 0;
1291 CONST char *nextidstr = Tcl_GetVar2(interp,"tcl::history","nextid",0);
1293 sscanf(nextidstr,"%d",&nextid);
1295 print(interp,"dbg%d.%d> ",iPtr->numLevels,nextid++);
1298 print(interp,"dbg+> ");
1302 rc = read(0,line,BUFSIZ);
1304 if (!newcmd) line[0] = 0;
1306 } else line[rc] = '\0';
1308 ccmd = Tcl_DStringAppend(&dstring,line,rc);
1309 if (!Tcl_CommandComplete(ccmd)) {
1311 continue; /* continue collecting command */
1315 /* if user pressed return with no cmd, use previous one */
1316 if ((ccmd[0] == '\n' || ccmd[0] == '\r') && ccmd[1] == '\0') {
1318 /* this loop is guaranteed to exit through break */
1319 for (c = cmd_list;c->cmdname;c++) {
1320 if (c->cmdtype == last_action_cmd) break;
1323 /* recreate textual version of command */
1324 Tcl_DStringAppend(&dstring,c->cmdname,-1);
1326 if (c->cmdtype == step ||
1327 c->cmdtype == next ||
1328 c->cmdtype == Next) {
1331 sprintf(num," %d",last_step_count);
1332 Tcl_DStringAppend(&dstring,num,-1);
1336 #if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION < 4
1337 rc = Tcl_RecordAndEval(interp,ccmd,0);
1339 rc = Tcl_RecordAndEval(interp,ccmd,TCL_NO_EVAL);
1340 rc = Tcl_Eval(interp,ccmd);
1342 Tcl_DStringFree(&dstring);
1347 char* res = Tcl_GetStringResult (interp);
1349 print(interp,"%s\n",res);
1353 print(interp,"%s\n",Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY));
1354 /* since user is typing by hand, we expect lots
1355 of errors, and want to give another chance */
1359 #define finish(x) {rc = x; goto done;}
1364 /* note that ccmd has trailing newline */
1365 print(interp,"error %d: %s\n",rc,ccmd);
1369 /* cannot fall thru here, must jump to label */
1371 Tcl_DStringFree(&dstring);
1373 /* Restore old blocking mode */
1374 if (stdinmode == TCL_MODE_NONBLOCKING) {
1375 expSetBlockModeProc(0, TCL_MODE_NONBLOCKING);
1380 static char init_auto_path[] = "lappend auto_path $dbg_library";
1383 init_debugger(interp)
1388 for (c = cmd_list;c->cmdname;c++) {
1389 Tcl_CreateObjCommand(interp,c->cmdname,c->cmdproc,
1390 (ClientData)&c->cmdtype,(Tcl_CmdDeleteProc *)0);
1393 debug_handle = Tcl_CreateObjTrace(interp,10000,0,
1394 debugger_trap,(ClientData)0, NULL);
1396 debugger_active = TRUE;
1397 Tcl_SetVar2(interp,Dbg_VarName,"active","1",0);
1398 #ifdef DBG_SCRIPTDIR
1399 Tcl_SetVar(interp,"dbg_library",DBG_SCRIPTDIR,0);
1401 Tcl_Eval(interp,init_auto_path);
1405 /* allows any other part of the application to jump to the debugger */
1408 Dbg_On(interp,immediate)
1410 int immediate; /* if true, stop immediately */
1411 /* should only be used in safe places */
1412 /* i.e., when Tcl_Eval can be called */
1414 if (!debugger_active) init_debugger(interp);
1416 /* Initialize debugger in single-step mode. Note: if the
1417 command reader is already active, it's too late which is why
1418 we also statically initialize debug_cmd to step. */
1422 #define LITERAL(s) Tcl_NewStringObj ((s), sizeof(s)-1)
1425 Tcl_Obj* fake_cmd = LITERAL ( "--interrupted-- (command_unknown)");
1427 Tcl_IncrRefCount (fake_cmd);
1428 debugger_trap((ClientData)0,interp,-1,Tcl_GetString (fake_cmd),0,1,&fake_cmd);
1429 /* (*interactor)(interp);*/
1430 Tcl_DecrRefCount (fake_cmd);
1440 if (!debugger_active) return;
1442 for (c = cmd_list;c->cmdname;c++) {
1443 Tcl_DeleteCommand(interp,c->cmdname);
1446 Tcl_DeleteTrace(interp,debug_handle);
1447 debugger_active = FALSE;
1448 Tcl_UnsetVar(interp,Dbg_VarName,TCL_GLOBAL_ONLY);
1450 /* initialize for next use */
1455 /* allows any other part of the application to tell the debugger where the Tcl channel for stdin is. */