1 /* Copyright (c) 2005, David Leonard. All rights reserved. */
2 
3 /*
4  * A simple debugger.
5  */
6 
7 #include <ctype.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <see/see.h>
11 #include "shell.h"
12 #include "debug.h"
13 
14 /* A breakpoint set by the user */
15 struct breakpoint {
16 	struct breakpoint *next;
17 	struct SEE_throw_location loc;
18 	int id;				/* unique id */
19 	int ignore_counter;		/* number of hits to ignore */
20 	int ignore_reset;
21 	int temporary;			/* remove this BP when hit */
22 };
23 
24 /* The current state of the debugger */
25 struct debug {
26 	void *save_host_data;
27 	void (*save_trace)(struct SEE_interpreter *,
28 		struct SEE_throw_location *, struct SEE_context *,
29 		enum SEE_trace_event);
30 	int break_immediately;
31 	struct breakpoint *breakpoints;
32 	int next_bp_id;
33 	char *last_command;
34 	struct SEE_throw_location *current_location;
35 };
36 
37 /* Command table elements */
38 struct cmd {
39 	const char *name;
40 	int (*fn)(struct SEE_interpreter *, struct debug *,
41 		struct SEE_throw_location *, struct SEE_context *,
42 		char *arg);
43 	const char *doc;
44 };
45 
46 /* Prototypes */
47 static struct breakpoint *bp_add(struct SEE_interpreter *, struct debug *,
48         struct SEE_throw_location *, int, int);
49 static void loc_print(FILE *, struct SEE_throw_location *);
50 static int bp_delete(struct SEE_interpreter *, struct debug *, int);
51 static void bp_print(struct SEE_interpreter *, struct breakpoint *, FILE *);
52 static void trace_callback(struct SEE_interpreter *,
53         struct SEE_throw_location *, struct SEE_context *,
54 	enum SEE_trace_event);
55 static int location_matches(struct SEE_interpreter *,
56         struct SEE_throw_location *, struct SEE_throw_location *);
57 static int should_break(struct SEE_interpreter *, struct debug *,
58         struct SEE_throw_location *, enum SEE_trace_event);
59 static int location_parse(struct SEE_interpreter *, struct debug *,
60         struct SEE_throw_location *, struct SEE_throw_location *, char **);
61 
62 static int cmd_where(struct SEE_interpreter *, struct debug *,
63         struct SEE_throw_location *, struct SEE_context *, char *);
64 static int cmd_step(struct SEE_interpreter *, struct debug *,
65         struct SEE_throw_location *, struct SEE_context *, char *);
66 static int cmd_cont(struct SEE_interpreter *, struct debug *,
67         struct SEE_throw_location *, struct SEE_context *, char *);
68 static int cmd_list(struct SEE_interpreter *, struct debug *,
69         struct SEE_throw_location *, struct SEE_context *, char *);
70 static int cmd_break(struct SEE_interpreter *, struct debug *,
71         struct SEE_throw_location *, struct SEE_context *, char *);
72 static void bp_show(struct SEE_interpreter *, struct breakpoint *);
73 static int cmd_show(struct SEE_interpreter *, struct debug *,
74         struct SEE_throw_location *, struct SEE_context *, char *);
75 static int cmd_delete(struct SEE_interpreter *, struct debug *,
76         struct SEE_throw_location *, struct SEE_context *, char *);
77 static int cmd_eval(struct SEE_interpreter *, struct debug *,
78         struct SEE_throw_location *, struct SEE_context *, char *);
79 static int cmd_throw(struct SEE_interpreter *, struct debug *,
80         struct SEE_throw_location *, struct SEE_context *, char *);
81 static int cmd_help(struct SEE_interpreter *, struct debug *,
82         struct SEE_throw_location *, struct SEE_context *, char *);
83 static int cmd_info(struct SEE_interpreter *, struct debug *,
84         struct SEE_throw_location *, struct SEE_context *, char *);
85 static int user_command(struct SEE_interpreter *, struct debug *,
86         struct SEE_throw_location *, struct SEE_context *);
87 static int loc_print_line(struct SEE_interpreter *, struct debug *,
88 	FILE *, struct SEE_throw_location *);
89 
90 static struct cmd cmdtab[] = {
91     { "break",	cmd_break,	"set a breakpoint" },
92     { "cont",	cmd_cont,	"continue running" },
93     { "delete",	cmd_delete,	"delete a breakpoint" },
94     { "eval",	cmd_eval,	"evaluate an expression" },
95     { "help",	cmd_help,	"print this information" },
96     { "info",	cmd_info,	"print context information" },
97     { "list",	cmd_list,	"show nearby lines" },
98     { "show",	cmd_show,	"show current breakpoints" },
99     { "step",	cmd_step,	"run until statement change" },
100     { "throw",	cmd_throw,	"evaluate an expression and throw it" },
101     { "where",	cmd_where,	"show traceback" },
102     { NULL }
103 };
104 
105 
106 /*
107  * Public API
108  */
109 
110 /* Allocates a new debugger context. There should only be one per interp. */
111 struct debug *
debug_new(interp)112 debug_new(interp)
113 	struct SEE_interpreter *interp;
114 {
115 	struct debug *debug;
116 
117 	debug = SEE_NEW(interp, struct debug);
118 	debug->break_immediately = 1;
119 	debug->breakpoints = NULL;
120 	debug->next_bp_id = 0;
121 	return debug;
122 }
123 
124 /* Evaluate input inside the debugger */
125 void
debug_eval(interp,debug,input,res)126 debug_eval(interp, debug, input, res)
127 	struct SEE_interpreter *interp;
128 	struct debug *debug;
129 	struct SEE_input *input;
130 	struct SEE_value *res;
131 {
132 	void *save_host_data;
133 	void (*save_trace)(struct SEE_interpreter *,
134 		struct SEE_throw_location *, struct SEE_context *,
135 		enum SEE_trace_event);
136 	SEE_try_context_t ctxt;
137 
138 	fprintf(stderr, "debugger: starting\n");
139 	save_host_data = debug->save_host_data;
140 	save_trace = debug->save_trace;
141 	debug->save_host_data = interp->host_data;
142 	debug->save_trace = interp->trace;
143 	debug->last_command = NULL;
144 	interp->host_data = debug;
145 	interp->trace = trace_callback;
146 	SEE_TRY(interp, ctxt) {
147 		SEE_Global_eval(interp, input, res);
148 	}
149 	/* finally */
150 	interp->host_data = debug->save_host_data;
151 	interp->trace = debug->save_trace;
152 	debug->save_host_data = save_host_data;
153 	debug->save_trace = save_trace;
154 	if (debug->last_command)
155 		free(debug->last_command);
156 	fprintf(stderr, "debugger: exiting\n");
157 
158 	SEE_DEFAULT_CATCH(interp, ctxt);
159 }
160 
161 /*
162  * Internal functions
163  */
164 
165 /* Adds a breakpoint to the breakpoint list, assigning it a unique number */
166 static struct breakpoint *
bp_add(interp,debug,loc,ignore,temporary)167 bp_add(interp, debug, loc, ignore, temporary)
168 	struct SEE_interpreter *interp;
169 	struct debug *debug;
170 	struct SEE_throw_location *loc;
171 	int ignore, temporary;
172 {
173 	struct breakpoint *bp = SEE_NEW(interp, struct breakpoint);
174 
175 	bp->loc.filename = loc->filename;
176 	bp->loc.lineno = loc->lineno;
177 	bp->id = ++debug->next_bp_id;
178 	bp->ignore_counter = bp->ignore_reset = ignore;
179 	bp->temporary = temporary;
180 
181 	bp->next = debug->breakpoints;
182 	debug->breakpoints = bp;
183 	return bp;
184 }
185 
186 static void
loc_print(file,loc)187 loc_print(file, loc)
188 	FILE *file;
189 	struct SEE_throw_location *loc;
190 {
191 	if (!loc || !loc->filename)
192 		fprintf(file, "<nowhere>");
193 	else {
194 		SEE_string_fputs(loc->filename, file);
195 		fprintf(file, ":%d", loc->lineno);
196 	}
197 }
198 
199 /* Deletes a breakpoint by its unique number. Returns true if deleted. */
200 static int
bp_delete(interp,debug,id)201 bp_delete(interp, debug, id)
202 	struct SEE_interpreter *interp;
203 	struct debug *debug;
204 	int id;
205 {
206 	struct breakpoint *bp, **nbp;
207 
208 	for (bp = *(nbp = &debug->breakpoints); bp; bp = *(nbp = &(bp->next)))
209 		if (bp->id == id) {
210 		    *nbp = bp->next;
211 		    return 1;
212 		}
213 	return 0;
214 }
215 
216 
217 /* Prints a breakpoint in human-readable form */
218 static void
bp_print(interp,bp,file)219 bp_print(interp, bp, file)
220 	struct SEE_interpreter *interp;
221 	struct breakpoint *bp;
222 	FILE *file;
223 {
224 	fprintf(file, "#%d ", bp->id);
225 	loc_print(file, &bp->loc);
226 	if (bp->ignore_reset)
227 		fprintf(file, " (remain %d reset %d)",
228 			bp->ignore_counter, bp->ignore_reset);
229 	if (bp->temporary)
230 		fprintf(file, "[temp]");
231 }
232 
233 
234 /*
235  * Callback function invoked during each step inside the debugger.
236  * Transfers control to the user if a breakpoint is hit.
237  */
238 static void
trace_callback(interp,loc,context,event)239 trace_callback(interp, loc, context, event)
240 	struct SEE_interpreter *interp;
241 	struct SEE_throw_location *loc;
242 	struct SEE_context *context;
243 	enum SEE_trace_event event;
244 {
245 	struct debug *debug;
246 	SEE_try_context_t ctxt;
247 
248 	debug = (struct debug *)interp->host_data;
249 
250 	/* Call any inner trace functions */
251 	if (debug->save_trace) {
252 		interp->host_data = debug->save_host_data;
253 		interp->trace = debug->save_trace;
254 		SEE_TRY(interp, ctxt) {
255 			(*interp->trace)(interp, loc, context, event);
256 		}
257 		interp->host_data = debug;
258 		interp->trace = trace_callback;
259 		SEE_DEFAULT_CATCH(interp, ctxt);
260 	}
261 
262 /*
263 	fprintf(stderr, "event: %s\n",
264 		event == SEE_TRACE_CALL ? "call" :
265 		event == SEE_TRACE_RETURN ? "return" :
266 		event == SEE_TRACE_STATEMENT ? "statement" :
267 		event == SEE_TRACE_THROW ? "throw" :
268 		"?");
269 */
270 	/* Invoke the command prompt on breakpoints */
271 	if (should_break(interp, debug, loc, event)) {
272 		debug->current_location = loc;
273 		loc_print_line(interp, debug, stderr, loc);
274 		while (!user_command(interp, debug, loc, context))
275 			{ /* nothing */ }
276 	}
277 }
278 
279 /* Returns true if the current location is matched by the user location */
280 static int
location_matches(interp,curloc,usrloc)281 location_matches(interp, curloc, usrloc)
282 	struct SEE_interpreter *interp;
283 	struct SEE_throw_location *curloc, *usrloc;
284 {
285 	return usrloc && usrloc->filename && curloc && curloc->filename &&
286 	       SEE_string_cmp(curloc->filename, usrloc->filename) == 0 &&
287 	       curloc->lineno == usrloc->lineno;
288 }
289 
290 /* Returns true if a breakpoint was hit, and the debugger should intercede */
291 static int
should_break(interp,debug,loc,event)292 should_break(interp, debug, loc, event)
293 	struct SEE_interpreter *interp;
294 	struct debug *debug;
295 	struct SEE_throw_location *loc;
296 	enum SEE_trace_event event;
297 {
298 	struct breakpoint *bp, **lbp;
299 
300 	if (event != SEE_TRACE_STATEMENT)
301 	    return 0;
302 
303 	/* Break if the break_immediately flag is set */
304 	if (debug->break_immediately) {
305 		debug->break_immediately = 0;
306 		return 1;
307 	}
308 
309 	/* Scan the breakpoint list for unignoreable bps */
310 	for (bp = *(lbp = &debug->breakpoints); bp; bp = *(lbp = &bp->next))
311 		if (location_matches(interp, loc, &bp->loc)) {
312 		    if (bp->ignore_counter)
313 		    	bp->ignore_counter--;
314 		    else {
315 		    	bp->ignore_counter = bp->ignore_reset;
316 			if (bp->temporary)
317 			    *lbp = bp->next;
318 			return 1;
319 		    }
320 		}
321 	return 0;
322 }
323 
324 /* Parse "[filename:]lineno" into nloc. Returns true if valid */
325 static int
location_parse(interp,debug,loc,nloc,argp)326 location_parse(interp, debug, loc, nloc, argp)
327 	struct SEE_interpreter *interp;
328 	struct debug *debug;
329 	struct SEE_throw_location *loc, *nloc;
330 	char **argp;
331 {
332 	char *p = *argp;
333 	struct SEE_string *filename = loc ? loc->filename : NULL;
334 	int lineno = 0, ok = 0;
335 
336 	if (*p && isdigit(*p) && filename) {
337 		/* pure line number */
338 		while (*p && isdigit(*p)) {
339 			lineno = lineno * 10 + *p - '0';
340 			p++;
341 		}
342 		nloc->filename = filename;
343 		nloc->lineno = lineno;
344 		ok = 1;
345 	} else if (*p && *p != ':') {
346 		char *start = p;
347 		while (*p && !isspace(*p) && *p != ':') p++;
348 		if (*p == ':' && p[1] && isdigit(p[1]))  {
349 			filename = SEE_string_sprintf(interp, "%.*s",
350 				p - start, start);
351 			p++;
352 			while (*p && isdigit(*p)) {
353 				lineno = lineno * 10 + *p - '0';
354 				p++;
355 			}
356 			nloc->filename = filename;
357 			nloc->lineno = lineno;
358 			ok = 1;
359 		} else
360 			fprintf(stderr,
361 				"missing ':<lineno>' after filename\n");
362 	} else
363 	    fprintf(stderr, "expected <filename>:<lineno>\n");
364 	if (ok) {
365 		while (*p && isspace(*p)) p++;
366 		*argp = p;
367 	}
368         return ok;
369 }
370 
371 static int
cmd_where(interp,debug,loc,context,arg)372 cmd_where(interp, debug, loc, context, arg)
373 	struct SEE_interpreter *interp;
374 	struct debug *debug;
375 	struct SEE_throw_location *loc;
376 	struct SEE_context *context;
377 	char *arg;
378 {
379 	SEE_PrintTraceback(interp, stderr);
380 
381 	fprintf(stderr, " @ ");
382 	loc_print(stderr, loc);
383 	fprintf(stderr, "\n");
384 
385 	return 0;
386 }
387 
388 static int
cmd_step(interp,debug,loc,context,arg)389 cmd_step(interp, debug, loc, context, arg)
390 	struct SEE_interpreter *interp;
391 	struct debug *debug;
392 	struct SEE_throw_location *loc;
393 	struct SEE_context *context;
394 	char *arg;
395 {
396 	debug->break_immediately = 1;
397 	return 1;
398 }
399 
400 static int
cmd_cont(interp,debug,loc,context,arg)401 cmd_cont(interp, debug, loc, context, arg)
402 	struct SEE_interpreter *interp;
403 	struct debug *debug;
404 	struct SEE_throw_location *loc;
405 	struct SEE_context *context;
406 	char *arg;
407 {
408 	return 1;
409 }
410 
411 static int
cmd_list(interp,debug,loc,context,arg)412 cmd_list(interp, debug, loc, context, arg)
413 	struct SEE_interpreter *interp;
414 	struct debug *debug;
415 	struct SEE_throw_location *loc;
416 	struct SEE_context *context;
417 	char *arg;
418 {
419 	struct SEE_throw_location rloc;
420 	int offset;
421 	int printed_something = 0;
422 
423 	memcpy(&rloc, loc, sizeof rloc);
424 	for (offset = -3; offset <= 3; offset++) {
425 	    rloc.lineno = loc->lineno + offset;
426 	    if (loc_print_line(interp, debug, stderr, &rloc))
427 		printed_something = 1;;
428 	}
429 	if (!printed_something)
430 	    fprintf(stderr, "debugger: unable to list source file\n");
431 	return 0;
432 }
433 
434 static int
cmd_break(interp,debug,loc,context,arg)435 cmd_break(interp, debug, loc, context, arg)
436 	struct SEE_interpreter *interp;
437 	struct debug *debug;
438 	struct SEE_throw_location *loc;
439 	struct SEE_context *context;
440 	char *arg;
441 {
442 	struct SEE_throw_location bloc;
443 	struct breakpoint *bp;
444 
445 	if (location_parse(interp, debug, loc, &bloc, &arg)) {
446 		bp = bp_add(interp, debug, &bloc, 0, 0);
447 		fprintf(stderr, "debugger: added breakpoint: ");
448 		bp_print(interp, bp, stderr);
449 		fprintf(stderr, "\n");
450 	}
451 	return 0;
452 }
453 
454 void
debug_add_bp(interp,debug,filename,lineno)455 debug_add_bp(interp, debug, filename, lineno)
456 	struct SEE_interpreter *interp;
457 	struct debug *debug;
458         const char *filename;
459 	int lineno;
460 {
461 	struct SEE_throw_location loc;
462 	struct breakpoint *bp;
463 
464 	loc.filename = SEE_string_sprintf(interp, "%s", filename);
465 	loc.lineno = lineno;
466 	bp = bp_add(interp, debug, &loc, 0, 0);
467 	fprintf(stderr, "debugger: added breakpoint: ");
468 	bp_print(interp, bp, stderr);
469 	fprintf(stderr, "\n");
470 }
471 
472 /* Show breakpoints in reverse order */
473 static void
bp_show(interp,bp)474 bp_show(interp, bp)
475 	struct SEE_interpreter *interp;
476 	struct breakpoint *bp;
477 {
478 	if (bp) {
479 	    bp_show(interp, bp->next);
480 	    fprintf(stderr, "  ");
481 	    bp_print(interp, bp, stderr);
482 	    fprintf(stderr, "\n");
483 	}
484 }
485 
486 
487 static int
cmd_show(interp,debug,loc,context,arg)488 cmd_show(interp, debug, loc, context, arg)
489 	struct SEE_interpreter *interp;
490 	struct debug *debug;
491 	struct SEE_throw_location *loc;
492 	struct SEE_context *context;
493 	char *arg;
494 {
495 	if (!debug->breakpoints)
496 		fprintf(stderr, "debugger: no breakpoints\n");
497 	else {
498 		fprintf(stderr, "debugger: current breakpoints:\n");
499 		bp_show(interp, debug->breakpoints);
500 	}
501 	return 0;
502 }
503 
504 static int
cmd_delete(interp,debug,loc,context,arg)505 cmd_delete(interp, debug, loc, context, arg)
506 	struct SEE_interpreter *interp;
507 	struct debug *debug;
508 	struct SEE_throw_location *loc;
509 	struct SEE_context *context;
510 	char *arg;
511 {
512 	char *p;
513 	int id;
514 
515 	p = arg;
516 	if (!*p || !isdigit(*p)) {
517 		fprintf(stderr, "debugger: expected number\n");
518 		return 0;
519 	}
520 	id = 0;
521 	while (*p && isdigit(*p))
522 		id = 10 *id + (*p++ - '0');
523 	if (bp_delete(interp, debug, id))
524 		fprintf(stderr, "debugger: breakpoint #%d deleted\n", id);
525 	else
526 		fprintf(stderr, "debugger: unknown breakpoint #%d\n", id);
527 	return 0;
528 }
529 
530 static int
cmd_eval(interp,debug,loc,context,arg)531 cmd_eval(interp, debug, loc, context, arg)
532 	struct SEE_interpreter *interp;
533 	struct debug *debug;
534 	struct SEE_throw_location *loc;
535 	struct SEE_context *context;
536 	char *arg;
537 {
538 	SEE_try_context_t ctxt;
539 	struct SEE_value res;
540 	struct SEE_string *str;
541 
542 	void (*save_trace)(struct SEE_interpreter *,
543 		struct SEE_throw_location *, struct SEE_context *,
544 		enum SEE_trace_event);
545 
546 	if (!*arg) {
547 		fprintf(stderr, "debugger: expected expression text\n");
548 		return 0;
549 	}
550 	str = SEE_string_sprintf(interp, "%s", arg);
551 	save_trace = interp->trace;
552 	interp->trace = NULL;
553 	SEE_TRY(interp, ctxt) {
554 		SEE_context_eval(context, str, &res);
555 	}
556 	interp->trace = save_trace;
557 	if (SEE_CAUGHT(ctxt)) {
558 		fprintf(stderr, "debugger: caught exception ");
559 		SEE_PrintValue(interp, SEE_CAUGHT(ctxt), stderr);
560 		fprintf(stderr, "\n");
561 	} else {
562 		fprintf(stderr, " = ");
563 		SEE_PrintValue(interp, &res, stderr);
564 		fprintf(stderr, "\n");
565 	}
566 	return 0;
567 }
568 
569 
570 static int
cmd_throw(interp,debug,loc,context,arg)571 cmd_throw(interp, debug, loc, context, arg)
572 	struct SEE_interpreter *interp;
573 	struct debug *debug;
574 	struct SEE_throw_location *loc;
575 	struct SEE_context *context;
576 	char *arg;
577 {
578 	SEE_try_context_t ctxt;
579 	struct SEE_value res;
580 	struct SEE_string *str;
581 
582 	void (*save_trace)(struct SEE_interpreter *,
583 		struct SEE_throw_location *, struct SEE_context *,
584 		enum SEE_trace_event);
585 
586 	if (!*arg) {
587 		fprintf(stderr, "debugger: missing expression argument\n");
588 		return 0;
589 	}
590 	str = SEE_string_sprintf(interp, "%s", arg);
591 	save_trace = interp->trace;
592 	interp->trace = NULL;
593 	SEE_TRY(interp, ctxt) {
594 		SEE_context_eval(context, str, &res);
595 	}
596 	interp->trace = save_trace;
597 	if (SEE_CAUGHT(ctxt)) {
598 		char *yn;
599 
600 		fprintf(stderr, "debugger: exception while evaluating expr: ");
601 		SEE_PrintValue(interp, SEE_CAUGHT(ctxt), stderr);
602 		fprintf(stderr, "\n");
603 		yn = readline("debugger: throw this exception instead? [n]: ");
604 		if (yn && (*yn == 'y' || *yn == 'Y')) {
605 			fprintf(stderr, "debugger: throwing...\n");
606 			SEE_DEFAULT_CATCH(interp, ctxt);
607 		}
608 	} else {
609 		fprintf(stderr, "debugger: throwing ");
610 		SEE_PrintValue(interp, &res, stderr);
611 		fprintf(stderr, " ...\n");
612 		SEE_THROW(interp, &res);
613 	}
614 	return 0;
615 }
616 
617 
618 static int
cmd_help(interp,debug,loc,context,arg)619 cmd_help(interp, debug, loc, context, arg)
620 	struct SEE_interpreter *interp;
621 	struct debug *debug;
622 	struct SEE_throw_location *loc;
623 	struct SEE_context *context;
624 	char *arg;
625 {
626 	int i;
627 
628 	fprintf(stderr, "debugger: command table follows\n");
629 	for (i = 0; cmdtab[i].name; i++)
630 		fprintf(stderr, "   %-20s%s\n",
631 			cmdtab[i].name, cmdtab[i].doc);
632 	return 0;
633 }
634 
635 static int
cmd_info(interp,debug,loc,context,arg)636 cmd_info(interp, debug, loc, context, arg)
637 	struct SEE_interpreter *interp;
638 	struct debug *debug;
639 	struct SEE_throw_location *loc;
640 	struct SEE_context *context;
641 	char *arg;
642 {
643 	fprintf(stderr, "debugger: context info follows\n");
644 	fprintf(stderr, "   activation = ");
645 	SEE_PrintObject(interp, context->activation, stderr);
646 	fprintf(stderr, "\n");
647 	fprintf(stderr, "   variable = ");
648 	SEE_PrintObject(interp, context->variable, stderr);
649 	fprintf(stderr, "\n");
650 	fprintf(stderr, "   varattr = < %s%s%s%s>\n",
651 		context->varattr & SEE_ATTR_READONLY ? "readonly " : "",
652 		context->varattr & SEE_ATTR_DONTENUM ? "dontenum " : "",
653 		context->varattr & SEE_ATTR_DONTDELETE ? "dontdelete " : "",
654 		context->varattr & SEE_ATTR_INTERNAL ? "internal " : "");
655 	fprintf(stderr, "   this = ");
656 	SEE_PrintObject(interp, context->thisobj, stderr);
657 	fprintf(stderr, "\n");
658 	return 0;
659 }
660 
661 
662 /* Prompts user for a command, and carry it out.
663  * Returns true if the command is to resume, false if not.
664  */
665 static int
user_command(interp,debug,loc,context)666 user_command(interp, debug, loc, context)
667 	struct SEE_interpreter *interp;
668 	struct debug *debug;
669 	struct SEE_throw_location *loc;
670 	struct SEE_context *context;
671 {
672 	char *line, *p, *cmd;
673 	int i, result;
674 
675 	loc_print(stderr, loc);
676 	line = readline(" % ");
677 	if (!line) {
678 		fprintf(stderr, "debugger: end-of-file received\n");
679 		exit(1);
680 	}
681 	if (!*line) {
682 	    if (debug->last_command) {
683 		free(line);
684 		line = strdup(debug->last_command);
685 	    }
686 	} else {
687 	    if (debug->last_command)
688 	        free(debug->last_command);
689 	    debug->last_command = strdup(line);
690 	}
691 	p = line;
692 	while (*p && isspace(*p)) p++;	/* skip leading whitespace */
693 
694 	cmd = p;			/* extract initial command word */
695 	if (*p && !isspace(*p)) p++;
696 	while (*p && isalnum(*p)) p++;
697 	if (*p) *p++ = '\0';
698 	while (*p && isspace(*p)) p++;	/* skip following whitespace */
699 
700 	result = 0;
701 	if (*cmd) {
702 	    for (i = 0; cmdtab[i].name; i++)
703 		if (strcmp(cmdtab[i].name, cmd) == 0) {
704 		    result = (*cmdtab[i].fn)(interp, debug, loc, context, p);
705 		    break;
706 		}
707 	    if (!cmdtab[i].name)
708 	    	fprintf(stderr, "debugger: unknown command '%s' (try 'help')\n",
709 			cmd);
710 	}
711 	free(line);
712 	return result;
713 }
714 
715 /* Prints the line from a source file, or nothing if it is not found.
716  * Returns true only if a line is printed. */
717 static int
loc_print_line(interp,debug,out,loc)718 loc_print_line(interp, debug, out, loc)
719 	struct SEE_interpreter *interp;
720 	struct debug *debug;
721 	FILE *out;
722 	struct SEE_throw_location *loc;
723 {
724 	FILE *f;
725 	char path[4096];
726 	int i, ch, lineno;
727 	struct breakpoint *bp;
728 	char clchar, bpchar;
729 
730 	if (!loc->filename)
731 	    return 0;
732 	if (loc->filename->length >= sizeof path - 1)
733 	    return 0;
734 	for (i = 0; i < loc->filename->length; i++) {
735 	    if (loc->filename->data[i] > 0x7f)
736 	    	return 0;
737 	    path[i] = loc->filename->data[i] & 0x7f;
738 	}
739 	path[i] = 0;
740 
741 	f = fopen(path, "r");
742 	if (!f)
743 	    return 0;
744 
745 	lineno = 1;
746 	while (lineno != loc->lineno)  {
747 	    ch = fgetc(f);
748 	    if (ch == EOF)
749 	       return 0;
750 	    if (ch == '\n')
751 	        lineno++;
752 	}
753 
754 	/* Put a * at the front if this line is a breakpoint */
755 	bpchar = ' ';
756 	for (bp = debug->breakpoints; bp; bp = bp->next)
757 	    if (location_matches(interp, loc, &bp->loc))
758 	        bpchar = '*';
759 
760 	clchar = ' ';
761 	if (location_matches(interp, loc, debug->current_location))
762 		clchar = '>';
763 
764 	fprintf(out, "%c%c%3d: ", bpchar, clchar, lineno);
765 	while ((ch = fgetc(f)) != EOF) {
766 	    if (ch == '\n')
767 	        break;
768 	    fputc(ch, out);
769 	}
770 	fputc('\n', out);
771 	fclose(f);
772 	return 1;
773 }
774