8f87134c12
* gdbtk-variable.c (variable_create): Allocate enough space to hold the NULL, too!
1629 lines
43 KiB
C
1629 lines
43 KiB
C
/* 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 *, GDB_FILE *));
|
||
|
||
static void null_fputs PARAMS ((const char *, GDB_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 */
|
||
{
|
||
/* Add parentheses to the name so that casts do
|
||
not confuse it. */
|
||
char *newname = (char *) xmalloc (strlen (name) + 3);
|
||
sprintf (newname, "(%s)", name);
|
||
var = create_variable (name, newname, pc);
|
||
FREEIF (newname);
|
||
}
|
||
|
||
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 *, GDB_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 *, GDB_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 *, GDB_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;
|
||
GDB_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;
|
||
GDB_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: */
|