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.
15 /* tclInt.h drags in stdlib. By claiming no-stdlib, force it to drag in */
16 /* Tcl's compat version. This avoids having to test for its presence */
17 /* which is too tricky - configure can't generate two cf files, so when */
18 /* Expect (or any app) uses the debugger, there's no way to get the info */
19 /* about whether stdlib exists or not, except pointing the debugger at */
20 /* an app-dependent .h file and I don't want to do that. */
26 /*#include <varargs.h> tclInt.h drags in varargs.h. Since Pyramid */
27 /* objects to including varargs.h twice, just */
29 /*#include "string.h" tclInt.h drags this in, too! */
37 static int simple_interactor();
40 /* most of the static variables in this file may be */
41 /* moved into Tcl_Interp */
43 static Dbg_InterProc *interactor = simple_interactor;
44 static ClientData interdata = 0;
45 static Dbg_IgnoreFuncsProc *ignoreproc = zero;
46 static Dbg_OutputProc *printproc = 0;
47 static ClientData printdata = 0;
49 static void print _ANSI_ARGS_(TCL_VARARGS(Tcl_Interp *,interp));
51 static int debugger_active = FALSE;
53 /* this is not externally documented anywhere as of yet */
54 char *Dbg_VarName = "dbg";
56 #define DEFAULT_COMPRESS 0
57 static int compress = DEFAULT_COMPRESS;
58 #define DEFAULT_WIDTH 75 /* leave a little space for printing */
60 static int buf_width = DEFAULT_WIDTH;
62 static int main_argc = 1;
63 static char *default_argv = "application";
64 static char **main_argv = &default_argv;
66 static Tcl_Trace debug_handle;
67 static int step_count = 1; /* count next/step */
69 #define FRAMENAMELEN 10 /* enough to hold strings like "#4" */
70 static char viewFrameName[FRAMENAMELEN];/* destination frame name for up/down */
72 static CallFrame *goalFramePtr; /* destination for next/return */
73 static int goalNumLevel; /* destination for Next */
75 static enum debug_cmd {
76 none, step, next, ret, cont, up, down, where, Next
79 /* info about last action to use as a default */
80 static enum debug_cmd last_action_cmd = next;
81 static int last_step_count = 1;
83 /* this acts as a strobe (while testing breakpoints). It is set to true */
84 /* every time a new debugger command is issued that is an action */
85 static debug_new_action;
87 #define NO_LINE -1 /* if break point is not set by line number */
91 char *file; /* file where breakpoint is */
92 int line; /* line where breakpoint is */
93 char *pat; /* pattern defining where breakpoint can be */
94 regexp *re; /* regular expression to trigger breakpoint */
95 char *expr; /* expr to trigger breakpoint */
96 char *cmd; /* cmd to eval at breakpoint */
97 struct breakpoint *next, *previous;
100 static struct breakpoint *break_base = 0;
101 static int breakpoint_max_id = 0;
103 static struct breakpoint *
106 struct breakpoint *b = (struct breakpoint *)ckalloc(sizeof(struct breakpoint));
107 if (break_base) break_base->previous = b;
108 b->next = break_base;
110 b->id = breakpoint_max_id++;
123 breakpoint_print(interp,b)
125 struct breakpoint *b;
127 print(interp,"breakpoint %d: ",b->id);
130 print(interp,"-re \"%s\" ",b->pat);
132 print(interp,"-glob \"%s\" ",b->pat);
133 } else if (b->line != NO_LINE) {
135 print(interp,"%s:",b->file);
137 print(interp,"%d ",b->line);
141 print(interp,"if {%s} ",b->expr);
144 print(interp,"then {%s}",b->cmd);
150 save_re_matches(interp,re)
156 char match_char;/* place to hold char temporarily */
157 /* uprooted by a NULL */
159 for (i=0;i<NSUBEXP;i++) {
160 if (re->startp[i] == 0) break;
162 sprintf(name,"%d",i);
163 /* temporarily null-terminate in middle */
164 match_char = *re->endp[i];
166 Tcl_SetVar2(interp,Dbg_VarName,name,re->startp[i],0);
168 /* undo temporary null-terminator */
169 *re->endp[i] = match_char;
173 /* return 1 to break, 0 to continue */
175 breakpoint_test(interp,cmd,bp)
177 char *cmd; /* command about to be executed */
178 struct breakpoint *bp; /* breakpoint to test */
181 if (0 == TclRegExec(bp->re,cmd,cmd)) return 0;
182 save_re_matches(interp,bp->re);
183 } else if (bp->pat) {
184 if (0 == Tcl_StringMatch(cmd,bp->pat)) return 0;
185 } else if (bp->line != NO_LINE) {
186 /* not yet implemented - awaiting support from Tcl */
193 /* ignore errors, since they are likely due to */
194 /* simply being out of scope a lot */
195 if (TCL_OK != Tcl_ExprBoolean(interp,bp->expr,&value)
196 || (value == 0)) return 0;
200 Tcl_Eval(interp,bp->cmd);
202 breakpoint_print(interp,bp);
208 static char *already_at_top_level = "already at top level";
210 /* similar to TclGetFrame but takes two frame ptrs and a direction.
211 If direction is up, search up stack from curFrame
212 If direction is down, simulate searching down stack by
213 seaching up stack from origFrame
217 TclGetFrame2(interp, origFramePtr, string, framePtrPtr, dir)
219 CallFrame *origFramePtr; /* frame that is true top-of-stack */
220 char *string; /* String describing frame. */
221 CallFrame **framePtrPtr; /* Store pointer to frame here (or NULL
222 * if global frame indicated). */
223 enum debug_cmd dir; /* look up or down the stack */
225 Interp *iPtr = (Interp *) interp;
227 CallFrame *framePtr; /* frame currently being searched */
229 CallFrame *curFramePtr = iPtr->varFramePtr;
232 * Parse string to figure out which level number to go to.
236 if (*string == '#') {
237 if (Tcl_GetInt(interp, string+1, &level) != TCL_OK) {
242 Tcl_AppendResult(interp, "bad level \"", string, "\"",
246 framePtr = origFramePtr; /* start search here */
248 } else if (isdigit(*string)) {
249 if (Tcl_GetInt(interp, string, &level) != TCL_OK) {
253 if (curFramePtr == 0) {
254 Tcl_SetResult(interp,already_at_top_level,TCL_STATIC);
257 level = curFramePtr->level - level;
258 framePtr = curFramePtr; /* start search here */
260 if (curFramePtr != 0) {
261 level = curFramePtr->level + level;
263 framePtr = origFramePtr; /* start search here */
266 level = curFramePtr->level - 1;
271 * Figure out which frame to use.
277 for (;framePtr != NULL; framePtr = framePtr->callerVarPtr) {
278 if (framePtr->level == level) {
282 if (framePtr == NULL) {
286 *framePtrPtr = framePtr;
291 static char *printify(s)
294 static int destlen = 0;
295 char *d; /* ptr into dest */
297 static char buf_basic[DEFAULT_WIDTH+1];
298 static char *dest = buf_basic;
300 if (s == 0) return("<null>");
302 /* worst case is every character takes 4 to printify */
304 if (need > destlen) {
305 if (dest && (dest != buf_basic)) ckfree(dest);
306 dest = (char *)ckalloc(need+1);
310 for (d = dest;*s;s++) {
311 /* since we check at worst by every 4 bytes, play */
312 /* conservative and subtract 4 from the limit */
313 if (d-dest > destlen-4) break;
316 strcpy(d,"\\b"); d += 2;
317 } else if (*s == '\f') {
318 strcpy(d,"\\f"); d += 2;
319 } else if (*s == '\v') {
320 strcpy(d,"\\v"); d += 2;
321 } else if (*s == '\r') {
322 strcpy(d,"\\r"); d += 2;
323 } else if (*s == '\n') {
324 strcpy(d,"\\n"); d += 2;
325 } else if (*s == '\t') {
326 strcpy(d,"\\t"); d += 2;
327 } else if ((unsigned)*s < 0x20) { /* unsigned strips parity */
328 sprintf(d,"\\%03o",*s); d += 4;
329 } else if (*s == 0177) {
330 strcpy(d,"\\177"); d += 4;
341 print_argv(interp,argc,argv)
346 static int buf_width_max = DEFAULT_WIDTH;
347 static char buf_basic[DEFAULT_WIDTH+1]; /* basic buffer */
348 static char *buf = buf_basic;
349 int space; /* space remaining in buf */
352 int proc; /* if current command is "proc" */
355 if (buf_width > buf_width_max) {
356 if (buf && (buf != buf_basic)) ckfree(buf);
357 buf = (char *)ckalloc(buf_width + 1);
358 buf_width_max = buf_width;
361 proc = (0 == strcmp("proc",argv[0]));
362 sprintf(buf,"%.*s",buf_width,argv[0]);
364 space = buf_width - len;
369 while (argc && (space > 0)) {
374 /* braces/quotes have been stripped off arguments */
375 /* so put them back. We wrap everything except lists */
376 /* with one argument. One exception is to always wrap */
377 /* proc's 2nd arg (the arg list), since people are */
378 /* used to always seeing it this way. */
380 if (proc && (arg_index > 1)) wrap = TRUE;
382 (void) TclFindElement(interp,*argv,
383 #if TCL_MAJOR_VERSION >= 8
386 &elementPtr,&nextPtr,(int *)0,(int *)0);
387 if (*elementPtr == '\0') wrap = TRUE;
388 else if (*nextPtr == '\0') wrap = FALSE;
392 /* wrap lists (or null) in braces */
394 sprintf(bufp," {%.*s}",space-3,*argv);
396 sprintf(bufp," %.*s",space-1,*argv);
399 space = buf_width - len;
406 /* this copies from our static buf to printify's static buf */
407 /* and back to our static buf */
408 strncpy(buf,printify(buf),buf_width);
411 /* usually but not always right, but assume truncation if buffer is */
412 /* full. this avoids tiny but odd-looking problem of appending "}" */
413 /* to truncated lists during {}-wrapping earlier */
414 if (strlen(buf) == buf_width) {
415 buf[buf_width-1] = buf[buf_width-2] = buf[buf_width-3] = '.';
421 #if TCL_MAJOR_VERSION >= 8
424 print_objv(interp,objc,objv)
432 argv = (char **)ckalloc(objc+1 * sizeof(char *));
433 for (argc=0 ; argc<objc ; argc++) {
434 argv[argc] = Tcl_GetStringFromObj(objv[argc],&len);
437 print_argv(interp,argc,argv);
443 PrintStackBelow(interp,curf,viewf)
445 CallFrame *curf; /* current FramePtr */
446 CallFrame *viewf; /* view FramePtr */
448 char ptr; /* graphically indicate where we are in the stack */
450 /* indicate where we are in the stack */
451 ptr = ((curf == viewf)?'*':' ');
454 print(interp,"%c0: %s\n",
455 ptr,print_argv(interp,main_argc,main_argv));
457 PrintStackBelow(interp,curf->callerVarPtr,viewf);
458 print(interp,"%c%d: %s\n",ptr,curf->level,
459 #if TCL_MAJOR_VERSION >= 8
460 print_objv(interp,curf->objc,curf->objv));
462 print_argv(interp,curf->argc,curf->argv));
469 PrintStack(interp,curf,viewf,argc,argv,level)
471 CallFrame *curf; /* current FramePtr */
472 CallFrame *viewf; /* view FramePtr */
477 PrintStackBelow(interp,curf,viewf);
479 print(interp," %s: %s\n",level,print_argv(interp,argc,argv));
482 /* return 0 if goal matches current frame or goal can't be found */
483 /* anywere in frame stack */
485 /* This catches things like a proc called from a Tcl_Eval which in */
486 /* turn was not called from a proc but some builtin such as source */
487 /* or Tcl_Eval. These builtin calls to Tcl_Eval lose any knowledge */
488 /* the FramePtr from the proc, so we have to search the entire */
489 /* stack frame to see if it's still there. */
495 CallFrame *cf = iptr->varFramePtr;
497 /* if at current level, return success immediately */
498 if (goal == cf) return 0;
501 cf = cf->callerVarPtr;
503 /* found, but since it's above us, fail */
510 /* debugger's trace handler */
513 debugger_trap(clientData,interp,level,command,cmdProc,cmdClientData,argc,argv)
514 ClientData clientData; /* not used */
516 int level; /* positive number if called by Tcl, -1 if */
517 /* called by Dbg_On in which case we don't */
520 int (*cmdProc)(); /* not used */
521 ClientData cmdClientData;
525 char level_text[6]; /* textual representation of level */
528 Interp *iPtr = (Interp *)interp;
530 CallFrame *trueFramePtr; /* where the pc is */
531 CallFrame *viewFramePtr; /* where up/down are */
533 int print_command_first_time = TRUE;
534 static int debug_suspended = FALSE;
536 struct breakpoint *b;
538 /* skip commands that are invoked interactively */
539 if (debug_suspended) return;
541 /* skip debugger commands */
542 if (argv[0][1] == '\0') {
543 switch (argv[0][0]) {
555 if ((*ignoreproc)(interp,argv[0])) return;
557 /* if level is unknown, use "?" */
558 sprintf(level_text,(level == -1)?"?":"%d",level);
560 /* save so we can restore later */
561 trueFramePtr = iPtr->varFramePtr;
563 /* do not allow breaking while testing breakpoints */
564 debug_suspended = TRUE;
566 /* test all breakpoints to see if we should break */
567 /* if any successful breakpoints, start interactor */
568 debug_new_action = FALSE; /* reset strobe */
569 break_status = FALSE; /* no successful breakpoints yet */
570 for (b = break_base;b;b=b->next) {
571 break_status |= breakpoint_test(interp,command,b);
574 if (!debug_new_action) goto start_interact;
576 /* if s or n triggered by breakpoint, make "s 1" */
577 /* (and so on) refer to next command, not this one */
587 if (step_count > 0) goto finish;
590 /* check if we are back at the same level where the next */
591 /* command was issued. Also test */
592 /* against all FramePtrs and if no match, assume that */
593 /* we've missed a return, and so we should break */
594 /* if (goalFramePtr != iPtr->varFramePtr) goto finish;*/
595 if (GoalFrame(goalFramePtr,iPtr)) goto finish;
597 if (step_count > 0) goto finish;
600 /* check if we are back at the same level where the next */
601 /* command was issued. */
602 if (goalNumLevel < iPtr->numLevels) goto finish;
604 if (step_count > 0) goto finish;
607 /* same comment as in "case next" */
608 if (goalFramePtr != iPtr->varFramePtr) goto finish;
613 if (print_command_first_time) {
614 print(interp,"%s: %s\n",
615 level_text,print_argv(interp,1,&command));
616 print_command_first_time = FALSE;
618 /* since user is typing a command, don't interrupt it immediately */
620 debug_suspended = TRUE;
622 /* interactor won't return until user gives a debugger cmd */
623 (*interactor)(interp,interdata);
626 /* save this so it can be restored after "w" command */
627 viewFramePtr = iPtr->varFramePtr;
629 if (debug_cmd == up || debug_cmd == down) {
630 /* calculate new frame */
631 if (-1 == TclGetFrame2(interp,trueFramePtr,viewFrameName,
632 &iPtr->varFramePtr,debug_cmd)) {
633 print(interp,"%s\n",interp->result);
634 Tcl_ResetResult(interp);
639 /* reset view back to normal */
640 iPtr->varFramePtr = trueFramePtr;
644 debug_suspended = FALSE;
652 goalFramePtr = iPtr->varFramePtr;
655 goalNumLevel = iPtr->numLevels;
658 goalFramePtr = iPtr->varFramePtr;
659 if (goalFramePtr == 0) {
660 print(interp,"nowhere to return to\n");
663 goalFramePtr = goalFramePtr->callerVarPtr;
666 PrintStack(interp,iPtr->varFramePtr,viewFramePtr,argc,argv,level_text);
670 /* restore view and restart interactor */
671 iPtr->varFramePtr = viewFramePtr;
675 debug_suspended = FALSE;
681 cmdNext(clientData, interp, argc, argv)
682 ClientData clientData;
687 debug_new_action = TRUE;
688 debug_cmd = *(enum debug_cmd *)clientData;
689 last_action_cmd = debug_cmd;
691 step_count = (argc == 1)?1:atoi(argv[1]);
692 last_step_count = step_count;
699 cmdDir(clientData, interp, argc, argv)
700 ClientData clientData;
705 debug_cmd = *(enum debug_cmd *)clientData;
707 if (argc == 1) argv[1] = "1";
708 strncpy(viewFrameName,argv[1],FRAMENAMELEN);
716 cmdSimple(clientData, interp, argc, argv)
717 ClientData clientData;
722 debug_new_action = TRUE;
723 debug_cmd = *(enum debug_cmd *)clientData;
724 last_action_cmd = debug_cmd;
731 breakpoint_destroy(b)
732 struct breakpoint *b;
734 if (b->file) ckfree(b->file);
735 if (b->pat) ckfree(b->pat);
736 if (b->re) ckfree((char *)b->re);
737 if (b->cmd) ckfree(b->cmd);
739 /* unlink from chain */
740 if ((b->previous == 0) && (b->next == 0)) {
742 } else if (b->previous == 0) {
743 break_base = b->next;
744 b->next->previous = 0;
745 } else if (b->next == 0) {
746 b->previous->next = 0;
748 b->previous->next = b->next;
749 b->next->previous = b->previous;
760 *straddr = ckalloc(strlen(str)+1);
761 strcpy(*straddr,str);
764 /* return 1 if a string is substring of a flag */
766 flageq(flag,string,minlen)
769 int minlen; /* at least this many chars must match */
771 for (;*flag;flag++,string++,minlen--) {
772 if (*string == '\0') break;
773 if (*string != *flag) return 0;
775 if (*string == '\0' && minlen <= 0) return 1;
782 cmdWhere(clientData, interp, argc, argv)
783 ClientData clientData;
796 if (flageq("-width",*argv,2)) {
799 buf_width = atoi(*argv);
801 } else print(interp,"%d\n",buf_width);
802 } else if (flageq("-compress",*argv,2)) {
805 compress = atoi(*argv);
807 } else print(interp,"%d\n",compress);
809 print(interp,"usage: w [-width #] [-compress 0|1]\n");
816 #define breakpoint_fail(msg) {error_msg = msg; goto break_fail;}
821 cmdBreak(clientData, interp, argc, argv)
822 ClientData clientData;
827 struct breakpoint *b;
833 for (b = break_base;b;b=b->next) breakpoint_print(interp,b);
837 if (argv[0][0] == '-') {
838 if (argv[0][1] == '\0') {
840 breakpoint_destroy(break_base);
842 breakpoint_max_id = 0;
844 } else if (isdigit(argv[0][1])) {
845 int id = atoi(argv[0]+1);
847 for (b = break_base;b;b=b->next) {
849 breakpoint_destroy(b);
850 if (!break_base) breakpoint_max_id = 0;
854 Tcl_SetResult(interp,"no such breakpoint",TCL_STATIC);
859 b = breakpoint_new();
861 if (flageq("-regexp",argv[0],2)) {
863 if ((argc > 0) && (b->re = TclRegComp(argv[0]))) {
864 savestr(&b->pat,argv[0]);
867 breakpoint_fail("bad regular expression")
869 } else if (flageq("-glob",argv[0],2)) {
872 savestr(&b->pat,argv[0]);
875 breakpoint_fail("no pattern?");
877 } else if ((!(flageq("if",*argv,1)) && (!(flageq("then",*argv,1))))) {
878 /* look for [file:]line */
880 char *linep; /* pointer to beginning of line number */
882 colon = strchr(argv[0],':');
885 savestr(&b->file,argv[0]);
890 /* get file from current scope */
891 /* savestr(&b->file, ?); */
894 if (TCL_OK == Tcl_GetInt(interp,linep,&b->line)) {
896 print(interp,"setting breakpoints by line number is currently unimplemented - use patterns or expressions\n");
898 /* not an int? - unwind & assume it is an expression */
900 if (b->file) ckfree(b->file);
907 if (flageq("if",argv[0],1)) {
910 } else if (!flageq("then",argv[0],1)) {
916 breakpoint_fail("if what");
919 savestr(&b->expr,argv[0]);
925 if (flageq("then",argv[0],1)) {
930 breakpoint_fail("then what?");
933 savestr(&b->cmd,argv[0]);
936 sprintf(interp->result,"%d",b->id);
940 breakpoint_destroy(b);
941 Tcl_SetResult(interp,error_msg,TCL_STATIC);
945 static char *help[] = {
946 "s [#] step into procedure",
947 "n [#] step over procedure",
948 "N [#] step over procedures, commands, and arguments",
950 "r continue until return to caller",
951 "u [#] move scope up level",
952 "d [#] move scope down level",
953 " go to absolute frame if # is prefaced by \"#\"",
954 "w show stack (\"where\")",
955 "w -w [#] show/set width",
956 "w -c [0|1] show/set compress",
957 "b show breakpoints",
958 "b [-r regexp-pattern] [if expr] [then command]",
959 "b [-g glob-pattern] [if expr] [then command]",
960 "b [[file:]#] [if expr] [then command]",
961 " if pattern given, break if command resembles pattern",
962 " if # given, break on line #",
963 " if expr given, break if expr true",
964 " if command given, execute command at breakpoint",
965 "b -# delete breakpoint",
966 "b - delete all breakpoints",
972 cmdHelp(clientData, interp, argc, argv)
973 ClientData clientData;
980 for (hp=help;*hp;hp++) {
981 print(interp,"%s\n",*hp);
987 /* occasionally, we print things larger buf_max but not by much */
988 /* see print statements in PrintStack routines for examples */
993 print TCL_VARARGS_DEF(Tcl_Interp *,arg1)
999 interp = TCL_VARARGS_START(Tcl_Interp *,arg1,args);
1000 fmt = va_arg(args,char *);
1001 if (!printproc) vprintf(fmt,args);
1003 static int buf_width_max = DEFAULT_WIDTH+PAD;
1004 static char buf_basic[DEFAULT_WIDTH+PAD+1];
1005 static char *buf = buf_basic;
1007 if (buf_width+PAD > buf_width_max) {
1008 if (buf && (buf != buf_basic)) ckfree(buf);
1009 buf = (char *)ckalloc(buf_width+PAD+1);
1010 buf_width_max = buf_width+PAD;
1013 vsprintf(buf,fmt,args);
1014 (*printproc)(interp,buf,printdata);
1021 Dbg_Interactor(interp,inter_proc,data)
1023 Dbg_InterProc *inter_proc;
1026 Dbg_InterStruct tmp;
1028 tmp.func = interactor;
1029 tmp.data = interdata;
1030 interactor = (inter_proc?inter_proc:simple_interactor);
1036 Dbg_IgnoreFuncsProc *
1037 Dbg_IgnoreFuncs(interp,proc)
1039 Dbg_IgnoreFuncsProc *proc;
1041 Dbg_IgnoreFuncsProc *tmp = ignoreproc;
1042 ignoreproc = (proc?proc:zero);
1048 Dbg_Output(interp,proc,data)
1050 Dbg_OutputProc *proc;
1053 Dbg_OutputStruct tmp;
1055 tmp.func = printproc;
1056 tmp.data = printdata;
1067 return debugger_active;
1071 Dbg_ArgcArgv(argc,argv,copy)
1084 main_argv = alloc = (char **)ckalloc((argc+1)*sizeof(char *));
1085 while (argc-- >= 0) {
1086 *main_argv++ = *argv++;
1093 static struct cmd_list {
1095 Tcl_CmdProc *cmdproc;
1096 enum debug_cmd cmdtype;
1098 {"n", cmdNext, next},
1099 {"s", cmdNext, step},
1100 {"N", cmdNext, Next},
1101 {"c", cmdSimple, cont},
1102 {"r", cmdSimple, ret},
1103 {"w", cmdWhere, none},
1104 {"b", cmdBreak, none},
1106 {"d", cmdDir, down},
1107 {"h", cmdHelp, none},
1111 /* this may seem excessive, but this avoids the explicit test for non-zero */
1112 /* in the caller, and chances are that that test will always be pointless */
1114 static int zero(interp,string)
1122 simple_interactor(interp)
1126 char *ccmd; /* pointer to complete command */
1127 char line[BUFSIZ+1]; /* space for partial command */
1129 Interp *iPtr = (Interp *)interp;
1131 Tcl_DString dstring;
1132 Tcl_DStringInit(&dstring);
1139 #if TCL_MAJOR_VERSION < 8
1140 print(interp,"dbg%d.%d> ",iPtr->numLevels,iPtr->curEventNum+1);
1142 /* unncessarily tricky coding - if nextid
1143 isn't defined, maintain our own static
1146 static int nextid = 0;
1147 char *nextidstr = Tcl_GetVar2(interp,"tcl::history","nextid",0);
1149 sscanf(nextidstr,"%d",&nextid);
1151 print(interp,"dbg%d.%d> ",iPtr->numLevels,nextid++);
1154 print(interp,"dbg+> ");
1158 if (0 >= (rc = read(0,line,BUFSIZ))) {
1159 if (!newcmd) line[0] = 0;
1161 } else line[rc] = '\0';
1163 ccmd = Tcl_DStringAppend(&dstring,line,rc);
1164 if (!Tcl_CommandComplete(ccmd)) {
1166 continue; /* continue collecting command */
1170 /* if user pressed return with no cmd, use previous one */
1171 if ((ccmd[0] == '\n' || ccmd[0] == '\r') && ccmd[1] == '\0') {
1173 /* this loop is guaranteed to exit through break */
1174 for (c = cmd_list;c->cmdname;c++) {
1175 if (c->cmdtype == last_action_cmd) break;
1178 /* recreate textual version of command */
1179 Tcl_DStringAppend(&dstring,c->cmdname,-1);
1181 if (c->cmdtype == step ||
1182 c->cmdtype == next ||
1183 c->cmdtype == Next) {
1186 sprintf(num," %d",last_step_count);
1187 Tcl_DStringAppend(&dstring,num,-1);
1191 #if TCL_MAJOR_VERSION == 7 && TCL_MINOR_VERSION < 4
1192 rc = Tcl_RecordAndEval(interp,ccmd,0);
1194 rc = Tcl_RecordAndEval(interp,ccmd,TCL_NO_EVAL);
1195 rc = Tcl_Eval(interp,ccmd);
1197 Tcl_DStringFree(&dstring);
1201 if (*interp->result != 0)
1202 print(interp,"%s\n",interp->result);
1205 print(interp,"%s\n",Tcl_GetVar(interp,"errorInfo",TCL_GLOBAL_ONLY));
1206 /* since user is typing by hand, we expect lots
1207 of errors, and want to give another chance */
1211 #define finish(x) {rc = x; goto done;}
1216 /* note that ccmd has trailing newline */
1217 print(interp,"error %d: %s\n",rc,ccmd);
1221 /* cannot fall thru here, must jump to label */
1223 Tcl_DStringFree(&dstring);
1228 static char init_auto_path[] = "lappend auto_path $dbg_library";
1231 init_debugger(interp)
1236 for (c = cmd_list;c->cmdname;c++) {
1237 Tcl_CreateCommand(interp,c->cmdname,c->cmdproc,
1238 (ClientData)&c->cmdtype,(Tcl_CmdDeleteProc *)0);
1241 debug_handle = Tcl_CreateTrace(interp,
1242 10000,debugger_trap,(ClientData)0);
1244 debugger_active = TRUE;
1245 Tcl_SetVar2(interp,Dbg_VarName,"active","1",0);
1246 #ifdef DBG_SCRIPTDIR
1247 Tcl_SetVar(interp,"dbg_library",DBG_SCRIPTDIR,0);
1249 Tcl_Eval(interp,init_auto_path);
1253 /* allows any other part of the application to jump to the debugger */
1256 Dbg_On(interp,immediate)
1258 int immediate; /* if true, stop immediately */
1259 /* should only be used in safe places */
1260 /* i.e., when Tcl_Eval can be called */
1262 if (!debugger_active) init_debugger(interp);
1268 static char *fake_cmd = "--interrupted-- (command_unknown)";
1270 debugger_trap((ClientData)0,interp,-1,fake_cmd,(int (*)())0,
1271 (ClientData)0,1,&fake_cmd);
1272 /* (*interactor)(interp);*/
1282 if (!debugger_active) return;
1284 for (c = cmd_list;c->cmdname;c++) {
1285 Tcl_DeleteCommand(interp,c->cmdname);
1288 Tcl_DeleteTrace(interp,debug_handle);
1289 debugger_active = FALSE;
1290 Tcl_UnsetVar(interp,Dbg_VarName,TCL_GLOBAL_ONLY);