]>
Commit | Line | Data |
---|---|---|
754e5da2 SG |
1 | /* TK interface routines. |
2 | Copyright 1994 Free Software Foundation, Inc. | |
3 | ||
4 | This file is part of GDB. | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
19 | ||
20 | #include "defs.h" | |
21 | #include "symtab.h" | |
22 | #include "inferior.h" | |
23 | #include "command.h" | |
24 | #include "bfd.h" | |
25 | #include "symfile.h" | |
26 | #include "objfiles.h" | |
27 | #include "target.h" | |
754e5da2 SG |
28 | #include <tcl.h> |
29 | #include <tk.h> | |
cd2df226 SG |
30 | #include <varargs.h> |
31 | #include <signal.h> | |
32 | #include <fcntl.h> | |
8532893d | 33 | #include <unistd.h> |
86db943c SG |
34 | #include <setjmp.h> |
35 | #include "top.h" | |
754e5da2 SG |
36 | |
37 | /* Non-zero means that we're doing the gdbtk interface. */ | |
38 | int gdbtk = 0; | |
39 | ||
40 | /* Non-zero means we are reloading breakpoints, etc from the | |
41 | Gdbtk kernel, and we should suppress various messages */ | |
42 | static int gdbtk_reloading = 0; | |
43 | ||
44 | /* Handle for TCL interpreter */ | |
45 | static Tcl_Interp *interp = NULL; | |
46 | ||
47 | /* Handle for TK main window */ | |
48 | static Tk_Window mainWindow = NULL; | |
49 | ||
479f0f18 SG |
50 | static int x_fd; /* X network socket */ |
51 | ||
754e5da2 SG |
52 | static void |
53 | null_routine(arg) | |
54 | int arg; | |
55 | { | |
56 | } | |
57 | ||
86db943c SG |
58 | static char *saved_output_buf = NULL; /* Start of output buffer */ |
59 | static char *saved_output_data_end = NULL; /* Ptr to nul at end of data */ | |
60 | static int saved_output_buf_free = 0; /* Amount of free space in buffer */ | |
61 | static char saved_output_static_buf[200]; /* Default buffer */ | |
62 | ||
63 | static void | |
64 | start_saving_output () | |
65 | { | |
66 | if (saved_output_buf) | |
67 | abort (); /* Should always be zero at this point */ | |
68 | ||
69 | saved_output_buf = saved_output_static_buf; | |
70 | saved_output_data_end = saved_output_buf; | |
71 | *saved_output_data_end = '\000'; | |
72 | saved_output_buf_free = sizeof saved_output_static_buf - 1; | |
73 | } | |
74 | ||
75 | static void | |
76 | save_output (ptr) | |
77 | const char *ptr; | |
78 | { | |
79 | int len; | |
80 | int needed, data_len; | |
81 | ||
82 | len = strlen (ptr); | |
83 | ||
84 | if (len <= saved_output_buf_free) | |
85 | { | |
86 | strcpy (saved_output_data_end, ptr); | |
87 | saved_output_data_end += len; | |
88 | saved_output_buf_free -= len; | |
89 | return; | |
90 | } | |
91 | ||
92 | data_len = saved_output_data_end - saved_output_buf; | |
93 | needed = (data_len + len + 1) * 2; | |
94 | ||
95 | if (saved_output_buf == saved_output_static_buf) | |
96 | { | |
97 | char *tmp; | |
98 | ||
99 | tmp = xmalloc (needed); | |
100 | strcpy (tmp, saved_output_buf); | |
101 | saved_output_buf = tmp; | |
102 | } | |
103 | else | |
104 | saved_output_buf = xrealloc (saved_output_buf, needed); | |
105 | ||
106 | saved_output_data_end = saved_output_buf + data_len; | |
107 | saved_output_buf_free = (needed - data_len) - 1; | |
108 | ||
109 | save_output (ptr); | |
110 | } | |
111 | ||
112 | #define get_saved_output() saved_output_buf | |
113 | ||
114 | static void | |
115 | finish_saving_output () | |
116 | { | |
117 | if (saved_output_buf != saved_output_static_buf) | |
118 | free (saved_output_buf); | |
119 | ||
120 | saved_output_buf = NULL; | |
121 | } | |
754e5da2 SG |
122 | \f |
123 | /* This routine redirects the output of fputs_unfiltered so that | |
124 | the user can see what's going on in his debugger window. */ | |
125 | ||
8532893d SG |
126 | static char holdbuf[200]; |
127 | static char *holdbufp = holdbuf; | |
128 | static int holdfree = sizeof (holdbuf); | |
129 | ||
754e5da2 | 130 | static void |
8532893d | 131 | flush_holdbuf () |
754e5da2 | 132 | { |
8532893d SG |
133 | if (holdbufp == holdbuf) |
134 | return; | |
135 | ||
136 | Tcl_VarEval (interp, "gdbtk_tcl_fputs ", "{", holdbuf, "}", NULL); | |
137 | holdbufp = holdbuf; | |
138 | holdfree = sizeof (holdbuf); | |
754e5da2 SG |
139 | } |
140 | ||
141 | static void | |
142 | gdbtk_flush (stream) | |
143 | FILE *stream; | |
144 | { | |
86db943c SG |
145 | if (stream != gdb_stdout || saved_output_buf) |
146 | return; | |
147 | ||
148 | /* Flush output from C to tcl land. */ | |
149 | ||
8532893d SG |
150 | flush_holdbuf (); |
151 | ||
86db943c SG |
152 | /* Force immediate screen update */ |
153 | ||
754e5da2 SG |
154 | Tcl_VarEval (interp, "gdbtk_tcl_flush", NULL); |
155 | } | |
156 | ||
8532893d | 157 | static void |
86db943c | 158 | gdbtk_fputs (ptr, stream) |
8532893d | 159 | const char *ptr; |
86db943c | 160 | FILE *stream; |
8532893d SG |
161 | { |
162 | int len; | |
163 | ||
86db943c SG |
164 | if (stream != gdb_stdout) |
165 | { | |
166 | Tcl_VarEval (interp, "gdbtk_tcl_fputs_error ", "{", ptr, "}", NULL); | |
167 | return; | |
168 | } | |
169 | ||
170 | if (saved_output_buf) | |
171 | { | |
172 | save_output (ptr); | |
173 | return; | |
174 | } | |
175 | ||
8532893d SG |
176 | len = strlen (ptr) + 1; |
177 | ||
178 | if (len > holdfree) | |
179 | { | |
180 | flush_holdbuf (); | |
181 | ||
182 | if (len > sizeof (holdbuf)) | |
183 | { | |
184 | Tcl_VarEval (interp, "gdbtk_tcl_fputs ", "{", ptr, "}", NULL); | |
185 | return; | |
186 | } | |
187 | } | |
188 | ||
189 | strncpy (holdbufp, ptr, len); | |
190 | holdbufp += len - 1; | |
191 | holdfree -= len - 1; | |
192 | } | |
193 | ||
754e5da2 SG |
194 | static int |
195 | gdbtk_query (args) | |
196 | va_list args; | |
197 | { | |
198 | char *query; | |
199 | char buf[200]; | |
200 | long val; | |
201 | ||
202 | query = va_arg (args, char *); | |
203 | ||
204 | vsprintf(buf, query, args); | |
205 | Tcl_VarEval (interp, "gdbtk_tcl_query ", "{", buf, "}", NULL); | |
206 | ||
207 | val = atol (interp->result); | |
208 | return val; | |
209 | } | |
210 | \f | |
754e5da2 SG |
211 | static void |
212 | breakpoint_notify(b, action) | |
213 | struct breakpoint *b; | |
214 | const char *action; | |
215 | { | |
216 | struct symbol *sym; | |
8532893d | 217 | char bpnum[50], line[50], pc[50]; |
754e5da2 SG |
218 | struct symtab_and_line sal; |
219 | char *filename; | |
220 | int v; | |
221 | ||
222 | if (b->type != bp_breakpoint) | |
223 | return; | |
224 | ||
225 | sal = find_pc_line (b->address, 0); | |
226 | ||
637b1661 | 227 | filename = symtab_to_filename (sal.symtab); |
754e5da2 SG |
228 | |
229 | sprintf (bpnum, "%d", b->number); | |
230 | sprintf (line, "%d", sal.line); | |
8532893d | 231 | sprintf (pc, "0x%x", b->address); |
754e5da2 SG |
232 | |
233 | v = Tcl_VarEval (interp, | |
234 | "gdbtk_tcl_breakpoint ", | |
235 | action, | |
236 | " ", bpnum, | |
237 | " ", filename, | |
238 | " ", line, | |
8532893d | 239 | " ", pc, |
754e5da2 SG |
240 | NULL); |
241 | ||
242 | if (v != TCL_OK) | |
243 | { | |
244 | gdbtk_fputs (interp->result); | |
245 | gdbtk_fputs ("\n"); | |
246 | } | |
754e5da2 SG |
247 | } |
248 | ||
249 | static void | |
250 | gdbtk_create_breakpoint(b) | |
251 | struct breakpoint *b; | |
252 | { | |
253 | breakpoint_notify(b, "create"); | |
254 | } | |
255 | ||
256 | static void | |
257 | gdbtk_delete_breakpoint(b) | |
258 | struct breakpoint *b; | |
259 | { | |
260 | breakpoint_notify(b, "delete"); | |
261 | } | |
262 | ||
263 | static void | |
264 | gdbtk_enable_breakpoint(b) | |
265 | struct breakpoint *b; | |
266 | { | |
267 | breakpoint_notify(b, "enable"); | |
268 | } | |
269 | ||
270 | static void | |
271 | gdbtk_disable_breakpoint(b) | |
272 | struct breakpoint *b; | |
273 | { | |
274 | breakpoint_notify(b, "disable"); | |
275 | } | |
276 | \f | |
277 | /* This implements the TCL command `gdb_loc', which returns a list consisting | |
278 | of the source and line number associated with the current pc. */ | |
279 | ||
280 | static int | |
281 | gdb_loc (clientData, interp, argc, argv) | |
282 | ClientData clientData; | |
283 | Tcl_Interp *interp; | |
284 | int argc; | |
285 | char *argv[]; | |
286 | { | |
287 | char *filename; | |
288 | char buf[100]; | |
289 | struct symtab_and_line sal; | |
290 | char *funcname; | |
8532893d | 291 | CORE_ADDR pc; |
754e5da2 SG |
292 | |
293 | if (argc == 1) | |
294 | { | |
1dfc8dfb | 295 | pc = selected_frame ? selected_frame->pc : stop_pc; |
754e5da2 SG |
296 | sal = find_pc_line (pc, 0); |
297 | } | |
298 | else if (argc == 2) | |
299 | { | |
754e5da2 | 300 | struct symtabs_and_lines sals; |
8532893d | 301 | int nelts; |
754e5da2 SG |
302 | |
303 | sals = decode_line_spec (argv[1], 1); | |
304 | ||
8532893d SG |
305 | nelts = sals.nelts; |
306 | sal = sals.sals[0]; | |
307 | free (sals.sals); | |
308 | ||
754e5da2 SG |
309 | if (sals.nelts != 1) |
310 | { | |
311 | Tcl_SetResult (interp, "Ambiguous line spec", TCL_STATIC); | |
754e5da2 SG |
312 | return TCL_ERROR; |
313 | } | |
314 | ||
8532893d | 315 | pc = sal.pc; |
754e5da2 SG |
316 | } |
317 | else | |
318 | { | |
319 | Tcl_SetResult (interp, "wrong # args", TCL_STATIC); | |
320 | return TCL_ERROR; | |
321 | } | |
322 | ||
754e5da2 SG |
323 | if (sal.symtab) |
324 | Tcl_AppendElement (interp, sal.symtab->filename); | |
325 | else | |
326 | Tcl_AppendElement (interp, ""); | |
8532893d SG |
327 | |
328 | find_pc_partial_function (pc, &funcname, NULL, NULL); | |
754e5da2 | 329 | Tcl_AppendElement (interp, funcname); |
8532893d | 330 | |
637b1661 | 331 | filename = symtab_to_filename (sal.symtab); |
754e5da2 | 332 | Tcl_AppendElement (interp, filename); |
8532893d SG |
333 | |
334 | sprintf (buf, "%d", sal.line); | |
754e5da2 SG |
335 | Tcl_AppendElement (interp, buf); /* line number */ |
336 | ||
8532893d SG |
337 | sprintf (buf, "0x%x", pc); |
338 | Tcl_AppendElement (interp, buf); /* PC */ | |
339 | ||
754e5da2 SG |
340 | return TCL_OK; |
341 | } | |
342 | \f | |
5b21fb68 SG |
343 | /* This implements the TCL command `gdb_sourcelines', which returns a list of |
344 | all of the lines containing executable code for the specified source file | |
345 | (ie: lines where you can put breakpoints). */ | |
346 | ||
347 | static int | |
348 | gdb_sourcelines (clientData, interp, argc, argv) | |
349 | ClientData clientData; | |
350 | Tcl_Interp *interp; | |
351 | int argc; | |
352 | char *argv[]; | |
353 | { | |
354 | struct symtab *symtab; | |
355 | struct linetable_entry *le; | |
356 | int nlines; | |
357 | char buf[100]; | |
358 | ||
359 | if (argc != 2) | |
360 | { | |
361 | Tcl_SetResult (interp, "wrong # args", TCL_STATIC); | |
362 | return TCL_ERROR; | |
363 | } | |
364 | ||
365 | symtab = lookup_symtab (argv[1]); | |
366 | ||
367 | if (!symtab) | |
368 | { | |
369 | Tcl_SetResult (interp, "No such file", TCL_STATIC); | |
370 | return TCL_ERROR; | |
371 | } | |
372 | ||
373 | /* If there's no linetable, or no entries, then we are done. */ | |
374 | ||
375 | if (!symtab->linetable | |
376 | || symtab->linetable->nitems == 0) | |
377 | { | |
378 | Tcl_AppendElement (interp, ""); | |
379 | return TCL_OK; | |
380 | } | |
381 | ||
382 | le = symtab->linetable->item; | |
383 | nlines = symtab->linetable->nitems; | |
384 | ||
385 | for (;nlines > 0; nlines--, le++) | |
386 | { | |
387 | /* If the pc of this line is the same as the pc of the next line, then | |
388 | just skip it. */ | |
389 | if (nlines > 1 | |
390 | && le->pc == (le + 1)->pc) | |
391 | continue; | |
392 | ||
393 | sprintf (buf, "%d", le->line); | |
394 | Tcl_AppendElement (interp, buf); | |
395 | } | |
396 | ||
397 | return TCL_OK; | |
398 | } | |
399 | \f | |
746d1df4 SG |
400 | static int |
401 | map_arg_registers (argc, argv, func, argp) | |
402 | int argc; | |
403 | char *argv[]; | |
404 | int (*func) PARAMS ((int regnum, void *argp)); | |
405 | void *argp; | |
406 | { | |
407 | int regnum; | |
408 | ||
409 | /* Note that the test for a valid register must include checking the | |
410 | reg_names array because NUM_REGS may be allocated for the union of the | |
411 | register sets within a family of related processors. In this case, the | |
412 | trailing entries of reg_names will change depending upon the particular | |
413 | processor being debugged. */ | |
414 | ||
415 | if (argc == 0) /* No args, just do all the regs */ | |
416 | { | |
417 | for (regnum = 0; | |
418 | regnum < NUM_REGS | |
419 | && reg_names[regnum] != NULL | |
420 | && *reg_names[regnum] != '\000'; | |
421 | regnum++) | |
422 | func (regnum, argp); | |
423 | ||
424 | return TCL_OK; | |
425 | } | |
426 | ||
427 | /* Else, list of register #s, just do listed regs */ | |
428 | for (; argc > 0; argc--, argv++) | |
429 | { | |
430 | regnum = atoi (*argv); | |
431 | ||
432 | if (regnum >= 0 | |
433 | && regnum < NUM_REGS | |
434 | && reg_names[regnum] != NULL | |
435 | && *reg_names[regnum] != '\000') | |
436 | func (regnum, argp); | |
437 | else | |
438 | { | |
439 | Tcl_SetResult (interp, "bad register number", TCL_STATIC); | |
440 | ||
441 | return TCL_ERROR; | |
442 | } | |
443 | } | |
444 | ||
445 | return TCL_OK; | |
446 | } | |
447 | ||
448 | static int | |
449 | get_register_name (regnum, argp) | |
450 | int regnum; | |
451 | void *argp; /* Ignored */ | |
452 | { | |
453 | Tcl_AppendElement (interp, reg_names[regnum]); | |
454 | } | |
455 | ||
5b21fb68 SG |
456 | /* This implements the TCL command `gdb_regnames', which returns a list of |
457 | all of the register names. */ | |
458 | ||
459 | static int | |
460 | gdb_regnames (clientData, interp, argc, argv) | |
461 | ClientData clientData; | |
462 | Tcl_Interp *interp; | |
463 | int argc; | |
464 | char *argv[]; | |
465 | { | |
746d1df4 SG |
466 | argc--; |
467 | argv++; | |
468 | ||
469 | return map_arg_registers (argc, argv, get_register_name, 0); | |
470 | } | |
471 | ||
746d1df4 SG |
472 | #ifndef REGISTER_CONVERTIBLE |
473 | #define REGISTER_CONVERTIBLE(x) (0 != 0) | |
474 | #endif | |
475 | ||
476 | #ifndef REGISTER_CONVERT_TO_VIRTUAL | |
477 | #define REGISTER_CONVERT_TO_VIRTUAL(x, y, z, a) | |
478 | #endif | |
479 | ||
480 | #ifndef INVALID_FLOAT | |
481 | #define INVALID_FLOAT(x, y) (0 != 0) | |
482 | #endif | |
483 | ||
484 | static int | |
485 | get_register (regnum, fp) | |
486 | void *fp; | |
487 | { | |
488 | char raw_buffer[MAX_REGISTER_RAW_SIZE]; | |
489 | char virtual_buffer[MAX_REGISTER_VIRTUAL_SIZE]; | |
490 | int format = (int)fp; | |
491 | ||
492 | if (read_relative_register_raw_bytes (regnum, raw_buffer)) | |
493 | { | |
494 | Tcl_AppendElement (interp, "Optimized out"); | |
495 | return; | |
496 | } | |
497 | ||
86db943c | 498 | start_saving_output (); /* Start collecting stdout */ |
746d1df4 SG |
499 | |
500 | /* Convert raw data to virtual format if necessary. */ | |
501 | ||
502 | if (REGISTER_CONVERTIBLE (regnum)) | |
503 | { | |
504 | REGISTER_CONVERT_TO_VIRTUAL (regnum, REGISTER_VIRTUAL_TYPE (regnum), | |
505 | raw_buffer, virtual_buffer); | |
506 | } | |
507 | else | |
508 | memcpy (virtual_buffer, raw_buffer, REGISTER_VIRTUAL_SIZE (regnum)); | |
509 | ||
510 | val_print (REGISTER_VIRTUAL_TYPE (regnum), virtual_buffer, 0, | |
511 | gdb_stdout, format, 1, 0, Val_pretty_default); | |
512 | ||
86db943c | 513 | Tcl_AppendElement (interp, get_saved_output ()); |
746d1df4 | 514 | |
86db943c | 515 | finish_saving_output (); /* Set stdout back to normal */ |
746d1df4 SG |
516 | } |
517 | ||
518 | static int | |
519 | gdb_fetch_registers (clientData, interp, argc, argv) | |
520 | ClientData clientData; | |
521 | Tcl_Interp *interp; | |
522 | int argc; | |
523 | char *argv[]; | |
524 | { | |
525 | int format; | |
526 | ||
527 | if (argc < 2) | |
5b21fb68 SG |
528 | { |
529 | Tcl_SetResult (interp, "wrong # args", TCL_STATIC); | |
530 | return TCL_ERROR; | |
531 | } | |
532 | ||
746d1df4 SG |
533 | argc--; |
534 | argv++; | |
5b21fb68 | 535 | |
746d1df4 SG |
536 | argc--; |
537 | format = **argv++; | |
538 | ||
539 | return map_arg_registers (argc, argv, get_register, format); | |
540 | } | |
541 | ||
542 | /* This contains the previous values of the registers, since the last call to | |
543 | gdb_changed_register_list. */ | |
544 | ||
545 | static char old_regs[REGISTER_BYTES]; | |
546 | ||
547 | static int | |
548 | register_changed_p (regnum, argp) | |
549 | void *argp; /* Ignored */ | |
550 | { | |
551 | char raw_buffer[MAX_REGISTER_RAW_SIZE]; | |
552 | char buf[100]; | |
553 | ||
554 | if (read_relative_register_raw_bytes (regnum, raw_buffer)) | |
555 | return; | |
556 | ||
557 | if (memcmp (&old_regs[REGISTER_BYTE (regnum)], raw_buffer, | |
558 | REGISTER_RAW_SIZE (regnum)) == 0) | |
559 | return; | |
560 | ||
561 | /* Found a changed register. Save new value and return it's number. */ | |
562 | ||
563 | memcpy (&old_regs[REGISTER_BYTE (regnum)], raw_buffer, | |
564 | REGISTER_RAW_SIZE (regnum)); | |
565 | ||
566 | sprintf (buf, "%d", regnum); | |
567 | Tcl_AppendElement (interp, buf); | |
568 | } | |
569 | ||
570 | static int | |
571 | gdb_changed_register_list (clientData, interp, argc, argv) | |
572 | ClientData clientData; | |
573 | Tcl_Interp *interp; | |
574 | int argc; | |
575 | char *argv[]; | |
576 | { | |
577 | int format; | |
578 | ||
579 | argc--; | |
580 | argv++; | |
581 | ||
582 | return map_arg_registers (argc, argv, register_changed_p, NULL); | |
5b21fb68 SG |
583 | } |
584 | \f | |
754e5da2 SG |
585 | /* This implements the TCL command `gdb_cmd', which sends it's argument into |
586 | the GDB command scanner. */ | |
587 | ||
588 | static int | |
589 | gdb_cmd (clientData, interp, argc, argv) | |
590 | ClientData clientData; | |
591 | Tcl_Interp *interp; | |
592 | int argc; | |
593 | char *argv[]; | |
594 | { | |
754e5da2 SG |
595 | if (argc != 2) |
596 | { | |
597 | Tcl_SetResult (interp, "wrong # args", TCL_STATIC); | |
598 | return TCL_ERROR; | |
599 | } | |
600 | ||
86db943c | 601 | execute_command (argv[1], 1); |
479f0f18 | 602 | |
754e5da2 | 603 | bpstat_do_actions (&stop_bpstat); |
754e5da2 | 604 | |
8532893d SG |
605 | /* Drain all buffered command output */ |
606 | ||
8532893d SG |
607 | gdb_flush (gdb_stdout); |
608 | ||
754e5da2 SG |
609 | return TCL_OK; |
610 | } | |
611 | ||
86db943c SG |
612 | /* This routine acts as a top-level for all GDB code called by tcl/Tk. It |
613 | handles cleanups, and calls to return_to_top_level (usually via error). | |
614 | This is necessary in order to prevent a longjmp out of the bowels of Tk, | |
615 | possibly leaving things in a bad state. Since this routine can be called | |
616 | recursively, it needs to save and restore the contents of the jmp_buf as | |
617 | necessary. */ | |
618 | ||
619 | static int | |
620 | call_wrapper (clientData, interp, argc, argv) | |
621 | ClientData clientData; | |
622 | Tcl_Interp *interp; | |
623 | int argc; | |
624 | char *argv[]; | |
625 | { | |
626 | int val; | |
627 | struct cleanup *saved_cleanup_chain; | |
628 | Tcl_CmdProc *func; | |
629 | jmp_buf saved_error_return; | |
630 | ||
631 | func = (Tcl_CmdProc *)clientData; | |
632 | memcpy (saved_error_return, error_return, sizeof (jmp_buf)); | |
633 | ||
634 | saved_cleanup_chain = save_cleanups (); | |
635 | ||
636 | if (!setjmp (error_return)) | |
637 | val = func (clientData, interp, argc, argv); | |
638 | else | |
639 | { | |
640 | val = TCL_ERROR; /* Flag an error for TCL */ | |
641 | ||
642 | finish_saving_output (); /* Restore stdout to normal */ | |
643 | ||
644 | gdb_flush (gdb_stderr); /* Flush error output */ | |
645 | ||
646 | /* In case of an error, we may need to force the GUI into idle mode because | |
647 | gdbtk_call_command may have bombed out while in the command routine. */ | |
648 | ||
649 | Tcl_VarEval (interp, "gdbtk_tcl_idle", NULL); | |
650 | } | |
651 | ||
652 | do_cleanups (ALL_CLEANUPS); | |
653 | ||
654 | restore_cleanups (saved_cleanup_chain); | |
655 | ||
656 | memcpy (error_return, saved_error_return, sizeof (jmp_buf)); | |
657 | ||
658 | return val; | |
659 | } | |
660 | ||
754e5da2 SG |
661 | static int |
662 | gdb_listfiles (clientData, interp, argc, argv) | |
663 | ClientData clientData; | |
664 | Tcl_Interp *interp; | |
665 | int argc; | |
666 | char *argv[]; | |
667 | { | |
668 | int val; | |
669 | struct objfile *objfile; | |
670 | struct partial_symtab *psymtab; | |
671 | ||
672 | ALL_PSYMTABS (objfile, psymtab) | |
673 | Tcl_AppendElement (interp, psymtab->filename); | |
674 | ||
675 | return TCL_OK; | |
676 | } | |
479f0f18 SG |
677 | |
678 | static int | |
679 | gdb_stop (clientData, interp, argc, argv) | |
680 | ClientData clientData; | |
681 | Tcl_Interp *interp; | |
682 | int argc; | |
683 | char *argv[]; | |
684 | { | |
6c27841f | 685 | target_stop (); |
479f0f18 SG |
686 | } |
687 | ||
754e5da2 SG |
688 | \f |
689 | static void | |
690 | tk_command (cmd, from_tty) | |
691 | char *cmd; | |
692 | int from_tty; | |
693 | { | |
694 | Tcl_VarEval (interp, cmd, NULL); | |
695 | ||
696 | gdbtk_fputs (interp->result); | |
697 | gdbtk_fputs ("\n"); | |
698 | } | |
699 | ||
700 | static void | |
701 | cleanup_init (ignored) | |
702 | int ignored; | |
703 | { | |
704 | if (mainWindow != NULL) | |
705 | Tk_DestroyWindow (mainWindow); | |
706 | mainWindow = NULL; | |
707 | ||
708 | if (interp != NULL) | |
709 | Tcl_DeleteInterp (interp); | |
710 | interp = NULL; | |
711 | } | |
712 | ||
637b1661 SG |
713 | /* Come here during long calculations to check for GUI events. Usually invoked |
714 | via the QUIT macro. */ | |
715 | ||
716 | static void | |
717 | gdbtk_interactive () | |
718 | { | |
719 | /* Tk_DoOneEvent (TK_DONT_WAIT|TK_IDLE_EVENTS); */ | |
720 | } | |
721 | ||
479f0f18 SG |
722 | /* Come here when there is activity on the X file descriptor. */ |
723 | ||
724 | static void | |
725 | x_event (signo) | |
726 | int signo; | |
727 | { | |
728 | /* Process pending events */ | |
729 | ||
730 | while (Tk_DoOneEvent (TK_DONT_WAIT|TK_ALL_EVENTS) != 0); | |
731 | } | |
732 | ||
733 | static int | |
734 | gdbtk_wait (pid, ourstatus) | |
735 | int pid; | |
736 | struct target_waitstatus *ourstatus; | |
737 | { | |
738 | signal (SIGIO, x_event); | |
739 | ||
740 | pid = target_wait (pid, ourstatus); | |
741 | ||
742 | signal (SIGIO, SIG_IGN); | |
743 | ||
744 | return pid; | |
745 | } | |
746 | ||
747 | /* This is called from execute_command, and provides a wrapper around | |
748 | various command routines in a place where both protocol messages and | |
749 | user input both flow through. Mostly this is used for indicating whether | |
750 | the target process is running or not. | |
751 | */ | |
752 | ||
753 | static void | |
754 | gdbtk_call_command (cmdblk, arg, from_tty) | |
755 | struct cmd_list_element *cmdblk; | |
756 | char *arg; | |
757 | int from_tty; | |
758 | { | |
759 | if (cmdblk->class == class_run) | |
760 | { | |
761 | Tcl_VarEval (interp, "gdbtk_tcl_busy", NULL); | |
762 | (*cmdblk->function.cfunc)(arg, from_tty); | |
763 | Tcl_VarEval (interp, "gdbtk_tcl_idle", NULL); | |
764 | } | |
765 | else | |
766 | (*cmdblk->function.cfunc)(arg, from_tty); | |
767 | } | |
768 | ||
754e5da2 SG |
769 | static void |
770 | gdbtk_init () | |
771 | { | |
772 | struct cleanup *old_chain; | |
773 | char *gdbtk_filename; | |
479f0f18 | 774 | int i; |
754e5da2 SG |
775 | |
776 | old_chain = make_cleanup (cleanup_init, 0); | |
777 | ||
778 | /* First init tcl and tk. */ | |
779 | ||
780 | interp = Tcl_CreateInterp (); | |
781 | ||
782 | if (!interp) | |
783 | error ("Tcl_CreateInterp failed"); | |
784 | ||
785 | mainWindow = Tk_CreateMainWindow (interp, NULL, "gdb", "Gdb"); | |
786 | ||
787 | if (!mainWindow) | |
788 | return; /* DISPLAY probably not set */ | |
789 | ||
790 | if (Tcl_Init(interp) != TCL_OK) | |
791 | error ("Tcl_Init failed: %s", interp->result); | |
792 | ||
793 | if (Tk_Init(interp) != TCL_OK) | |
794 | error ("Tk_Init failed: %s", interp->result); | |
795 | ||
86db943c SG |
796 | Tcl_CreateCommand (interp, "gdb_cmd", call_wrapper, gdb_cmd, NULL); |
797 | Tcl_CreateCommand (interp, "gdb_loc", call_wrapper, gdb_loc, NULL); | |
798 | Tcl_CreateCommand (interp, "gdb_sourcelines", call_wrapper, gdb_sourcelines, | |
799 | NULL); | |
800 | Tcl_CreateCommand (interp, "gdb_listfiles", call_wrapper, gdb_listfiles, | |
746d1df4 | 801 | NULL); |
86db943c SG |
802 | Tcl_CreateCommand (interp, "gdb_stop", call_wrapper, gdb_stop, NULL); |
803 | Tcl_CreateCommand (interp, "gdb_regnames", call_wrapper, gdb_regnames, NULL); | |
804 | Tcl_CreateCommand (interp, "gdb_fetch_registers", call_wrapper, | |
805 | gdb_fetch_registers, NULL); | |
806 | Tcl_CreateCommand (interp, "gdb_changed_register_list", call_wrapper, | |
807 | gdb_changed_register_list, NULL); | |
754e5da2 SG |
808 | |
809 | gdbtk_filename = getenv ("GDBTK_FILENAME"); | |
8532893d SG |
810 | if (!gdbtk_filename) |
811 | if (access ("gdbtk.tcl", R_OK) == 0) | |
812 | gdbtk_filename = "gdbtk.tcl"; | |
813 | else | |
814 | gdbtk_filename = GDBTK_FILENAME; | |
815 | ||
816 | if (Tcl_EvalFile (interp, gdbtk_filename) != TCL_OK) | |
817 | error ("Failure reading %s: %s", gdbtk_filename, interp->result); | |
754e5da2 | 818 | |
cd2df226 | 819 | /* Get the file descriptor for the X server */ |
479f0f18 | 820 | |
cd2df226 | 821 | x_fd = ConnectionNumber (Tk_Display (mainWindow)); |
479f0f18 SG |
822 | |
823 | /* Setup for I/O interrupts */ | |
824 | ||
825 | signal (SIGIO, SIG_IGN); | |
826 | ||
827 | i = fcntl (x_fd, F_GETFL, 0); | |
828 | fcntl (x_fd, F_SETFL, i|FASYNC); | |
829 | fcntl (x_fd, F_SETOWN, getpid()); | |
830 | ||
754e5da2 SG |
831 | command_loop_hook = Tk_MainLoop; |
832 | fputs_unfiltered_hook = gdbtk_fputs; | |
833 | print_frame_info_listing_hook = null_routine; | |
834 | query_hook = gdbtk_query; | |
835 | flush_hook = gdbtk_flush; | |
836 | create_breakpoint_hook = gdbtk_create_breakpoint; | |
837 | delete_breakpoint_hook = gdbtk_delete_breakpoint; | |
838 | enable_breakpoint_hook = gdbtk_enable_breakpoint; | |
839 | disable_breakpoint_hook = gdbtk_disable_breakpoint; | |
637b1661 | 840 | interactive_hook = gdbtk_interactive; |
479f0f18 SG |
841 | target_wait_hook = gdbtk_wait; |
842 | call_command_hook = gdbtk_call_command; | |
754e5da2 SG |
843 | |
844 | discard_cleanups (old_chain); | |
845 | ||
846 | add_com ("tk", class_obscure, tk_command, | |
847 | "Send a command directly into tk."); | |
848 | } | |
849 | ||
850 | /* Come here during initialze_all_files () */ | |
851 | ||
852 | void | |
853 | _initialize_gdbtk () | |
854 | { | |
c5197511 SG |
855 | if (use_windows) |
856 | { | |
857 | /* Tell the rest of the world that Gdbtk is now set up. */ | |
754e5da2 | 858 | |
c5197511 SG |
859 | init_ui_hook = gdbtk_init; |
860 | } | |
754e5da2 | 861 | } |