/* Tcl/Tk interface routines.
- Copyright 1994, 1995, 1996 Free Software Foundation, Inc.
+ Copyright 1994, 1995, 1996, 1997 Free Software Foundation, Inc.
Written by Stu Grossman <grossman@cygnus.com> of Cygnus Support.
#include "target.h"
#include <tcl.h>
#include <tk.h>
+#include <itcl.h>
+#include <tix.h>
+
+#ifdef IDE
+#include "event.h"
+#include "idetcl.h"
+#endif
+
#ifdef ANSI_PROTOTYPES
#include <stdarg.h>
#else
#include <stdio.h>
#include "gdbcmd.h"
+#ifndef WINNT
#ifndef FIOASYNC
#include <sys/stropts.h>
#endif
+#endif
+
+#ifdef WINNT
+#define GDBTK_PATH_SEP ";"
+#else
+#define GDBTK_PATH_SEP ":"
+#endif
/* Some versions (1.3.79, 1.3.81) of Linux don't support SIOCSPGRP the way
gdbtk wants to use it... */
#undef SIOCSPGRP
#endif
+static void null_routine PARAMS ((int));
+static void gdbtk_flush PARAMS ((FILE *));
+static void gdbtk_fputs PARAMS ((const char *, FILE *));
+static int gdbtk_query PARAMS ((const char *, va_list));
+static char *gdbtk_readline PARAMS ((char *));
+static void gdbtk_init PARAMS ((char *));
+static void tk_command_loop PARAMS ((void));
+static void gdbtk_call_command PARAMS ((struct cmd_list_element *, char *, int));
+static int gdbtk_wait PARAMS ((int, struct target_waitstatus *));
+static void x_event PARAMS ((int));
+static void gdbtk_interactive PARAMS ((void));
+static void cleanup_init PARAMS ((int));
+static void tk_command PARAMS ((char *, int));
+static int gdb_disassemble PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int compare_lines PARAMS ((const PTR, const PTR));
+static int gdbtk_dis_asm_read_memory PARAMS ((bfd_vma, bfd_byte *, int, disassemble_info *));
+static int gdb_path_conv PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_stop PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_listfiles PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int call_wrapper PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_cmd PARAMS ((ClientData, Tcl_Interp *, int, char *argv[]));
+static int gdb_fetch_registers PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static void gdbtk_readline_end PARAMS ((void));
+static int gdb_changed_register_list PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static void register_changed_p PARAMS ((int, void *));
+static int gdb_get_breakpoint_list PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_get_breakpoint_info PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static void breakpoint_notify PARAMS ((struct breakpoint *, const char *));
+static void gdbtk_create_breakpoint PARAMS ((struct breakpoint *));
+static void gdbtk_delete_breakpoint PARAMS ((struct breakpoint *));
+static void gdbtk_modify_breakpoint PARAMS ((struct breakpoint *));
+static int gdb_loc PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_eval PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int gdb_sourcelines PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static int map_arg_registers PARAMS ((int, char *[], void (*) (int, void *), void *));
+static void get_register_name PARAMS ((int, void *));
+static int gdb_regnames PARAMS ((ClientData, Tcl_Interp *, int, char *[]));
+static void get_register PARAMS ((int, void *));
+
/* Handle for TCL interpreter */
static Tcl_Interp *interp = NULL;
-/* Handle for TK main window */
-
-static Tk_Window mainWindow = NULL;
-
static int x_fd; /* X network socket */
/* This variable is true when the inferior is running. Although it's
static int disassemble_from_exec = -1;
-/* Supply malloc calls for tcl/tk. */
+#ifndef _WIN32
+
+/* Supply malloc calls for tcl/tk. We do not want to do this on
+ Windows, because Tcl_Alloc is probably in a DLL which will not call
+ the mmalloc routines. */
char *
-Tcl_Malloc (size)
+Tcl_Alloc (size)
unsigned int size;
{
return xmalloc (size);
free (ptr);
}
+#endif /* _WIN32 */
+
static void
null_routine(arg)
int arg;
const char *ptr;
FILE *stream;
{
-
if (result_ptr)
Tcl_DStringAppend (result_ptr, (char *)ptr, -1);
else
Tcl_DStringAppend (&str, "gdbtk_tcl_fputs", -1);
Tcl_DStringAppendElement (&str, (char *)ptr);
- Tcl_Eval (interp, Tcl_DStringValue (&str));
+ Tcl_Eval (interp, Tcl_DStringValue (&str));
Tcl_DStringFree (&str);
}
}
static int
gdbtk_query (query, args)
- char *query;
+ const char *query;
va_list args;
{
char buf[200], *merge[2];
{
char *merge[2];
char *command;
+ int result;
merge[0] = "gdbtk_tcl_readline";
merge[1] = prompt;
command = Tcl_Merge (2, merge);
- if (Tcl_Eval (interp, command) == TCL_OK)
+ result = Tcl_Eval (interp, command);
+ free (command);
+ if (result == TCL_OK)
{
return (strdup (interp -> result));
}
Tcl_DStringAppendElement (dsp, buf);
}
+static int
+gdb_path_conv (clientData, interp, argc, argv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int argc;
+ char *argv[];
+{
+#ifdef WINNT
+ char pathname[256], *ptr;
+ if (argc != 2)
+ error ("wrong # args");
+ cygwin32_conv_to_full_win32_path (argv[1], pathname);
+ for (ptr = pathname; *ptr; ptr++)
+ {
+ if (*ptr == '\\')
+ *ptr = '/';
+ }
+#else
+ char *pathname = argv[1];
+#endif
+ Tcl_DStringAppend (result_ptr, pathname, strlen(pathname));
+ return TCL_OK;
+}
+
static int
gdb_get_breakpoint_list (clientData, interp, argc, argv)
ClientData clientData;
"longjmp", "longjmp resume", "step resume",
"through sigtramp", "watchpoint scope",
"call dummy" };
- static char *bpdisp[] = {"delete", "disable", "donttouch"};
+ static char *bpdisp[] = {"delete", "delstop", "disable", "donttouch"};
struct command_line *cmd;
int bpnum;
struct breakpoint *b;
{
char buf[100];
int v;
+ struct symtab_and_line sal;
if (b->type != bp_breakpoint)
return;
/* We ensure that ACTION contains no special Tcl characters, so we
can do this. */
- sprintf (buf, "gdbtk_tcl_breakpoint %s %d", action, b->number);
+ sal = find_pc_line (b->address, 0);
+ sprintf (buf, "gdbtk_tcl_breakpoint %s %d 0x%lx %d {%s}", action, b->number,
+ (long)b->address, sal.line, symtab_to_filename (sal.symtab));
v = Tcl_Eval (interp, buf);
dsprintf_append_element (result_ptr, "%d", sal.line); /* line number */
- dsprintf_append_element (result_ptr, "0x%lx", pc); /* PC */
+ dsprintf_append_element (result_ptr, "0x%s", paddr_nz(pc)); /* PC in current frame */
+
+ dsprintf_append_element (result_ptr, "0x%s", paddr_nz(stop_pc)); /* Real PC */
return TCL_OK;
}
argc--;
argv++;
- return map_arg_registers (argc, argv, get_register_name, 0);
+ return map_arg_registers (argc, argv, get_register_name, NULL);
}
#ifndef REGISTER_CONVERTIBLE
argc--;
format = **argv++;
- return map_arg_registers (argc, argv, get_register, format);
+ return map_arg_registers (argc, argv, get_register, (void *) format);
}
/* This contains the previous values of the registers, since the last call to
void *argp; /* Ignored */
{
char raw_buffer[MAX_REGISTER_RAW_SIZE];
- char buf[100];
if (read_relative_register_raw_bytes (regnum, raw_buffer))
return;
return TCL_OK;
}
+/* Client of call_wrapper - this routine performs the actual call to
+ the client function. */
+
+struct wrapped_call_args
+{
+ Tcl_Interp *interp;
+ Tcl_CmdProc *func;
+ int argc;
+ char **argv;
+ int val;
+};
+
+static int
+wrapped_call (args)
+ struct wrapped_call_args *args;
+{
+ args->val = (*args->func) (args->func, args->interp, args->argc, args->argv);
+ return 1;
+}
+
/* This routine acts as a top-level for all GDB code called by tcl/Tk. It
handles cleanups, and calls to return_to_top_level (usually via error).
This is necessary in order to prevent a longjmp out of the bowels of Tk,
int argc;
char *argv[];
{
- int val;
- struct cleanup *saved_cleanup_chain;
- Tcl_CmdProc *func;
- jmp_buf saved_error_return;
+ struct wrapped_call_args wrapped_args;
Tcl_DString result, *old_result_ptr;
Tcl_DStringInit (&result);
old_result_ptr = result_ptr;
result_ptr = &result;
- func = (Tcl_CmdProc *)clientData;
- memcpy (saved_error_return, error_return, sizeof (jmp_buf));
+ wrapped_args.func = (Tcl_CmdProc *)clientData;
+ wrapped_args.interp = interp;
+ wrapped_args.argc = argc;
+ wrapped_args.argv = argv;
+ wrapped_args.val = 0;
- saved_cleanup_chain = save_cleanups ();
-
- if (!setjmp (error_return))
- val = func (clientData, interp, argc, argv);
- else
+ if (!catch_errors (wrapped_call, &wrapped_args, "", RETURN_MASK_ALL))
{
- val = TCL_ERROR; /* Flag an error for TCL */
+ wrapped_args.val = TCL_ERROR; /* Flag an error for TCL */
gdb_flush (gdb_stderr); /* Flush error output */
mode because gdbtk_call_command may have bombed out while in
the command routine. */
+ running_now = 0;
Tcl_Eval (interp, "gdbtk_tcl_idle");
}
- do_cleanups (ALL_CLEANUPS);
-
- restore_cleanups (saved_cleanup_chain);
-
- memcpy (error_return, saved_error_return, sizeof (jmp_buf));
-
Tcl_DStringResult (interp, &result);
result_ptr = old_result_ptr;
- return val;
+ return wrapped_args.val;
}
static int
int argc;
char *argv[];
{
- target_stop ();
+ if (target_stop)
+ target_stop ();
+ else
+ quit_flag = 1; /* hope something sees this */
return TCL_OK;
}
{
INIT_DISASSEMBLE_INFO_NO_ARCH (di, gdb_stdout,
(fprintf_ftype) fprintf_unfiltered);
+ di.flavour = bfd_target_unknown_flavour;
di.memory_error_func = dis_asm_memory_error;
di.print_address_func = dis_asm_print_address;
di_initialized = 1;
di.mach = tm_print_insn_info.mach;
if (TARGET_BYTE_ORDER == BIG_ENDIAN)
- tm_print_insn_info.endian = BFD_ENDIAN_BIG;
+ di.endian = BFD_ENDIAN_BIG;
else
- tm_print_insn_info.endian = BFD_ENDIAN_LITTLE;
+ di.endian = BFD_ENDIAN_LITTLE;
if (argc != 3 && argc != 4)
error ("wrong # args");
cleanup_init (ignored)
int ignored;
{
- if (mainWindow != NULL)
- Tk_DestroyWindow (mainWindow);
- mainWindow = NULL;
-
if (interp != NULL)
Tcl_DeleteInterp (interp);
interp = NULL;
{
/* Process pending events */
- while (Tk_DoOneEvent (TK_DONT_WAIT|TK_ALL_EVENTS) != 0);
+ while (Tcl_DoOneEvent (TCL_DONT_WAIT|TCL_ALL_EVENTS) != 0);
}
static int
action.sa_handler = x_event;
action.sa_mask = nullsigmask;
action.sa_flags = SA_RESTART;
+#ifndef WINNT
sigaction(SIGIO, &action, NULL);
+#endif
pid = target_wait (pid, ourstatus);
action.sa_handler = SIG_IGN;
- sigaction(SIGIO, &action, NULL);
+#ifndef WINNT
+ sigaction(SIGIO, &action, NULL);
+#endif
return pid;
}
running_now = 1;
Tcl_Eval (interp, "gdbtk_tcl_busy");
(*cmdblk->function.cfunc)(arg, from_tty);
- Tcl_Eval (interp, "gdbtk_tcl_idle");
running_now = 0;
+ Tcl_Eval (interp, "gdbtk_tcl_idle");
}
else
(*cmdblk->function.cfunc)(arg, from_tty);
}
static void
-gdbtk_init ()
+gdbtk_init ( argv0 )
+ char *argv0;
{
struct cleanup *old_chain;
- char *gdbtk_filename;
- int i;
+ char *lib, *gdbtk_lib, gdbtk_lib_tmp[1024],gdbtk_file[128];
+ int i, found_main;
struct sigaction action;
static sigset_t nullsigmask = {0};
+#ifdef IDE
+ struct ide_event_handle *h;
+ const char *errmsg;
+ char *libexecdir;
+#endif
+
+ /* If there is no DISPLAY environment variable, Tk_Init below will fail,
+ causing gdb to abort. If instead we simply return here, gdb will
+ gracefully degrade to using the command line interface. */
+
+#ifndef WINNT
+ if (getenv ("DISPLAY") == NULL)
+ return;
+#endif
old_chain = make_cleanup (cleanup_init, 0);
/* First init tcl and tk. */
-
+ Tcl_FindExecutable (argv0);
interp = Tcl_CreateInterp ();
if (!interp)
error ("Tcl_CreateInterp failed");
- mainWindow = Tk_CreateMainWindow (interp, NULL, "gdb", "Gdb");
-
- if (!mainWindow)
- return; /* DISPLAY probably not set */
-
if (Tcl_Init(interp) != TCL_OK)
error ("Tcl_Init failed: %s", interp->result);
if (Tk_Init(interp) != TCL_OK)
error ("Tk_Init failed: %s", interp->result);
+ if (Itcl_Init(interp) == TCL_ERROR)
+ error ("Itcl_Init failed: %s", interp->result);
+
+ if (Tix_Init(interp) != TCL_OK)
+ error ("Tix_Init failed: %s", interp->result);
+
+#ifdef IDE
+ /* Initialize the Paths variable.
+ if (ide_initialize_paths (interp, "gdb") != TCL_OK)
+ return (TCL_ERROR);
+ */
+ /* Find the directory where we expect to find idemanager. We ignore
+ errors since it doesn't really matter if this fails. */
+ libexecdir = Tcl_GetVar2 (interp, "Paths", "libexecdir", TCL_GLOBAL_ONLY);
+
+ IluTk_Init ();
+
+ h = ide_event_init_client (&errmsg, libexecdir);
+ if (h == NULL)
+ {
+ Tcl_AppendResult (interp, "can't initialize event system: ", errmsg,
+ (char *) NULL);
+ fprintf(stderr, "WARNING: ide_event_init_client failed: %s\n", interp->result);
+ }
+ else
+ {
+ if (ide_create_tclevent_command (interp, h) != TCL_OK)
+ error ("ide_create_tclevent_command failed: %s", interp->result);
+ if (ide_create_edit_command (interp, h) != TCL_OK)
+ error ("ide_create_edit_command failed: %s", interp->result);
+
+ if (ide_create_property_command (interp, h) != TCL_OK)
+ error ("ide_create_property_command failed: %s", interp->result);
+
+ /*
+ if (ide_initialize (interp, "gdb") != TCL_OK)
+ error ("ide_initialize failed: %s", interp->result);
+ */
+ }
+ Tcl_SetVar (interp, "GDBTK_IDE", "1", 0);
+#else
+ Tcl_SetVar (interp, "GDBTK_IDE", "0", 0);
+#endif /* IDE */
+
Tcl_CreateCommand (interp, "gdb_cmd", call_wrapper, gdb_cmd, NULL);
Tcl_CreateCommand (interp, "gdb_loc", call_wrapper, gdb_loc, NULL);
+ Tcl_CreateCommand (interp, "gdb_path_conv", call_wrapper, gdb_path_conv, NULL);
Tcl_CreateCommand (interp, "gdb_sourcelines", call_wrapper, gdb_sourcelines,
NULL);
Tcl_CreateCommand (interp, "gdb_listfiles", call_wrapper, gdb_listfiles,
gdb_get_breakpoint_info, NULL);
command_loop_hook = tk_command_loop;
- print_frame_info_listing_hook = null_routine;
+ print_frame_info_listing_hook =
+ (void (*) PARAMS ((struct symtab *, int, int, int))) null_routine;
query_hook = gdbtk_query;
flush_hook = gdbtk_flush;
create_breakpoint_hook = gdbtk_create_breakpoint;
/* Get the file descriptor for the X server */
- x_fd = ConnectionNumber (Tk_Display (mainWindow));
+ x_fd = ConnectionNumber (Tk_Display (Tk_MainWindow (interp)));
/* Setup for I/O interrupts */
action.sa_mask = nullsigmask;
action.sa_flags = 0;
action.sa_handler = SIG_IGN;
+#ifndef WINNT
sigaction(SIGIO, &action, NULL);
+#endif
#ifdef FIOASYNC
i = 1;
#endif /* F_SETOWN */
#endif /* !SIOCSPGRP */
#else
+#ifndef WINNT
if (ioctl (x_fd, I_SETSIG, S_INPUT|S_RDNORM) < 0)
perror_with_name ("gdbtk_init: ioctl I_SETSIG failed");
+#endif
+
#endif /* ifndef FIOASYNC */
add_com ("tk", class_obscure, tk_command,
Tcl_LinkVar (interp, "disassemble-from-exec", (char *)&disassemble_from_exec,
TCL_LINK_INT);
- /* Load up gdbtk.tcl after all the environment stuff has been setup. */
+ /* find the gdb tcl library and source main.tcl */
- gdbtk_filename = getenv ("GDBTK_FILENAME");
- if (!gdbtk_filename)
- if (access ("gdbtk.tcl", R_OK) == 0)
- gdbtk_filename = "gdbtk.tcl";
+ gdbtk_lib = getenv ("GDBTK_LIBRARY");
+ if (!gdbtk_lib)
+ if (access ("gdbtcl/main.tcl", R_OK) == 0)
+ gdbtk_lib = "gdbtcl";
else
- gdbtk_filename = GDBTK_FILENAME;
+ gdbtk_lib = GDBTK_LIBRARY;
+
+ strcpy (gdbtk_lib_tmp, gdbtk_lib);
+ found_main = 0;
+ /* see if GDBTK_LIBRARY is a path list */
+ lib = strtok (gdbtk_lib_tmp, GDBTK_PATH_SEP);
+ do
+ {
+ if (Tcl_VarEval (interp, "lappend auto_path ", lib, NULL) != TCL_OK)
+ {
+ fputs_unfiltered (Tcl_GetVar (interp, "errorInfo", 0), gdb_stderr);
+ error ("");
+ }
+ if (!found_main)
+ {
+ strcpy (gdbtk_file, lib);
+ strcat (gdbtk_file, "/main.tcl");
+ if (access (gdbtk_file, R_OK) == 0)
+ {
+ found_main++;
+ Tcl_SetVar (interp, "GDBTK_LIBRARY", lib, 0);
+ }
+ }
+ }
+ while ((lib = strtok (NULL, ":")) != NULL);
+
+ if (!found_main)
+ {
+ fputs_unfiltered_hook = NULL; /* Force errors to stdout/stderr */
+ if (getenv("GDBTK_LIBRARY"))
+ {
+ fprintf_unfiltered (stderr, "Unable to find main.tcl in %s\n",getenv("GDBTK_LIBRARY"));
+ fprintf_unfiltered (stderr,
+ "Please set GDBTK_LIBRARY to a path that includes the GDB tcl files.\n");
+ }
+ else
+ {
+ fprintf_unfiltered (stderr, "Unable to find main.tcl in %s\n", GDBTK_LIBRARY);
+ fprintf_unfiltered (stderr, "You might want to set GDBTK_LIBRARY\n");
+ }
+ error("");
+ }
/* Defer setup of fputs_unfiltered_hook to near the end so that error messages
prior to this point go to stdout/stderr. */
fputs_unfiltered_hook = gdbtk_fputs;
- if (Tcl_EvalFile (interp, gdbtk_filename) != TCL_OK)
+ if (Tcl_EvalFile (interp, gdbtk_file) != TCL_OK)
{
fputs_unfiltered_hook = NULL; /* Force errors to stdout/stderr */
- fprintf_unfiltered (stderr, "%s:%d: %s\n", gdbtk_filename,
+ fprintf_unfiltered (stderr, "%s:%d: %s\n", gdbtk_file,
interp->errorLine, interp->result);
fputs_unfiltered ("Stack trace:\n", gdb_stderr);