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