aboutsummaryrefslogtreecommitdiff
path: root/gdb/gdbtk-variable.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/gdbtk-variable.c')
-rw-r--r--gdb/gdbtk-variable.c1622
1 files changed, 1622 insertions, 0 deletions
diff --git a/gdb/gdbtk-variable.c b/gdb/gdbtk-variable.c
new file mode 100644
index 0000000..9ed6a10
--- /dev/null
+++ b/gdb/gdbtk-variable.c
@@ -0,0 +1,1622 @@
+/* Variable user interface for GDB, the GNU debugger.
+ Copyright 1999 Free Software Foundation, Inc.
+
+This file is part of GDB.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "defs.h"
+#include "value.h"
+#include "expression.h"
+#include "frame.h"
+#include "valprint.h"
+
+#include <tcl.h>
+#include <tk.h>
+#include "gdbtk.h"
+#include "gdbtk-wrapper.h"
+
+#include <math.h>
+
+/* Enumeration type defining the return values for valueChanged */
+enum value_changed
+{
+ VALUE_UNCHANGED, /* the variable's value is unchanged */
+ VALUE_CHANGED, /* the variable's value has changed */
+ VALUE_OUT_OF_SCOPE /* the variable is no longer in scope */
+};
+
+/* String representations of the value_changed enums */
+static char *value_changed_string[] = {
+ "VARIABLE_UNCHANGED",
+ "VARIABLE_CHANGED",
+ "VARIABLE_OUT_OF_SCOPE",
+ NULL
+};
+
+/* Enumeration for the format types */
+enum display_format
+{
+ FORMAT_NATURAL, /* What gdb actually calls 'natural' */
+ FORMAT_BINARY, /* Binary display */
+ FORMAT_DECIMAL, /* Decimal display */
+ FORMAT_HEXADECIMAL, /* Hex display */
+ FORMAT_OCTAL /* Octal display */
+};
+
+/* Mappings of display_format enums to gdb's format codes */
+int format_code[] = {0, 't', 'd', 'x', 'o'};
+
+/* String representations of the format codes */
+char *format_string[] = {"natural", "binary", "decimal", "hexadecimal", "octal"};
+
+/* Every parent variable keeps a linked list of its children, described
+ by the following structure. */
+struct variable_child {
+
+ /* Pointer to the child's data */
+ struct _gdb_variable *child;
+
+ /* Pointer to the next child */
+ struct variable_child *next;
+};
+
+/* Every variable in the system has a structure of this type defined
+ for it. This structure holds all information necessary to manipulate
+ a particular object variable. Members which must be freed are noted. */
+struct _gdb_variable {
+
+ /* Alloc'd name of the variable for this object.. If this variable is a
+ child, then this name will be the child's source name.
+ (bar, not foo.bar) */
+ char *name;
+
+ /* The alloc'd real name of this variable. This is used to construct the
+ variable's children. It is always a valid expression. */
+ char *real_name;
+
+ /* The alloc'd name for this variable's object. This is here for
+ convenience when constructing this object's children. */
+ char *obj_name;
+
+ /* Alloc'd expression for this variable */
+ struct expression *exp;
+
+ /* Block for which this expression is valid */
+ struct block *valid_block;
+
+ /* The frame for this expression */
+ CORE_ADDR frame;
+
+ /* The value of this expression */
+ value_ptr value;
+
+ /* Did an error occur evaluating the expression or getting its value? */
+ int error;
+
+ /* The number of (immediate) children this variable has */
+ int num_children;
+
+ /* If this object is a child, this points to its parent. */
+ struct _gdb_variable *parent;
+
+ /* A list of this object's children */
+ struct variable_child *children;
+
+ /* The format of the output for this object */
+ enum display_format format;
+};
+
+typedef struct _gdb_variable gdb_variable;
+
+/* This variable will hold the value of the output from gdb
+ for commands executed through call_gdb_* */
+static Tcl_Obj *fputs_obj;
+
+/*
+ * Public functions defined in this file
+ */
+
+int gdb_variable_init PARAMS ((Tcl_Interp *));
+
+/*
+ * Private functions defined in this file
+ */
+
+/* Entries into this file */
+
+static int gdb_variable_command PARAMS ((ClientData, Tcl_Interp *, int,
+ Tcl_Obj *CONST[]));
+
+static int variable_create PARAMS ((Tcl_Interp *, int, Tcl_Obj *CONST[]));
+
+static void variable_delete PARAMS ((Tcl_Interp *, gdb_variable *));
+
+static void variable_debug PARAMS ((gdb_variable *));
+
+static int variable_obj_command PARAMS ((ClientData, Tcl_Interp *, int,
+ Tcl_Obj *CONST[]));
+static Tcl_Obj *variable_children PARAMS ((Tcl_Interp *, gdb_variable *));
+
+static enum value_changed variable_value_changed PARAMS ((gdb_variable *));
+
+static int variable_format PARAMS ((Tcl_Interp *, int, Tcl_Obj *CONST[],
+ gdb_variable *));
+
+static int variable_type PARAMS ((Tcl_Interp *, int, Tcl_Obj *CONST[],
+ gdb_variable *));
+
+static int variable_value PARAMS ((Tcl_Interp *, int, Tcl_Obj *CONST[],
+ gdb_variable *));
+
+static int variable_editable PARAMS ((gdb_variable *));
+
+/* Helper functions for the above functions. */
+
+static gdb_variable *create_variable PARAMS ((char *, char *, CORE_ADDR));
+
+static void delete_children PARAMS ((Tcl_Interp *, gdb_variable *, int));
+
+static void install_variable PARAMS ((Tcl_Interp *, char *, gdb_variable *));
+
+static void uninstall_variable PARAMS ((Tcl_Interp *, gdb_variable *));
+
+static gdb_variable *child_exists PARAMS ((gdb_variable *, char *));
+
+static gdb_variable *create_child PARAMS ((Tcl_Interp *, gdb_variable *,
+ char *, int));
+static char *name_of_child PARAMS ((gdb_variable *, int));
+
+static int number_of_children PARAMS ((gdb_variable *));
+
+static enum display_format variable_default_display PARAMS ((gdb_variable *));
+
+static void save_child_in_parent PARAMS ((gdb_variable *, gdb_variable *));
+
+static void remove_child_from_parent PARAMS ((gdb_variable *, gdb_variable *));
+
+static struct type *get_type PARAMS ((value_ptr));
+
+static struct type *get_target_type PARAMS ((struct type *));
+
+static Tcl_Obj *get_call_output PARAMS ((void));
+
+static void clear_gdb_output PARAMS ((void));
+
+static int call_gdb_type_print PARAMS ((value_ptr));
+
+static int call_gdb_val_print PARAMS ((value_ptr, int));
+
+static void variable_fputs PARAMS ((const char *, FILE *));
+
+static void null_fputs PARAMS ((const char *, FILE *));
+
+static int my_value_equal PARAMS ((gdb_variable *, value_ptr));
+
+#define INIT_VARIABLE(x) { \
+(x)->name = NULL; \
+(x)->real_name = NULL; \
+(x)->obj_name = NULL; \
+(x)->exp = NULL; \
+(x)->valid_block = NULL; \
+(x)->frame = (CORE_ADDR) 0; \
+(x)->value = NULL; \
+(x)->error = 0; \
+(x)->num_children = 0; \
+(x)->parent = NULL; \
+(x)->children = NULL; \
+(x)->format = FORMAT_NATURAL; \
+}
+
+#if defined(FREEIF)
+# undef FREEIF
+#endif
+#define FREEIF(x) if (x != NULL) free((char *) (x))
+
+/* Initialize the variable code. This function should be called once
+ to install and initialize the variable code into the interpreter. */
+int
+gdb_variable_init (interp)
+ Tcl_Interp *interp;
+{
+ Tcl_Command result;
+
+ result = Tcl_CreateObjCommand (interp, "gdb_variable", call_wrapper,
+ (ClientData) gdb_variable_command, NULL);
+ if (result == NULL)
+ return TCL_ERROR;
+
+ return TCL_OK;
+}
+
+/* This function defines the "gdb_variable" command which is used to
+ create variable objects. Its syntax includes:
+
+ gdb_variable create
+ gdb_variable create NAME
+ gdb_variable create -expr EXPR
+ gdb_variable create NAME -expr EXPR
+
+ NAME = name of object to create. If no NAME, then automatically create
+ a name
+ EXPR = the gdb expression for which to create a variable. This will
+ be the most common usage.
+*/
+static int
+gdb_variable_command (clientData, interp, objc, objv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ static char *commands[] = { "create", NULL };
+ enum commands_enum { VARIABLE_CREATE };
+ int index, result;
+
+ if (objc < 2)
+ {
+ Tcl_WrongNumArgs (interp, 1, objv, "option ?arg...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj (interp, objv[1], commands, "options", 0,
+ &index) != TCL_OK)
+ {
+ return TCL_ERROR;
+ }
+
+ switch ((enum commands_enum) index)
+ {
+ case VARIABLE_CREATE:
+ result = variable_create (interp, objc - 2, objv + 2);
+ break;
+
+ default:
+ return TCL_ERROR;
+ }
+
+ return result;
+}
+
+/* This function implements the actual object command for each
+ variable object that is created (and each of its children).
+
+ Currently the following commands are implemented:
+ - delete delete this object and its children
+ - valueChanged has the value of this object changed since the last check?
+ - numChildren how many children does this object have
+ - children create the children and return a list of their objects
+ - debug print out a little debug info for the object
+ - name print out the name of this variable
+*/
+static int
+variable_obj_command (clientData, interp, objc, objv)
+ ClientData clientData;
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ enum commands_enum {
+ VARIABLE_DELETE,
+ VARIABLE_VALUE_CHANGED,
+ VARIABLE_NUM_CHILDREN,
+ VARIABLE_CHILDREN,
+ VARIABLE_DEBUG,
+ VARIABLE_FORMAT,
+ VARIABLE_TYPE,
+ VARIABLE_VALUE,
+ VARIABLE_NAME,
+ VARIABLE_EDITABLE
+ };
+ static char *commands[] = {
+ "delete",
+ "valueChanged",
+ "numChildren",
+ "children",
+ "debug",
+ "format",
+ "type",
+ "value",
+ "name",
+ "editable",
+ NULL
+ };
+ gdb_variable *var = (gdb_variable *) clientData;
+ int index, result;
+
+ if (objc < 2)
+ {
+ Tcl_WrongNumArgs (interp, 1, objv, "option ?arg...?");
+ return TCL_ERROR;
+ }
+
+ if (Tcl_GetIndexFromObj (interp, objv[1], commands, "options", 0,
+ &index) != TCL_OK)
+ return TCL_ERROR;
+
+ result = TCL_OK;
+ switch ((enum commands_enum) index)
+ {
+ case VARIABLE_DELETE:
+ if (objc > 2)
+ {
+ int len;
+ char *s = Tcl_GetStringFromObj (objv[2], &len);
+ if (*s == 'c' && strncmp (s, "children", len) == 0)
+ {
+ delete_children (interp, var, 1);
+ break;
+ }
+ }
+ variable_delete (interp, var);
+ break;
+
+ case VARIABLE_VALUE_CHANGED:
+ {
+ enum value_changed vc = variable_value_changed (var);
+ Tcl_SetObjResult (interp, Tcl_NewStringObj (value_changed_string[vc], -1));
+ }
+ break;
+
+ case VARIABLE_NUM_CHILDREN:
+ Tcl_SetObjResult (interp, Tcl_NewIntObj (var->num_children));
+ break;
+
+ case VARIABLE_CHILDREN:
+ {
+ Tcl_Obj *children = variable_children (interp, var);
+ Tcl_SetObjResult (interp, children);
+ }
+ break;
+
+ case VARIABLE_DEBUG:
+ variable_debug (var);
+ break;
+
+ case VARIABLE_FORMAT:
+ result = variable_format (interp, objc, objv, var);
+ break;
+
+ case VARIABLE_TYPE:
+ result = variable_type (interp, objc, objv, var);
+ break;
+
+ case VARIABLE_VALUE:
+ result = variable_value (interp, objc, objv, var);
+ break;
+
+ case VARIABLE_NAME:
+ Tcl_SetObjResult (interp, Tcl_NewStringObj (var->name, -1));
+ break;
+
+ case VARIABLE_EDITABLE:
+ Tcl_SetObjResult (interp, Tcl_NewIntObj (variable_editable (var)));
+ break;
+
+ default:
+ return TCL_ERROR;
+ }
+
+ return result;
+}
+
+/*
+ * Variable object construction/destruction
+ */
+
+/* This function is responsible for processing the user's specifications
+ and constructing a variable object. */
+static int
+variable_create (interp, objc, objv)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+{
+ enum create_opts { CREATE_EXPR, CREATE_PC };
+ static char *create_options[] = { "-expr", "-pc", NULL };
+ gdb_variable *var;
+ char *name;
+ char obj_name[31];
+ int index;
+ static int id = 0;
+ CORE_ADDR pc = (CORE_ADDR) -1;
+
+ /* REMINDER: This command may be invoked in the following ways:
+ gdb_variable create
+ gdb_variable create NAME
+ gdb_variable create -expr EXPR
+ gdb_variable create NAME -expr EXPR
+
+ NAME = name of object to create. If no NAME, then automatically create
+ a name
+ EXPR = the gdb expression for which to create a variable. This will
+ be the most common usage.
+ */
+ name = NULL;
+ if (objc)
+ name = Tcl_GetStringFromObj (objv[0], NULL);
+ if (name == NULL || *name == '-')
+ {
+ /* generate a name for this object */
+ id++;
+ sprintf (obj_name, "var%d", id);
+ }
+ else
+ {
+ /* specified name for object */
+ strncpy (obj_name, name, 30);
+ objv++;
+ objc--;
+ }
+
+ /* Run through all the possible options for this command */
+ name = NULL;
+ while (objc > 0)
+ {
+ if (Tcl_GetIndexFromObj (interp, objv[0], create_options, "options",
+ 0, &index) != TCL_OK)
+ {
+ result_ptr->flags |= GDBTK_IN_TCL_RESULT;
+ return TCL_ERROR;
+ }
+
+ switch ((enum create_opts) index)
+ {
+ case CREATE_EXPR:
+ name = Tcl_GetStringFromObj (objv[1], NULL);
+ objc--;
+ objv++;
+ break;
+
+ case CREATE_PC:
+ {
+ char *str;
+ str = Tcl_GetStringFromObj (objv[1], NULL);
+ pc = parse_and_eval_address (str);
+ objc--;
+ objv++;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ objc--;
+ objv++;
+ }
+
+ /* Create the variable */
+ var = create_variable (name, name, pc);
+
+ if (var != NULL)
+ {
+ /* Install a command into the interpreter that represents this
+ object */
+ install_variable (interp, obj_name, var);
+ Tcl_SetObjResult (interp, Tcl_NewStringObj (obj_name, -1));
+ result_ptr->flags |= GDBTK_IN_TCL_RESULT;
+
+ return TCL_OK;
+ }
+
+ return TCL_ERROR;
+}
+
+/* Fill out a gdb_variable structure for the variable being constructed.
+ This function should never fail if real_name is a valid expression.
+ (That means no longjmp'ing!) */
+static gdb_variable *
+create_variable (name, real_name, pc)
+ char *name;
+ char *real_name;
+ CORE_ADDR pc;
+{
+ gdb_variable *var;
+ value_ptr mark;
+ struct frame_info *fi, *old_fi;
+ struct block *block;
+ void (*old_fputs) PARAMS ((const char *, FILE *));
+ gdb_result r;
+
+ var = (gdb_variable *) xmalloc (sizeof (gdb_variable));
+ INIT_VARIABLE (var);
+
+ if (name != NULL)
+ {
+ char *p;
+
+ /* Parse and evaluate the expression, filling in as much
+ of the variable's data as possible */
+
+ /* Allow creator to specify context of variable */
+ if (pc == (CORE_ADDR) -1)
+ block = 0;
+ else
+ {
+ r = GDB_block_for_pc (pc, &block);
+ if (r != GDB_OK)
+ block = 0;
+ }
+
+ p = real_name;
+ innermost_block = NULL;
+ r = GDB_parse_exp_1 (&p, block, 0, &(var->exp));
+ if (r != GDB_OK)
+ {
+ FREEIF ((char *) var);
+ return NULL;
+ }
+
+ /* Don't allow variables to be created for types. */
+ if (var->exp->elts[0].opcode == OP_TYPE)
+ {
+ free_current_contents ((char **) &(var->exp));
+ FREEIF (var);
+ printf_unfiltered ("Attempt to use a type name as an expression.");
+ return NULL;
+ }
+
+ var->valid_block = innermost_block;
+ var->name = savestring (name, strlen (name));
+ var->real_name = savestring (real_name, strlen (real_name));
+
+ /* Several of the GDB_* calls can cause messages to be displayed. We swallow
+ those here, because we don't need them (the "value" command will
+ show them). */
+ old_fputs = fputs_unfiltered_hook;
+ fputs_unfiltered_hook = null_fputs;
+
+ /* When the PC is different from the current PC (pc == -1),
+ then we must select the appropriate frame before parsing
+ the expression, otherwise the value will not be current.
+ Since select_frame is so benign, just call it for all cases. */
+ r = GDB_block_innermost_frame (var->valid_block, &fi);
+ if (r != GDB_OK)
+ fi = NULL;
+ if (fi)
+ var->frame = FRAME_FP (fi);
+ old_fi = selected_frame;
+ GDB_select_frame (fi, -1);
+
+ mark = value_mark ();
+ if (GDB_evaluate_expression (var->exp, &var->value) == GDB_OK)
+ {
+ release_value (var->value);
+ if (VALUE_LAZY (var->value))
+ {
+ if (GDB_value_fetch_lazy (var->value) != GDB_OK)
+ var->error = 1;
+ else
+ var->error = 0;
+ }
+ }
+ else
+ var->error = 1;
+ value_free_to_mark (mark);
+
+ /* Reset the selected frame */
+ GDB_select_frame (old_fi, -1);
+
+ /* Restore the output hook to normal */
+ fputs_unfiltered_hook = old_fputs;
+
+ var->num_children = number_of_children (var);
+ var->format = variable_default_display (var);
+ }
+
+ return var;
+}
+
+/* Install the given variable VAR into the tcl interpreter with
+ the object name NAME. */
+static void
+install_variable (interp, name, var)
+ Tcl_Interp *interp;
+ char *name;
+ gdb_variable *var;
+{
+ var->obj_name = savestring (name, strlen (name));
+ Tcl_CreateObjCommand (interp, name, variable_obj_command,
+ (ClientData) var, NULL);
+}
+
+/* Unistall the object VAR in the tcl interpreter. */
+static void
+uninstall_variable (interp, var)
+ Tcl_Interp *interp;
+ gdb_variable *var;
+{
+ Tcl_DeleteCommand (interp, var->obj_name);
+}
+
+/* Delete the variable object VAR and its children */
+static void
+variable_delete (interp, var)
+ Tcl_Interp *interp;
+ gdb_variable *var;
+{
+ /* Delete any children of this variable, too. */
+ delete_children (interp, var, 0);
+
+ /* If this variable has a parent, remove it from its parent's list */
+ if (var->parent != NULL)
+ {
+ remove_child_from_parent (var->parent, var);
+ }
+
+ uninstall_variable (interp, var);
+
+ /* Free memory associated with this variable */
+ FREEIF (var->name);
+ FREEIF (var->real_name);
+ FREEIF (var->obj_name);
+ if (var->exp != NULL)
+ free_current_contents ((char **) &var->exp);
+ FREEIF (var);
+}
+
+/* Silly debugging info */
+static void
+variable_debug (var)
+ gdb_variable *var;
+{
+ Tcl_Obj *str;
+
+ str = Tcl_NewStringObj ("name=", -1);
+ Tcl_AppendStringsToObj (str, var->name, "\nreal_name=", var->real_name,
+ "\nobj_name=", var->obj_name, NULL);
+ Tcl_SetObjResult (gdbtk_interp, str);
+}
+
+/*
+ * Child construction/destruction
+ */
+
+/* Delete the children associated with the object VAR. If NOTIFY is set,
+ notify the parent object that this child was deleted. This is used as
+ a small optimization when deleting variables and their children. If the
+ parent is also being deleted, don't bother notifying it that its children
+ are being deleted. */
+static void
+delete_children (interp, var, notify)
+ Tcl_Interp *interp;
+ gdb_variable *var;
+ int notify;
+{
+ struct variable_child *vc;
+ struct variable_child *next;
+
+ for (vc = var->children; vc != NULL; vc = next)
+ {
+ if (!notify)
+ vc->child->parent = NULL;
+ variable_delete (interp, vc->child);
+ next = vc->next;
+ free (vc);
+ }
+}
+
+/* Return the number of children for a given variable.
+
+ This can get a little complicated, since we would like to make
+ certain assumptions about certain types of variables.
+
+ - struct/union *: dereference first
+ - (*)(): do not allow derefencing
+ - arrays:
+ - declared size = num of children or
+ - -1 if we don't know, i.e., int foo [];
+ - if there was an error reported constructing this object,
+ assume it has no children (and try this again later)
+ - void * and char * have no children
+*/
+static int
+number_of_children (var)
+ gdb_variable *var;
+{
+ struct type *type;
+ struct type *target;
+ int children;
+
+ if (var->value != NULL)
+ {
+ type = get_type (var->value);
+ target = get_target_type (type);
+ children = 0;
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ if (TYPE_LENGTH (type) > 0 && TYPE_LENGTH (target) > 0
+ && TYPE_ARRAY_UPPER_BOUND_TYPE (type) != BOUND_CANNOT_BE_DETERMINED)
+ children = TYPE_LENGTH (type) / TYPE_LENGTH (target);
+ else
+ children = -1;
+ break;
+
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ /* If we have a virtual table pointer, omit it. */
+ if (TYPE_VPTR_BASETYPE (type) == type
+ && !(TYPE_VPTR_FIELDNO (type) < 0))
+ children = TYPE_NFIELDS (type) - 1;
+ else
+ children = TYPE_NFIELDS (type);
+ break;
+
+ case TYPE_CODE_PTR:
+ /* This is where things get compilcated. All pointers have one child.
+ Except, of course, for struct and union ptr, which we automagically
+ dereference for the user and function ptrs, which have no children. */
+ switch (TYPE_CODE (target))
+ {
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ /* If we have a virtual table pointer, omit it. */
+ if (TYPE_VPTR_BASETYPE (target) == target
+ && !(TYPE_VPTR_FIELDNO (target) < 0))
+ children = TYPE_NFIELDS (target) - 1;
+ else
+ children = TYPE_NFIELDS (target);
+ break;
+
+ case TYPE_CODE_FUNC:
+ children = 0;
+ break;
+
+ default:
+ /* Don't dereference char* or void*. */
+ if (TYPE_NAME (target) != NULL
+ && (STREQ (TYPE_NAME (target), "char")
+ || STREQ (TYPE_NAME (target), "void")))
+ children = 0;
+ else
+ children = 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ else
+ {
+ /* var->value can be null if we tried to access non-existent or
+ protected memory. In this case, we simply do not allow any
+ children. This will be checked again when we check if its
+ value has changed. */
+ children = 0;
+ }
+
+ return children;
+}
+
+/* Return a list of all the children of VAR, creating them if necessary. */
+static Tcl_Obj *
+variable_children (interp, var)
+ Tcl_Interp *interp;
+ gdb_variable *var;
+{
+ Tcl_Obj *list;
+ gdb_variable *child;
+ char *name;
+ int i;
+
+ list = Tcl_NewListObj (0, NULL);
+ for (i = 0; i < var->num_children; i++)
+ {
+ /* check if child exists */
+ name = name_of_child (var, i);
+ child = child_exists (var, name);
+ if (child == NULL)
+ {
+ child = create_child (interp, var, name, i);
+
+ /* name_of_child returns a malloc'd string */
+ free (name);
+ }
+ Tcl_ListObjAppendElement (NULL, list, Tcl_NewStringObj (child->obj_name, -1));
+ }
+
+ return list;
+}
+
+/* Does a child with the name NAME exist in VAR? If so, return its data.
+ If not, return NULL. */
+static gdb_variable *
+child_exists (var, name)
+ gdb_variable *var; /* Parent */
+ char *name; /* name of child */
+{
+ struct variable_child *vc;
+
+ for (vc = var->children; vc != NULL; vc = vc->next)
+ {
+ if (STREQ (vc->child->name, name))
+ return vc->child;
+ }
+
+ return NULL;
+}
+
+/* Create and install a child of the parent of the given name */
+static gdb_variable *
+create_child (interp, parent, name, index)
+ Tcl_Interp *interp;
+ gdb_variable *parent;
+ char *name;
+ int index;
+{
+ struct type *type;
+ struct type *target;
+ gdb_variable *child;
+ char separator[10], prefix[2048], suffix[20];
+ char *childs_name;
+ char *save_name;
+ int deref = 0;
+ int len;
+
+ /* name should never be null. For pointer derefs, it should contain "*name".
+ For arrays of a known size, the name will simply contain the index into
+ the array. */
+
+ separator[0] = '\0';
+ prefix[0] = '\0';
+ suffix[0] = '\0';;
+ save_name = name;
+
+ /* This code must contain a lot of the logic for children based on the parent's
+ type. */
+ type = get_type (parent->value);
+ target = get_target_type (type);
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ sprintf (suffix, "[%s]", name);
+ name = "";
+ break;
+
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ if (index < TYPE_N_BASECLASSES (type))
+ {
+ strcpy (prefix, "((");
+ strcat (prefix, name);
+ strcat (prefix, ")");
+ strcpy (suffix, ") ");
+ name = "";
+ }
+ else
+ strcpy (separator, ".");
+ break;
+
+ case TYPE_CODE_PTR:
+ switch (TYPE_CODE (target))
+ {
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ if (index < TYPE_N_BASECLASSES (target))
+ {
+ strcpy (prefix, "(*(");
+ strcat (prefix, name);
+ strcat (prefix, " *)");
+ strcpy (suffix, ")");
+ name = "";
+ }
+ else
+ strcpy (separator, "->");
+ break;
+
+ default:
+ deref = 1;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ /* When we get here, we should know how to construct a legal
+ expression for the child's name */
+ len = strlen (prefix);
+ len += strlen (parent->real_name);
+ len += strlen (separator);
+ len += strlen (name);
+ len += strlen (suffix);
+ if (deref)
+ len += 3;
+ childs_name = (char *) xmalloc ((len + 1) * sizeof (char));
+ if (deref)
+ {
+ strcpy (childs_name, "(*");
+ strcat (childs_name, parent->real_name);
+ strcat (childs_name, suffix);
+ strcat (childs_name, ")");
+ }
+ else
+ {
+ strcpy (childs_name, prefix);
+ strcat (childs_name, parent->real_name);
+ strcat (childs_name, separator);
+ strcat (childs_name, name);
+ strcat (childs_name, suffix);
+ }
+
+ /* childs_name now contains a valid expression for the child */
+ child = create_variable (save_name, childs_name, (CORE_ADDR) -1);
+ child->parent = parent;
+ free (childs_name);
+ childs_name = (char *) xmalloc ((strlen (parent->obj_name) + strlen (save_name) + 2)
+ * sizeof (char));
+ sprintf (childs_name, "%s.%s", parent->obj_name, save_name);
+ install_variable (interp, childs_name, child);
+ free (childs_name);
+
+ /* Save a pointer to this child in the parent */
+ save_child_in_parent (parent, child);
+
+ return child;
+}
+
+/* Save CHILD in the PARENT's data. */
+static void
+save_child_in_parent (parent, child)
+ gdb_variable *parent;
+ gdb_variable *child;
+{
+ struct variable_child *vc;
+
+ /* Insert the child at the top */
+ vc = parent->children;
+ parent->children =
+ (struct variable_child *) xmalloc (sizeof (struct variable_child));
+
+ parent->children->next = vc;
+ parent->children->child = child;
+}
+
+/* Remove the CHILD from the PARENT's list of children. */
+static void
+remove_child_from_parent (parent, child)
+ gdb_variable *parent;
+ gdb_variable *child;
+{
+ struct variable_child *vc, *prev;
+
+ /* Find the child in the parent's list */
+ prev = NULL;
+ for (vc = parent->children; vc != NULL; )
+ {
+ if (vc->child == child)
+ break;
+ prev = vc;
+ vc = vc->next;
+ }
+
+ if (prev == NULL)
+ parent->children = vc->next;
+ else
+ prev->next = vc->next;
+
+}
+
+/* What is the name of the INDEX'th child of VAR? */
+static char *
+name_of_child (var, index)
+ gdb_variable *var;
+ int index;
+{
+ struct type *type;
+ struct type *target;
+ char *name;
+ char *string;
+
+ type = get_type (var->value);
+ target = get_target_type (type);
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_ARRAY:
+ {
+ /* We never get here unless var->num_children is greater than 0... */
+ int len = 1;
+ while ((int) pow ((double) 10, (double) len) < index)
+ len++;
+ name = (char *) xmalloc (1 + len * sizeof (char));
+ sprintf (name, "%d", index);
+ }
+ break;
+
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ string = TYPE_FIELD_NAME (type, index);
+ name = savestring (string, strlen (string));
+ break;
+
+ case TYPE_CODE_PTR:
+ switch (TYPE_CODE (target))
+ {
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ string = TYPE_FIELD_NAME (target, index);
+ name = savestring (string, strlen (string));
+ break;
+
+ default:
+ name = (char *) xmalloc ((strlen (var->name) + 2) * sizeof (char));
+ sprintf (name, "*%s", var->name);
+ break;
+ }
+ }
+
+ return name;
+}
+
+/* Has the value of this object changed since the last time we looked?
+
+ There are some special cases:
+ - structs/unions/arrays. The "value" of these never changes.
+ Only their children's values change.
+ - if an error occurred with evaluate_expression or fetch_value_lazy,
+ then we need to be a little more elaborate with our determination
+ of "value changed". Specifically, the value does not change when
+ both the previous evaluate fails and the one done here also fails.
+*/
+static enum value_changed
+variable_value_changed (var)
+ gdb_variable *var;
+{
+ value_ptr mark, new_val;
+ struct frame_info *fi, *old_fi;
+ int within_scope;
+ enum value_changed result;
+ gdb_result r;
+
+ /* Save the selected stack frame, since we will need to change it
+ in order to evaluate expressions. */
+ old_fi = selected_frame;
+
+ /* Determine whether the variable is still around. */
+ if (var->valid_block == NULL)
+ within_scope = 1;
+ else
+ {
+ GDB_reinit_frame_cache ();
+ r = GDB_find_frame_addr_in_frame_chain (var->frame, &fi);
+ if (r != GDB_OK)
+ fi = NULL;
+ within_scope = fi != NULL;
+ /* FIXME: GDB_select_frame could fail */
+ if (within_scope)
+ GDB_select_frame (fi, -1);
+ }
+
+ result = VALUE_OUT_OF_SCOPE;
+ if (within_scope)
+ {
+ struct type *type = get_type (var->value);
+
+ /* Arrays, struct, classes, unions never change value */
+ if (type != NULL && (TYPE_CODE (type) == TYPE_CODE_STRUCT
+ || TYPE_CODE (type) == TYPE_CODE_UNION
+ || TYPE_CODE (type) == TYPE_CODE_ARRAY))
+ result = VALUE_UNCHANGED;
+ else
+ {
+ mark = value_mark ();
+ if (GDB_evaluate_expression (var->exp, &new_val) == GDB_OK)
+ {
+ if (!my_value_equal (var, new_val))
+ {
+ /* value changed */
+ release_value (new_val);
+ if (var->value == NULL)
+ {
+ /* This can happen if there was an error
+ evaluating the expression (like deref NULL) */
+ var->num_children = number_of_children (var);
+ }
+ value_free (var->value);
+ var->value = new_val;
+ result = VALUE_CHANGED;
+ }
+ else
+ result = VALUE_UNCHANGED;
+ }
+ else
+ {
+ /* evaluate expression failed. If we failed before, then
+ the value of this variable has not changed. If we
+ succeed before, then the value did change. */
+ if (var->value == NULL)
+ result = VALUE_UNCHANGED;
+ else
+ {
+ var->value = NULL;
+ var->error = 1;
+ result = VALUE_CHANGED;
+ }
+ }
+
+ value_free_to_mark (mark);
+ }
+ }
+
+ /* Restore selected frame */
+ GDB_select_frame (old_fi, -1);
+
+ return result;
+}
+
+static int
+variable_format (interp, objc, objv, var)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+ gdb_variable *var;
+{
+
+ if (objc > 2)
+ {
+ /* Set the format of VAR to given format */
+ int len;
+ char *fmt = Tcl_GetStringFromObj (objv[2], &len);
+ if (STREQN (fmt, "natural", len))
+ var->format = FORMAT_NATURAL;
+ else if (STREQN (fmt, "binary", len))
+ var->format = FORMAT_NATURAL;
+ else if (STREQN (fmt, "decimal", len))
+ var->format = FORMAT_DECIMAL;
+ else if (STREQN (fmt, "hexadecimal", len))
+ var->format = FORMAT_HEXADECIMAL;
+ else if (STREQN (fmt, "octal", len))
+ var->format = FORMAT_OCTAL;
+ else
+ {
+ Tcl_Obj *obj = Tcl_NewStringObj (NULL, 0);
+ Tcl_AppendStringsToObj (obj, "unknown display format \"",
+ fmt, "\": must be: \"natural\", \"binary\""
+ ", \"decimal\", \"hexadecimal\", or \"octal\"",
+ NULL);
+ Tcl_SetObjResult (interp, obj);
+ return TCL_ERROR;
+ }
+ }
+ else
+ {
+ /* Report the current format */
+ Tcl_Obj *fmt;
+
+ fmt = Tcl_NewStringObj (format_string [(int) var->format], -1);
+ Tcl_SetObjResult (interp, fmt);
+ }
+
+ return TCL_OK;
+}
+
+/* What is the default display for this variable? We assume that
+ everything is "natural". Any exceptions? */
+static enum display_format
+variable_default_display (var)
+ gdb_variable *var;
+{
+ return FORMAT_NATURAL;
+}
+
+/* This function returns the type of a variable in the interpreter (or an error)
+ and returns either TCL_OK or TCL_ERROR as appropriate. */
+static int
+variable_type (interp, objc, objv, var)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+ gdb_variable *var;
+{
+ int result;
+ value_ptr val;
+ char *first, *last, *string;
+ Tcl_RegExp regexp;
+ gdb_result r;
+
+ if (var->value != NULL)
+ val = var->value;
+ else
+ {
+ r = GDB_evaluate_type (var->exp, &val);
+ if (r != GDB_OK)
+ return TCL_ERROR;
+ }
+
+ result = call_gdb_type_print (val);
+ if (result == TCL_OK)
+ {
+ string = strdup (Tcl_GetStringFromObj (get_call_output (), NULL));
+ first = string;
+
+ /* gdb will print things out like "struct {...}" for anonymous structs.
+ In gui-land, we don't want the {...}, so we strip it here. */
+ regexp = Tcl_RegExpCompile (interp, "{...}");
+ if (Tcl_RegExpExec (interp, regexp, string, first))
+ {
+ /* We have an anonymous struct/union/class/enum */
+ Tcl_RegExpRange (regexp, 0, &first, &last);
+ if (*(first - 1) == ' ')
+ first--;
+ *first = '\0';
+ }
+
+ Tcl_SetObjResult (interp, Tcl_NewStringObj (string, -1));
+ FREEIF (string);
+ return TCL_OK;
+ }
+
+ Tcl_SetObjResult (interp, get_call_output ());
+ return result;
+}
+
+/* This function returns the value of a variable in the interpreter (or an error)
+ and returns either TCL_OK or TCL_ERROR as appropriate. */
+static int
+variable_value (interp, objc, objv, var)
+ Tcl_Interp *interp;
+ int objc;
+ Tcl_Obj *CONST objv[];
+ gdb_variable *var;
+{
+ int result;
+ struct type *type;
+ value_ptr val;
+ Tcl_Obj *str;
+ gdb_result r;
+ int real_addressprint;
+
+ /* If we set the value of the variable, objv[2] will contain the
+ variable's new value. We need to first construct a legal expression
+ for this -- ugh! */
+ if (objc > 2)
+ {
+ /* Does this cover all the bases? */
+ struct expression *exp;
+ value_ptr value;
+ int saved_input_radix = input_radix;
+
+ if (VALUE_LVAL (var->value) != not_lval && var->value->modifiable)
+ {
+ char *s;
+
+ input_radix = 10; /* ALWAYS reset to decimal temporarily */
+ s = Tcl_GetStringFromObj (objv[2], NULL);
+ r = GDB_parse_exp_1 (&s, 0, 0, &exp);
+ if (r != GDB_OK)
+ return TCL_ERROR;
+ if (GDB_evaluate_expression (exp, &value) != GDB_OK)
+ return TCL_ERROR;
+
+ val = value_assign (var->value, value);
+ value_free (var->value);
+ release_value (val);
+ var->value = val;
+ input_radix = saved_input_radix;
+ }
+
+ return TCL_OK;
+ }
+
+ if (var->value != NULL)
+ val = var->value;
+ else
+ {
+ /* This can happen if we attempt to get the value of a struct
+ member when the parent is an invalid pointer.
+
+ GDB reports the error as the error derived from accessing the
+ parent, but we don't have access to that here... */
+ Tcl_SetObjResult (interp, Tcl_NewStringObj ("???", -1));
+ return TCL_ERROR;
+ }
+
+ /* C++: addressprint causes val_print to print the
+ address of the reference, too. So clear it to get
+ the real value -- BUT ONLY FOR C++ REFERENCE TYPES! */
+ real_addressprint = addressprint;
+
+ /* BOGUS: if val_print sees a struct/class, it will print out its
+ children instead of "{...}" */
+ type = get_type (val);
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ str = Tcl_NewStringObj ("{...}", -1);
+ break;
+
+ case TYPE_CODE_ARRAY:
+ {
+ char number[256];
+ str = Tcl_NewStringObj (NULL, 0);
+ sprintf (number, "%d", var->num_children);
+ Tcl_AppendStringsToObj (str, "[", number, "]", NULL);
+ }
+ break;
+
+ case TYPE_CODE_REF:
+ /* Clear addressprint so that the actual value is printed */
+ addressprint = 0;
+
+ /* fall through */
+ default:
+ result = call_gdb_val_print (val, format_code[(int) var->format]);
+ Tcl_SetObjResult (interp, get_call_output ());
+
+ /* Restore addressprint */
+ addressprint = real_addressprint;
+ return result;
+ }
+
+ /* We only get here if we encountered one of the "special types" above */
+
+ /* Restore addressprint */
+ addressprint = real_addressprint;
+
+ Tcl_SetObjResult (interp, str);
+ return TCL_OK;
+}
+
+/* Is this variable editable? Use the variable's type to make
+ this determination. */
+static int
+variable_editable (var)
+ gdb_variable *var;
+{
+ struct type *type;
+ int result;
+ gdb_result r;
+
+ type = get_type (var->value);
+ if (type == NULL)
+ {
+ value_ptr val;
+ r = GDB_evaluate_type (var->exp, &val);
+ if (r != GDB_OK)
+ return 0;
+ type = get_type (val);
+ }
+
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_STRUCT:
+ case TYPE_CODE_UNION:
+ case TYPE_CODE_ARRAY:
+ case TYPE_CODE_FUNC:
+ case TYPE_CODE_MEMBER:
+ case TYPE_CODE_METHOD:
+ result = 0;
+ break;
+
+ default:
+ result = 1;
+ break;
+ }
+
+ return result;
+}
+
+/*
+ * Call stuff. These functions are used to capture the output of gdb commands
+ * without going through the tcl interpreter.
+ */
+
+/* Retrieve gdb output in the buffer since last call. */
+static Tcl_Obj *
+get_call_output ()
+{
+ /* Clear the error flags, in case we errored. */
+ if (result_ptr != NULL)
+ result_ptr->flags &= ~GDBTK_ERROR_ONLY;
+ return fputs_obj;
+}
+
+/* Clear the output of the buffer. */
+static void
+clear_gdb_output ()
+{
+ if (fputs_obj != NULL)
+ Tcl_DecrRefCount (fputs_obj);
+
+ fputs_obj = Tcl_NewStringObj (NULL, -1);
+ Tcl_IncrRefCount (fputs_obj);
+}
+
+/* Call the gdb command "type_print", retaining its output in the buffer. */
+static int
+call_gdb_type_print (val)
+ value_ptr val;
+{
+ void (*old_hook) PARAMS ((const char *, FILE *));
+ int result;
+
+ /* Save the old hook and install new hook */
+ old_hook = fputs_unfiltered_hook;
+ fputs_unfiltered_hook = variable_fputs;
+
+ /* Call our command with our args */
+ clear_gdb_output ();
+
+
+ if (GDB_type_print (val, "", gdb_stdout, -1) == GDB_OK)
+ result = TCL_OK;
+ else
+ result = TCL_ERROR;
+
+ /* Restore fputs hook */
+ fputs_unfiltered_hook = old_hook;
+
+ return result;
+}
+
+/* Call the gdb command "val_print", retaining its output in the buffer. */
+static int
+call_gdb_val_print (val, format)
+ value_ptr val;
+ int format;
+{
+ void (*old_hook) PARAMS ((const char *, FILE *));
+ gdb_result r;
+ int result;
+
+ /* Save the old hook and install new hook */
+ old_hook = fputs_unfiltered_hook;
+ fputs_unfiltered_hook = variable_fputs;
+
+ /* Call our command with our args */
+ clear_gdb_output ();
+
+ if (VALUE_LAZY (val))
+ {
+ r = GDB_value_fetch_lazy (val);
+ if (r != GDB_OK)
+ {
+ fputs_unfiltered_hook = old_hook;
+ return TCL_ERROR;
+ }
+ }
+ r = GDB_val_print (VALUE_TYPE (val), VALUE_CONTENTS_RAW (val), VALUE_ADDRESS (val),
+ gdb_stdout, format, 1, 0, 0);
+ if (r == GDB_OK)
+ result = TCL_OK;
+ else
+ result = TCL_ERROR;
+
+ /* Restore fputs hook */
+ fputs_unfiltered_hook = old_hook;
+
+ return result;
+}
+
+/* The fputs_unfiltered_hook function used to save the output from one of the
+ call commands in this file. */
+static void
+variable_fputs (text, stream)
+ const char *text;
+ FILE *stream;
+{
+ /* Just append everything to the fputs_obj... Issues with stderr/stdout? */
+ Tcl_AppendToObj (fputs_obj, (char *) text, -1);
+}
+
+/* Empty handler for the fputs_unfiltered_hook. Set the hook to this function
+ whenever the output is irrelevent. */
+static void
+null_fputs (text, stream)
+ const char *text;
+ FILE *stream;
+{
+ return;
+}
+
+/*
+ * Special wrapper-like stuff to supplement the generic wrappers
+ */
+
+/* This returns the type of the variable. This skips past typedefs
+ and returns the real type of the variable. */
+static struct type *
+get_type (val)
+ value_ptr val;
+{
+ struct type *type = NULL;
+
+ if (val != NULL)
+ {
+ type = VALUE_TYPE (val);
+ while (type != NULL && TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
+ type = TYPE_TARGET_TYPE (type);
+ }
+
+ return type;
+}
+
+/* This returns the target type (or NULL) of TYPE, also skipping
+ past typedefs, just like get_type (). */
+static struct type *
+get_target_type (type)
+ struct type *type;
+{
+ if (type != NULL)
+ {
+ type = TYPE_TARGET_TYPE (type);
+ while (type != NULL && TYPE_CODE (type) == TYPE_CODE_TYPEDEF)
+ type = TYPE_TARGET_TYPE (type);
+ }
+
+ return type;
+}
+
+/* This function is a special wrap. This call never "fails".*/
+static int
+my_value_equal (var, val2)
+ gdb_variable *var;
+ value_ptr val2;
+{
+ int err1, err2, r;
+
+ /* This is bogus, but unfortunately necessary. We must know
+ exactly what caused an error -- reading var->val (which we
+ get from var->error and/or val2, so that we can really determine
+ if we think that something has changed. */
+ err1 = var->error;
+ err2 = 0;
+ if (VALUE_LAZY (val2) && GDB_value_fetch_lazy (val2) != GDB_OK)
+ err2 = 1;
+
+ /* Another special case: NULL values. If both are null, say
+ they're equal. */
+ if (var->value == NULL && val2 == NULL)
+ return 1;
+ else if (var->value == NULL || val2 == NULL)
+ return 0;
+
+ if (GDB_value_equal (var->value, val2, &r) != GDB_OK)
+ {
+ /* An error occurred, this could have happened if
+ either val1 or val2 errored. ERR1 and ERR2 tell
+ us which of these it is. If both errored, then
+ we assume nothing has changed. If one of them is
+ valid, though, then something has changed. */
+ if (err1 == err2)
+ {
+ /* both the old and new values caused errors, so
+ we say the value did not change */
+ /* This is indeterminate, though. Perhaps we should
+ be safe and say, yes, it changed anyway?? */
+ return 1;
+ }
+ else
+ {
+ /* err2 replaces var->error since this new value
+ WILL replace the old one. */
+ var->error = err2;
+ return 0;
+ }
+ }
+
+ return r;
+}
+
+/* Local variables: */
+/* change-log-default-name: "ChangeLog-gdbtk" */
+/* End: */