debugger.c revision f765521f
1/*
2 * Copyright (c) 2001 by The XFree86 Project, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17 * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 *
22 * Except as contained in this notice, the name of the XFree86 Project shall
23 * not be used in advertising or otherwise to promote the sale, use or other
24 * dealings in this Software without prior written authorization from the
25 * XFree86 Project.
26 *
27 * Author: Paulo César Pereira de Andrade
28 */
29
30/* $XFree86: xc/programs/xedit/lisp/debugger.c,v 1.24tsi Exp $ */
31
32#include <ctype.h>
33#include "lisp/io.h"
34#include "lisp/debugger.h"
35#include "lisp/write.h"
36
37#ifdef DEBUGGER
38#define DebuggerHelp		0
39#define DebuggerAbort		1
40#define DebuggerBacktrace	2
41#define DebuggerContinue	3
42#define DebuggerFinish		4
43#define DebuggerFrame		5
44#define DebuggerNext		6
45#define DebuggerPrint		7
46#define DebuggerStep		8
47#define DebuggerBreak		9
48#define DebuggerDelete		10
49#define DebuggerDown		11
50#define DebuggerUp		12
51#define DebuggerInfo		13
52#define DebuggerWatch		14
53
54#define DebuggerInfoBreakpoints	0
55#define DebuggerInfoBacktrace	1
56
57/*
58 * Prototypes
59 */
60static char *format_integer(int);
61static void LispDebuggerCommand(LispObj *obj);
62
63/*
64 * Initialization
65 */
66static struct {
67    const char *name;
68    int action;
69} const commands[] = {
70    {"help",		DebuggerHelp},
71    {"abort",		DebuggerAbort},
72    {"backtrace",	DebuggerBacktrace},
73    {"b",		DebuggerBreak},
74    {"break",		DebuggerBreak},
75    {"bt",		DebuggerBacktrace},
76    {"continue",	DebuggerContinue},
77    {"d",		DebuggerDelete},
78    {"delete",		DebuggerDelete},
79    {"down",		DebuggerDown},
80    {"finish",		DebuggerFinish},
81    {"frame",		DebuggerFrame},
82    {"info",		DebuggerInfo},
83    {"n",		DebuggerNext},
84    {"next",		DebuggerNext},
85    {"print",		DebuggerPrint},
86    {"run",		DebuggerContinue},
87    {"s",		DebuggerStep},
88    {"step",		DebuggerStep},
89    {"up",		DebuggerUp},
90    {"watch",		DebuggerWatch},
91};
92
93static struct {
94    const char *name;
95    int subaction;
96} const info_commands[] = {
97    {"breakpoints",	DebuggerInfoBreakpoints},
98    {"stack",		DebuggerInfoBacktrace},
99    {"watchpoints",	DebuggerInfoBreakpoints},
100};
101
102static const char *debugger_help =
103"Available commands are:\n\
104\n\
105help		- This message.\n\
106abort		- Abort the current execution, and return to toplevel.\n\
107backtrace, bt	- Print backtrace.\n\
108b, break	- Set breakpoint at function name argument.\n\
109continue	- Continue execution.\n\
110d, delete	- Delete breakpoint(s), all breakpoint if no arguments given.\n\
111down		- Set environment to frame called by the current one.\n\
112finish		- Executes until current form is finished.\n\
113frame		- Set environment to selected frame.\n\
114info		- Prints information about the debugger state.\n\
115n, next		- Evaluate next form.\n\
116print		- Print value of variable name argument.\n\
117run		- Continue execution.\n\
118s, step		- Evaluate next form, stopping on any subforms.\n\
119up		- Set environment to frame that called the current one.\n\
120\n\
121Commands may be abbreviated.\n";
122
123static const char *debugger_info_help =
124"Available subcommands are:\n\
125\n\
126breakpoints	- List and prints status of breakpoints, and watchpoints.\n\
127stack		- Backtrace of stack.\n\
128watchpoints	- List and prints status of watchpoints, and breakpoints.\n\
129\n\
130Subcommands may be abbreviated.\n";
131
132/* Debugger variables layout (if you change it, update description):
133 *
134 * DBG
135 *	is a macro for lisp__data.dbglist
136 *	is a NIL terminated list
137 *	every element is a list in the format (NOT NIL terminated):
138 *	(list* NAM ARG ENV HED LEX)
139 *	where
140 *		NAM is an ATOM for the function/macro name
141 *		    or NIL for lambda expressions
142 *		ARG is NAM arguments (a LIST)
143 *		ENV is the value of lisp__data.stack.base (a FIXNUM)
144 *		LEN is the value of lisp__data.env.length (a FIXNUM)
145 *		LEX is the value of lisp__data.env.lex (a FIXNUM)
146 *	new elements are added to the beggining of the DBG list
147 *
148 * BRK
149 *	is macro for lisp__data.brklist
150 *	is a NIL terminated list
151 *	every element is a list in the format (NIL terminated):
152 *	(list NAM IDX TYP HIT VAR VAL FRM)
153 *	where
154 *		NAM is an ATOM for the name of the object at
155 *		    wich the breakpoint was added
156 *		IDX is a FIXNUM, the breakpoint number
157 *		    must be stored, as breakpoints may be deleted
158 *		TYP is a FIXNUM that must be an integer of enum LispBreakType
159 *		HIT is a FIXNUM, with the number of times this breakpoint was
160 *		    hitted.
161 *		VAR variable to watch a SYMBOL	(not needed for breakpoints)
162 *		VAL value of watched variable	(not needed for breakpoints)
163 *		FRM frame where variable started being watched
164 *						(not needed for breakpoints)
165 *	new elements are added to the end of the list
166 */
167
168/*
169 * Implementation
170 */
171void
172LispDebugger(LispDebugCall call, LispObj *name, LispObj *arg)
173{
174    int force = 0;
175    LispObj *obj, *prev;
176
177    switch (call) {
178	case LispDebugCallBegin:
179	    ++lisp__data.debug_level;
180	    GCDisable();
181	    DBG = CONS(CONS(name, CONS(arg, CONS(FIXNUM(lisp__data.stack.base),
182		       CONS(FIXNUM(lisp__data.env.length),
183			    FIXNUM(lisp__data.env.lex))))), DBG);
184	    GCEnable();
185	    for (obj = BRK; obj != NIL; obj = CDR(obj))
186		if (ATOMID(CAR(CAR(obj))) == ATOMID(name) &&
187		    FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj))))) ==
188		    LispDebugBreakFunction)
189		    break;
190	    if (obj != NIL) {
191		long counter;
192
193		/* if not at a fresh line */
194		if (LispGetColumn(NIL))
195		    LispFputc(Stdout, '\n');
196		LispFputs(Stdout, "BREAK #");
197		LispWriteObject(NIL, CAR(CDR(CAR(obj))));
198		LispFputs(Stdout, "> (");
199		LispWriteObject(NIL, CAR(CAR(DBG)));
200		LispFputc(Stdout, ' ');
201		LispWriteObject(NIL, CAR(CDR(CAR(DBG))));
202		LispFputs(Stdout, ")\n");
203		force = 1;
204		/* update hits counter */
205		counter = FIXNUM_VALUE(CAR(CDR(CDR(CDR(CAR(obj))))));
206		CAR(CDR(CDR(CDR(CAR(obj))))) FIXNUM(counter + 1);
207	    }
208	    break;
209	case LispDebugCallEnd:
210	    DBG = CDR(DBG);
211	    if (lisp__data.debug_level < lisp__data.debug_step)
212		lisp__data.debug_step = lisp__data.debug_level;
213	    --lisp__data.debug_level;
214	    break;
215	case LispDebugCallFatal:
216	    LispDebuggerCommand(NIL);
217	    return;
218	case LispDebugCallWatch:
219	    break;
220    }
221
222    /* didn't return, check watchpoints */
223    if (call == LispDebugCallEnd || call == LispDebugCallWatch) {
224watch_again:
225	for (prev = obj = BRK; obj != NIL; prev = obj, obj = CDR(obj)) {
226	    if (FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj))))) ==
227		LispDebugBreakVariable) {
228		/* the variable */
229		LispObj *wat = CAR(CDR(CDR(CDR(CDR(CAR(obj))))));
230		void *sym = LispGetVarAddr(CAAR(obj));
231		LispObj *frm = CAR(CDR(CDR(CDR(CDR(CDR(CDR(CAR(obj))))))));
232
233		if ((sym == NULL && lisp__data.debug_level <= 0) ||
234		    (sym != wat->data.opaque.data &&
235		     FIXNUM_VALUE(frm) > lisp__data.debug_level)) {
236		    LispFputs(Stdout, "WATCH #");
237		    LispFputs(Stdout, format_integer(FIXNUM_VALUE(CAR(CDR(CAR(obj))))));
238		    LispFputs(Stdout, "> ");
239		    LispFputs(Stdout, STRPTR(CAR(CAR(obj))));
240		    LispFputs(Stdout, " deleted. Variable does not exist anymore.\n");
241		    /* force debugger to stop */
242		    force = 1;
243		    if (obj == prev) {
244			BRK = CDR(BRK);
245			goto watch_again;
246		    }
247		    else
248			RPLACD(prev, CDR(obj));
249		    obj = prev;
250		}
251		else {
252		    /* current value */
253		    LispObj *cur = *(LispObj**)wat->data.opaque.data;
254		    /* last value */
255		    LispObj *val = CAR(CDR(CDR(CDR(CDR(CDR(CAR(obj)))))));
256		    if (XEQUAL(val, cur) == NIL) {
257			long counter;
258
259			LispFputs(Stdout, "WATCH #");
260			LispFputs(Stdout, format_integer(FIXNUM_VALUE(CAR(CDR(CAR(obj))))));
261			LispFputs(Stdout, "> ");
262			LispFputs(Stdout, STRPTR(CAR(CAR(obj))));
263			LispFputc(Stdout, '\n');
264
265			LispFputs(Stdout, "OLD: ");
266			LispWriteObject(NIL, val);
267
268			LispFputs(Stdout, "\nNEW: ");
269			LispWriteObject(NIL, cur);
270			LispFputc(Stdout, '\n');
271
272			/* update current value */
273			CAR(CDR(CDR(CDR(CDR(CDR(CAR(obj))))))) = cur;
274			/* update hits counter */
275			counter = FIXNUM_VALUE(CAR(CDR(CDR(CDR(CAR(obj))))));
276			CAR(CDR(CDR(CDR(CAR(obj))))) = FIXNUM(counter + 1);
277			/* force debugger to stop */
278			force = 1;
279		    }
280		}
281	    }
282	}
283
284	if (call == LispDebugCallWatch)
285	    /* special call, just don't keep gc protected variables that may be
286	     * using a lot of memory... */
287	    return;
288    }
289
290    switch (lisp__data.debug) {
291	case LispDebugUnspec:
292	    LispDebuggerCommand(NIL);
293	    goto debugger_done;
294	case LispDebugRun:
295	    if (force)
296		LispDebuggerCommand(NIL);
297	    goto debugger_done;
298	case LispDebugFinish:
299	    if (!force &&
300		(call != LispDebugCallEnd ||
301		 lisp__data.debug_level != lisp__data.debug_step))
302		goto debugger_done;
303	    break;
304	case LispDebugNext:
305	    if (call == LispDebugCallBegin) {
306		if (!force && lisp__data.debug_level != lisp__data.debug_step)
307		    goto debugger_done;
308	    }
309	    else if (call == LispDebugCallEnd) {
310		if (!force && lisp__data.debug_level >= lisp__data.debug_step)
311		    goto debugger_done;
312	    }
313	    break;
314	case LispDebugStep:
315	    break;
316    }
317
318    if (call == LispDebugCallBegin) {
319	LispFputc(Stdout, '#');
320	LispFputs(Stdout, format_integer(lisp__data.debug_level));
321	LispFputs(Stdout, "> (");
322	LispWriteObject(NIL, CAR(CAR(DBG)));
323	LispFputc(Stdout, ' ');
324	LispWriteObject(NIL, CAR(CDR(CAR(DBG))));
325	LispFputs(Stdout, ")\n");
326	LispDebuggerCommand(NIL);
327    }
328    else if (call == LispDebugCallEnd) {
329	LispFputc(Stdout, '#');
330	LispFputs(Stdout, format_integer(lisp__data.debug_level + 1));
331	LispFputs(Stdout, "= ");
332	LispWriteObject(NIL, arg);
333	LispFputc(Stdout, '\n');
334	LispDebuggerCommand(NIL);
335    }
336    else if (force)
337	LispDebuggerCommand(arg);
338
339debugger_done:
340    return;
341}
342
343static void
344LispDebuggerCommand(LispObj *args)
345{
346    LispObj *obj, *frm, *curframe;
347    int i = 0, frame, matches, action = -1, subaction = 0;
348    char *cmd, *arg, *ptr, line[256];
349
350    int envbase = lisp__data.stack.base,
351	envlen = lisp__data.env.length,
352	envlex = lisp__data.env.lex;
353
354    frame = lisp__data.debug_level;
355    curframe = CAR(DBG);
356
357    line[0] = '\0';
358    arg = line;
359    for (;;) {
360	LispFputs(Stdout, DBGPROMPT);
361	LispFflush(Stdout);
362	if (LispFgets(Stdin, line, sizeof(line)) == NULL) {
363	    LispFputc(Stdout, '\n');
364	    return;
365	}
366	/* get command */
367	ptr = line;
368	while (*ptr && isspace(*ptr))
369	    ++ptr;
370	cmd = ptr;
371	while (*ptr && !isspace(*ptr))
372	    ++ptr;
373	if (*ptr)
374	    *ptr++ = '\0';
375
376	if (*cmd) {	/* if *cmd is nul, then arg may be still set */
377	    /* get argument(s) */
378	    while (*ptr && isspace(*ptr))
379		++ptr;
380	    arg = ptr;
381	    /* goto end of line */
382	    if (*ptr) {
383		while (*ptr)
384		    ++ptr;
385		--ptr;
386		while (*ptr && isspace(*ptr))
387		    --ptr;
388		if (*ptr)
389		    *++ptr = '\0';
390	    }
391	}
392
393	if (*cmd == '\0') {
394	    if (action < 0) {
395		if (lisp__data.debug == LispDebugFinish)
396		    action = DebuggerFinish;
397		else if (lisp__data.debug == LispDebugNext)
398		    action = DebuggerNext;
399		else if (lisp__data.debug == LispDebugStep)
400		    action = DebuggerStep;
401		else if (lisp__data.debug == LispDebugRun)
402		    action = DebuggerContinue;
403		else
404		    continue;
405	    }
406	}
407	else {
408	    for (i = matches = 0; i < sizeof(commands) / sizeof(commands[0]);
409		 i++) {
410		const char *str = commands[i].name;
411
412		ptr = cmd;
413		while (*ptr && *ptr == *str) {
414		    ++ptr;
415		    ++str;
416		}
417		if (*ptr == '\0') {
418		    action = commands[i].action;
419		    if (*str == '\0') {
420			matches = 1;
421			break;
422		    }
423		    ++matches;
424		}
425	    }
426	    if (matches == 0) {
427		LispFputs(Stdout, "* Command unknown: ");
428		LispFputs(Stdout, cmd);
429		LispFputs(Stdout, ". Type help for help.\n");
430		continue;
431	    }
432	    else if (matches > 1) {
433		LispFputs(Stdout, "* Command is ambiguous: ");
434		LispFputs(Stdout, cmd);
435		LispFputs(Stdout, ". Type help for help.\n");
436		continue;
437	    }
438	}
439
440	switch (action) {
441	    case DebuggerHelp:
442		LispFputs(Stdout, debugger_help);
443		break;
444	    case DebuggerInfo:
445		if (*arg == '\0') {
446		    LispFputs(Stdout, debugger_info_help);
447		    break;
448		}
449
450		for (i = matches = 0;
451		     i < sizeof(info_commands) / sizeof(info_commands[0]);
452		     i++) {
453		    const char *str = info_commands[i].name;
454
455		    ptr = arg;
456		    while (*ptr && *ptr == *str) {
457			++ptr;
458			++str;
459		    }
460		    if (*ptr == '\0') {
461			subaction = info_commands[i].subaction;
462			if (*str == '\0') {
463			    matches = 1;
464			    break;
465			}
466			++matches;
467		    }
468		}
469		if (matches == 0) {
470		    LispFputs(Stdout, "* Command unknown: ");
471		    LispFputs(Stdout, arg);
472		    LispFputs(Stdout, ". Type info for help.\n");
473		    continue;
474		}
475		else if (matches > 1) {
476		    LispFputs(Stdout, "* Command is ambiguous: ");
477		    LispFputs(Stdout, arg);
478		    LispFputs(Stdout, ". Type info for help.\n");
479		    continue;
480		}
481
482		switch (subaction) {
483		    case DebuggerInfoBreakpoints:
484			LispFputs(Stdout, "Num\tHits\tType\t\tWhat\n");
485			for (obj = BRK; obj != NIL; obj = CDR(obj)) {
486			    /* breakpoint number */
487			    LispFputc(Stdout, '#');
488			    LispWriteObject(NIL, CAR(CDR(CAR(obj))));
489
490			    /* number of hits */
491			    LispFputc(Stdout, '\t');
492			    LispWriteObject(NIL, CAR(CDR(CDR(CDR(CAR(obj))))));
493
494			    /* breakpoint type */
495			    LispFputc(Stdout, '\t');
496			    switch ((int)FIXNUM_VALUE(CAR(CDR(CDR(CAR(obj)))))) {
497				case LispDebugBreakFunction:
498				    LispFputs(Stdout, "Function");
499				    break;
500				case LispDebugBreakVariable:
501				    LispFputs(Stdout, "Variable");
502				    break;
503			    }
504
505			    /* breakpoint object */
506			    LispFputc(Stdout, '\t');
507			    LispWriteObject(NIL, CAR(CAR(obj)));
508			    LispFputc(Stdout, '\n');
509			}
510			break;
511		    case DebuggerInfoBacktrace:
512			goto debugger_print_backtrace;
513		}
514		break;
515	    case DebuggerAbort:
516		while (lisp__data.mem.level) {
517		    --lisp__data.mem.level;
518		    if (lisp__data.mem.mem[lisp__data.mem.level])
519			free(lisp__data.mem.mem[lisp__data.mem.level]);
520		}
521		lisp__data.mem.index = 0;
522		LispTopLevel();
523		if (!lisp__data.running) {
524		    LispMessage("*** Fatal: nowhere to longjmp.");
525		    abort();
526		}
527		/* don't need to restore environment */
528		siglongjmp(lisp__data.jmp, 1);
529		/*NOTREACHED*/
530		break;
531	    case DebuggerBreak:
532		for (ptr = arg; *ptr; ptr++) {
533		    if (isspace(*ptr))
534			break;
535		    else
536			*ptr = toupper(*ptr);
537		}
538
539		if (!*arg || *ptr || strchr(arg, '(') || strchr(arg, '(') ||
540		    strchr(arg, ';')) {
541		    LispFputs(Stdout, "* Bad function name '");
542		    LispFputs(Stdout, arg);
543		    LispFputs(Stdout, "' specified.\n");
544		}
545		else {
546		    for (obj = frm = BRK; obj != NIL; frm = obj, obj = CDR(obj))
547			;
548		    i = lisp__data.debug_break;
549		    ++lisp__data.debug_break;
550		    GCDisable();
551		    obj = CONS(ATOM(arg),
552			       CONS(FIXNUM(i),
553				    CONS(FIXNUM(LispDebugBreakFunction),
554					 CONS(FIXNUM(0), NIL))));
555		    if (BRK == NIL)
556			BRK = CONS(obj, NIL);
557		    else
558			RPLACD(frm, CONS(obj, NIL));
559		    GCEnable();
560		}
561		break;
562	    case DebuggerWatch: {
563		void *sym;
564		int vframe;
565		LispObj *val, *atom;
566
567		/* make variable name uppercase, an ATOM */
568		ptr = arg;
569		while (*ptr) {
570		    *ptr = toupper(*ptr);
571		    ++ptr;
572		}
573		atom = ATOM(arg);
574		val = LispGetVar(atom);
575		if (val == NULL) {
576		    LispFputs(Stdout, "* No variable named '");
577		    LispFputs(Stdout, arg);
578		    LispFputs(Stdout, "' in the selected frame.\n");
579		    break;
580		}
581
582		/* variable is available at the current frame */
583		sym = LispGetVarAddr(atom);
584
585		/* find the lowest frame where the variable is visible */
586		vframe = 0;
587		if (frame > 0) {
588		    for (; vframe < frame; vframe++) {
589			for (frm = DBG, i = lisp__data.debug_level; i > vframe;
590			     frm = CDR(frm), i--)
591			    ;
592			obj = CAR(frm);
593			lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(obj))));
594			lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(obj)))));
595			lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(obj)))));
596
597			if (LispGetVarAddr(atom) == sym)
598			    /* got variable initial frame */
599			    break;
600		    }
601		    vframe = i;
602		    if (vframe != frame) {
603			/* restore environment */
604			for (frm = DBG, i = lisp__data.debug_level; i > frame;
605			     frm = CDR(frm), i--)
606			    ;
607			obj = CAR(frm);
608			lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(obj))));
609			lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(obj)))));
610			lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(obj)))));
611		    }
612		}
613
614		i = lisp__data.debug_break;
615		++lisp__data.debug_break;
616		for (obj = frm = BRK; obj != NIL; frm = obj, obj = CDR(obj))
617		    ;
618
619		GCDisable();
620		obj = CONS(atom,					/* NAM */
621			   CONS(FIXNUM(i),				/* IDX */
622				CONS(FIXNUM(LispDebugBreakVariable),	/* TYP */
623				     CONS(FIXNUM(0),			/* HIT */
624					  CONS(OPAQUE(sym, 0),		/* VAR */
625					       CONS(val,		/* VAL */
626						    CONS(FIXNUM(vframe),/* FRM */
627							      NIL)))))));
628
629		/* add watchpoint */
630		if (BRK == NIL)
631		    BRK = CONS(obj, NIL);
632		else
633		    RPLACD(frm, CONS(obj, NIL));
634		GCEnable();
635	    }	break;
636	    case DebuggerDelete:
637		if (*arg == 0) {
638		    int confirm = 0;
639
640		    for (;;) {
641			int ch;
642
643			LispFputs(Stdout, "* Delete all breakpoints? (y or n) ");
644			LispFflush(Stdout);
645			if ((ch = LispFgetc(Stdin)) == '\n')
646			    continue;
647			while ((i = LispFgetc(Stdin)) != '\n' && i != EOF)
648			    ;
649			if (tolower(ch) == 'n')
650			    break;
651			else if (tolower(ch) == 'y') {
652			    confirm = 1;
653			    break;
654			}
655		    }
656		    if (confirm)
657			BRK = NIL;
658		}
659		else {
660		    for (ptr = arg; *ptr;) {
661			while (*ptr && isdigit(*ptr))
662			    ++ptr;
663			if (*ptr && !isspace(*ptr)) {
664			    *ptr = '\0';
665			    LispFputs(Stdout, "* Bad breakpoint number '");
666			    LispFputs(Stdout, arg);
667			    LispFputs(Stdout, "' specified.\n");
668			    break;
669			}
670			i = atoi(arg);
671			for (obj = frm = BRK; frm != NIL;
672			     obj = frm, frm = CDR(frm))
673			    if (FIXNUM_VALUE(CAR(CDR(CAR(frm)))) == i)
674				break;
675			if (frm == NIL) {
676			    LispFputs(Stdout, "* No breakpoint number ");
677			    LispFputs(Stdout, arg);
678			    LispFputs(Stdout, " available.\n");
679			    break;
680			}
681			if (obj == frm)
682			    BRK = CDR(BRK);
683			else
684			    RPLACD(obj, CDR(frm));
685			while (*ptr && isspace(*ptr))
686			    ++ptr;
687			arg = ptr;
688		    }
689		}
690		break;
691	    case DebuggerFrame:
692		i = -1;
693		ptr = arg;
694		if (*ptr) {
695		    i = 0;
696		    while (*ptr && isdigit(*ptr)) {
697			i *= 10;
698			i += *ptr - '0';
699			++ptr;
700		    }
701		    if (*ptr) {
702			LispFputs(Stdout, "* Frame identifier must "
703				"be a positive number.\n");
704			break;
705		    }
706		}
707		else
708		    goto debugger_print_frame;
709		if (i >= 0 && i <= lisp__data.debug_level)
710		    goto debugger_new_frame;
711		LispFputs(Stdout, "* No such frame ");
712		LispFputs(Stdout, format_integer(i));
713		LispFputs(Stdout, ".\n");
714		break;
715	    case DebuggerDown:
716		if (frame + 1 > lisp__data.debug_level) {
717		    LispFputs(Stdout, "* Cannot go down.\n");
718		    break;
719		}
720		i = frame + 1;
721		goto debugger_new_frame;
722		break;
723	    case DebuggerUp:
724		if (frame == 0) {
725		    LispFputs(Stdout, "* Cannot go up.\n");
726		    break;
727		}
728		i = frame - 1;
729		goto debugger_new_frame;
730		break;
731	    case DebuggerPrint:
732		ptr = arg;
733		while (*ptr) {
734		    *ptr = toupper(*ptr);
735		    ++ptr;
736		}
737		obj = LispGetVar(ATOM(arg));
738		if (obj != NULL) {
739		    LispWriteObject(NIL, obj);
740		    LispFputc(Stdout, '\n');
741		}
742		else {
743		    LispFputs(Stdout, "* No variable named '");
744		    LispFputs(Stdout, arg);
745		    LispFputs(Stdout, "' in the selected frame.\n");
746		}
747		break;
748	    case DebuggerBacktrace:
749debugger_print_backtrace:
750		if (DBG == NIL) {
751		    LispFputs(Stdout, "* No stack.\n");
752		    break;
753		}
754		DBG = LispReverse(DBG);
755		for (obj = DBG, i = 0; obj != NIL; obj = CDR(obj), i++) {
756		    frm = CAR(obj);
757		    LispFputc(Stdout, '#');
758		    LispFputs(Stdout, format_integer(i));
759		    LispFputs(Stdout, "> (");
760		    LispWriteObject(NIL, CAR(frm));
761		    LispFputc(Stdout, ' ');
762		    LispWriteObject(NIL, CAR(CDR(frm)));
763		    LispFputs(Stdout, ")\n");
764		}
765		DBG = LispReverse(DBG);
766		break;
767	    case DebuggerContinue:
768		lisp__data.debug = LispDebugRun;
769		goto debugger_command_done;
770	    case DebuggerFinish:
771		if (lisp__data.debug != LispDebugFinish) {
772		    lisp__data.debug_step = lisp__data.debug_level - 2;
773		    lisp__data.debug = LispDebugFinish;
774		}
775		else
776		    lisp__data.debug_step = lisp__data.debug_level - 1;
777		goto debugger_command_done;
778	    case DebuggerNext:
779		if (lisp__data.debug != LispDebugNext) {
780		    lisp__data.debug = LispDebugNext;
781		    lisp__data.debug_step = lisp__data.debug_level + 1;
782		}
783		goto debugger_command_done;
784	    case DebuggerStep:
785		lisp__data.debug = LispDebugStep;
786		goto debugger_command_done;
787	}
788	continue;
789
790debugger_new_frame:
791	/* goto here with i as the new frame value, after error checking */
792	if (i != frame) {
793	    frame = i;
794	    for (frm = DBG, i = lisp__data.debug_level;
795		 i > frame; frm = CDR(frm), i--)
796		;
797	    curframe = CAR(frm);
798	    lisp__data.stack.base = FIXNUM_VALUE(CAR(CDR(CDR(curframe))));
799	    lisp__data.env.length = FIXNUM_VALUE(CAR(CDR(CDR(CDR(curframe)))));
800	    lisp__data.env.lex = FIXNUM_VALUE(CDR(CDR(CDR(CDR(curframe)))));
801	}
802debugger_print_frame:
803	LispFputc(Stdout, '#');
804	LispFputs(Stdout, format_integer(frame));
805	LispFputs(Stdout, "> (");
806	LispWriteObject(NIL, CAR(curframe));
807	LispFputc(Stdout, ' ');
808	LispWriteObject(NIL, CAR(CDR(curframe)));
809	LispFputs(Stdout, ")\n");
810    }
811
812debugger_command_done:
813    lisp__data.stack.base = envbase;
814    lisp__data.env.length = envlen;
815    lisp__data.env.lex = envlex;
816}
817
818static char *
819format_integer(int integer)
820{
821    static char buffer[16];
822
823    sprintf(buffer, "%d", integer);
824
825    return (buffer);
826}
827
828#endif /* DEBUGGER */
829