/* Low level packing and unpacking of values for GDB, the GNU Debugger.
- Copyright 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994,
- 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005 Free
- Software Foundation, Inc.
+ Copyright (C) 1986, 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995,
+ 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004, 2005, 2006, 2007
+ Free Software Foundation, Inc.
This file is part of GDB.
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. */
-
-/* Hack so that value.h can detect when it's being included by
- value.c. */
-#define VALUE_C
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
#include "defs.h"
#include "gdb_string.h"
#include "gdbcmd.h"
#include "target.h"
#include "language.h"
-#include "scm-lang.h"
#include "demangle.h"
#include "doublest.h"
#include "gdb_assert.h"
void _initialize_values (void);
+struct value
+{
+ /* Type of value; either not an lval, or one of the various
+ different possible kinds of lval. */
+ enum lval_type lval;
+
+ /* Is it modifiable? Only relevant if lval != not_lval. */
+ int modifiable;
+
+ /* Location of value (if lval). */
+ union
+ {
+ /* If lval == lval_memory, this is the address in the inferior.
+ If lval == lval_register, this is the byte offset into the
+ registers structure. */
+ CORE_ADDR address;
+
+ /* Pointer to internal variable. */
+ struct internalvar *internalvar;
+ } location;
+
+ /* Describes offset of a value within lval of a structure in bytes.
+ If lval == lval_memory, this is an offset to the address. If
+ lval == lval_register, this is a further offset from
+ location.address within the registers structure. Note also the
+ member embedded_offset below. */
+ int offset;
+
+ /* Only used for bitfields; number of bits contained in them. */
+ int bitsize;
+
+ /* Only used for bitfields; position of start of field. For
+ BITS_BIG_ENDIAN=0 targets, it is the position of the LSB. For
+ BITS_BIG_ENDIAN=1 targets, it is the position of the MSB. */
+ int bitpos;
+
+ /* Frame register value is relative to. This will be described in
+ the lval enum above as "lval_register". */
+ struct frame_id frame_id;
+
+ /* Type of the value. */
+ struct type *type;
+
+ /* If a value represents a C++ object, then the `type' field gives
+ the object's compile-time type. If the object actually belongs
+ to some class derived from `type', perhaps with other base
+ classes and additional members, then `type' is just a subobject
+ of the real thing, and the full object is probably larger than
+ `type' would suggest.
+
+ If `type' is a dynamic class (i.e. one with a vtable), then GDB
+ can actually determine the object's run-time type by looking at
+ the run-time type information in the vtable. When this
+ information is available, we may elect to read in the entire
+ object, for several reasons:
+
+ - When printing the value, the user would probably rather see the
+ full object, not just the limited portion apparent from the
+ compile-time type.
+
+ - If `type' has virtual base classes, then even printing `type'
+ alone may require reaching outside the `type' portion of the
+ object to wherever the virtual base class has been stored.
+
+ When we store the entire object, `enclosing_type' is the run-time
+ type -- the complete object -- and `embedded_offset' is the
+ offset of `type' within that larger type, in bytes. The
+ value_contents() macro takes `embedded_offset' into account, so
+ most GDB code continues to see the `type' portion of the value,
+ just as the inferior would.
+
+ If `type' is a pointer to an object, then `enclosing_type' is a
+ pointer to the object's run-time type, and `pointed_to_offset' is
+ the offset in bytes from the full object to the pointed-to object
+ -- that is, the value `embedded_offset' would have if we followed
+ the pointer and fetched the complete object. (I don't really see
+ the point. Why not just determine the run-time type when you
+ indirect, and avoid the special case? The contents don't matter
+ until you indirect anyway.)
+
+ If we're not doing anything fancy, `enclosing_type' is equal to
+ `type', and `embedded_offset' is zero, so everything works
+ normally. */
+ struct type *enclosing_type;
+ int embedded_offset;
+ int pointed_to_offset;
+
+ /* Values are stored in a chain, so that they can be deleted easily
+ over calls to the inferior. Values assigned to internal
+ variables or put into the value history are taken off this
+ list. */
+ struct value *next;
+
+ /* Register number if the value is from a register. */
+ short regnum;
+
+ /* If zero, contents of this value are in the contents field. If
+ nonzero, contents are in inferior memory at address in the
+ location.address field plus the offset field (and the lval field
+ should be lval_memory).
+
+ WARNING: This field is used by the code which handles watchpoints
+ (see breakpoint.c) to decide whether a particular value can be
+ watched by hardware watchpoints. If the lazy flag is set for
+ some member of a value chain, it is assumed that this member of
+ the chain doesn't need to be watched as part of watching the
+ value itself. This is how GDB avoids watching the entire struct
+ or array when the user wants to watch a single struct member or
+ array element. If you ever change the way lazy flag is set and
+ reset, be sure to consider this use as well! */
+ char lazy;
+
+ /* If nonzero, this is the value of a variable which does not
+ actually exist in the program. */
+ char optimized_out;
+
+ /* If value is a variable, is it initialized or not. */
+ int initialized;
+
+ /* Actual contents of the value. For use of this value; setting it
+ uses the stuff above. Not valid if lazy is nonzero. Target
+ byte-order. We force it to be aligned properly for any possible
+ value. Note that a value therefore extends beyond what is
+ declared here. */
+ union
+ {
+ gdb_byte contents[1];
+ DOUBLEST force_doublest_align;
+ LONGEST force_longest_align;
+ CORE_ADDR force_core_addr_align;
+ void *force_pointer_align;
+ } aligner;
+ /* Do not add any new members here -- contents above will trash
+ them. */
+};
+
/* Prototypes for local functions. */
static void show_values (char *, int);
val->embedded_offset = 0;
val->pointed_to_offset = 0;
val->modifiable = 1;
+ val->initialized = 1; /* Default to initialized. */
return val;
}
/* Accessor methods. */
+struct value *
+value_next (struct value *value)
+{
+ return value->next;
+}
+
struct type *
value_type (struct value *value)
{
{
return value->offset;
}
+void
+set_value_offset (struct value *value, int offset)
+{
+ value->offset = offset;
+}
int
value_bitpos (struct value *value)
{
return value->bitpos;
}
+void
+set_value_bitpos (struct value *value, int bit)
+{
+ value->bitpos = bit;
+}
int
value_bitsize (struct value *value)
{
return value->bitsize;
}
+void
+set_value_bitsize (struct value *value, int bit)
+{
+ value->bitsize = bit;
+}
-bfd_byte *
+gdb_byte *
value_contents_raw (struct value *value)
{
return value->aligner.contents + value->embedded_offset;
}
-bfd_byte *
+gdb_byte *
value_contents_all_raw (struct value *value)
{
return value->aligner.contents;
return value->enclosing_type;
}
-const bfd_byte *
+const gdb_byte *
value_contents_all (struct value *value)
{
if (value->lazy)
value->lazy = val;
}
-const bfd_byte *
+const gdb_byte *
value_contents (struct value *value)
{
return value_contents_writeable (value);
}
-bfd_byte *
+gdb_byte *
value_contents_writeable (struct value *value)
{
if (value->lazy)
value_fetch_lazy (value);
- return value->aligner.contents;
+ return value_contents_raw (value);
+}
+
+/* Return non-zero if VAL1 and VAL2 have the same contents. Note that
+ this function is different from value_equal; in C the operator ==
+ can return 0 even if the two values being compared are equal. */
+
+int
+value_contents_equal (struct value *val1, struct value *val2)
+{
+ struct type *type1;
+ struct type *type2;
+ int len;
+
+ type1 = check_typedef (value_type (val1));
+ type2 = check_typedef (value_type (val2));
+ len = TYPE_LENGTH (type1);
+ if (len != TYPE_LENGTH (type2))
+ return 0;
+
+ return (memcmp (value_contents (val1), value_contents (val2), len) == 0);
}
int
{
return &value->regnum;
}
+
+int
+deprecated_value_modifiable (struct value *value)
+{
+ return value->modifiable;
+}
+void
+deprecated_set_value_modifiable (struct value *value, int modifiable)
+{
+ value->modifiable = modifiable;
+}
\f
/* Return a mark in the value chain. All values allocated after the
mark is obtained (except for those released) are subject to being freed
struct value *val = allocate_value (encl_type);
val->type = arg->type;
VALUE_LVAL (val) = VALUE_LVAL (arg);
- VALUE_ADDRESS (val) = VALUE_ADDRESS (arg);
+ val->location = arg->location;
val->offset = arg->offset;
val->bitpos = arg->bitpos;
val->bitsize = arg->bitsize;
if (absnum <= 0)
{
if (num == 0)
- error ("The history is empty.");
+ error (_("The history is empty."));
else if (num == 1)
- error ("There is only one value in the history.");
+ error (_("There is only one value in the history."));
else
- error ("History does not go back to $$%d.", -num);
+ error (_("History does not go back to $$%d."), -num);
}
if (absnum > value_history_count)
- error ("History has not yet reached $%d.", absnum);
+ error (_("History has not yet reached $%d."), absnum);
absnum--;
return value_copy (chunk->values[absnum % VALUE_HISTORY_CHUNK]);
}
-/* Clear the value history entirely.
- Must be done when new symbol tables are loaded,
- because the type pointers become invalid. */
-
-void
-clear_value_history (void)
-{
- struct value_history_chunk *next;
- int i;
- struct value *val;
-
- while (value_history_chain)
- {
- for (i = 0; i < VALUE_HISTORY_CHUNK; i++)
- if ((val = value_history_chain->values[i]) != NULL)
- xfree (val);
- next = value_history_chain->next;
- xfree (value_history_chain);
- value_history_chain = next;
- }
- value_history_count = 0;
-}
-
static void
show_values (char *num_exp, int from_tty)
{
for (i = num; i < num + 10 && i <= value_history_count; i++)
{
val = access_value_history (i);
- printf_filtered ("$%d = ", i);
+ printf_filtered (("$%d = "), i);
value_print (val, gdb_stdout, 0, Val_pretty_default);
- printf_filtered ("\n");
+ printf_filtered (("\n"));
}
/* The next "info history +" should start after what we just printed. */
static struct internalvar *internalvars;
+/* If the variable does not already exist create it and give it the value given.
+ If no value is given then the default is zero. */
+static void
+init_if_undefined_command (char* args, int from_tty)
+{
+ struct internalvar* intvar;
+
+ /* Parse the expression - this is taken from set_command(). */
+ struct expression *expr = parse_expression (args);
+ register struct cleanup *old_chain =
+ make_cleanup (free_current_contents, &expr);
+
+ /* Validate the expression.
+ Was the expression an assignment?
+ Or even an expression at all? */
+ if (expr->nelts == 0 || expr->elts[0].opcode != BINOP_ASSIGN)
+ error (_("Init-if-undefined requires an assignment expression."));
+
+ /* Extract the variable from the parsed expression.
+ In the case of an assign the lvalue will be in elts[1] and elts[2]. */
+ if (expr->elts[1].opcode != OP_INTERNALVAR)
+ error (_("The first parameter to init-if-undefined should be a GDB variable."));
+ intvar = expr->elts[2].internalvar;
+
+ /* Only evaluate the expression if the lvalue is void.
+ This may still fail if the expresssion is invalid. */
+ if (TYPE_CODE (value_type (intvar->value)) == TYPE_CODE_VOID)
+ evaluate_expression (expr);
+
+ do_cleanups (old_chain);
+}
+
+
/* Look up an internal variable with name NAME. NAME should not
normally include a dollar sign.
return var;
var = (struct internalvar *) xmalloc (sizeof (struct internalvar));
- var->name = concat (name, NULL);
+ var->name = concat (name, (char *)NULL);
var->value = allocate_value (builtin_type_void);
+ var->endian = gdbarch_byte_order (current_gdbarch);
release_value (var->value);
var->next = internalvars;
internalvars = var;
value_of_internalvar (struct internalvar *var)
{
struct value *val;
+ int i, j;
+ gdb_byte temp;
val = value_copy (var->value);
if (value_lazy (val))
value_fetch_lazy (val);
VALUE_LVAL (val) = lval_internalvar;
VALUE_INTERNALVAR (val) = var;
+
+ /* Values are always stored in the target's byte order. When connected to a
+ target this will most likely always be correct, so there's normally no
+ need to worry about it.
+
+ However, internal variables can be set up before the target endian is
+ known and so may become out of date. Fix it up before anybody sees.
+
+ Internal variables usually hold simple scalar values, and we can
+ correct those. More complex values (e.g. structures and floating
+ point types) are left alone, because they would be too complicated
+ to correct. */
+
+ if (var->endian != gdbarch_byte_order (current_gdbarch))
+ {
+ gdb_byte *array = value_contents_raw (val);
+ struct type *type = check_typedef (value_enclosing_type (val));
+ switch (TYPE_CODE (type))
+ {
+ case TYPE_CODE_INT:
+ case TYPE_CODE_PTR:
+ /* Reverse the bytes. */
+ for (i = 0, j = TYPE_LENGTH (type) - 1; i < j; i++, j--)
+ {
+ temp = array[j];
+ array[j] = array[i];
+ array[i] = temp;
+ }
+ break;
+ }
+ }
+
return val;
}
set_internalvar_component (struct internalvar *var, int offset, int bitpos,
int bitsize, struct value *newval)
{
- bfd_byte *addr = value_contents_writeable (var->value) + offset;
+ gdb_byte *addr = value_contents_writeable (var->value) + offset;
if (bitsize)
modify_field (addr, value_as_long (newval),
long. */
xfree (var->value);
var->value = newval;
+ var->endian = gdbarch_byte_order (current_gdbarch);
release_value (newval);
/* End code which must not call error(). */
}
return var->name;
}
-/* Free all internalvars. Done when new symtabs are loaded,
- because that makes the values invalid. */
+/* Update VALUE before discarding OBJFILE. COPIED_TYPES is used to
+ prevent cycles / duplicates. */
+
+static void
+preserve_one_value (struct value *value, struct objfile *objfile,
+ htab_t copied_types)
+{
+ if (TYPE_OBJFILE (value->type) == objfile)
+ value->type = copy_type_recursive (objfile, value->type, copied_types);
+
+ if (TYPE_OBJFILE (value->enclosing_type) == objfile)
+ value->enclosing_type = copy_type_recursive (objfile,
+ value->enclosing_type,
+ copied_types);
+}
+
+/* Update the internal variables and value history when OBJFILE is
+ discarded; we must copy the types out of the objfile. New global types
+ will be created for every convenience variable which currently points to
+ this objfile's types, and the convenience variables will be adjusted to
+ use the new global types. */
void
-clear_internalvars (void)
+preserve_values (struct objfile *objfile)
{
+ htab_t copied_types;
+ struct value_history_chunk *cur;
struct internalvar *var;
+ int i;
- while (internalvars)
- {
- var = internalvars;
- internalvars = var->next;
- xfree (var->name);
- xfree (var->value);
- xfree (var);
- }
+ /* Create the hash table. We allocate on the objfile's obstack, since
+ it is soon to be deleted. */
+ copied_types = create_copied_types_hash (objfile);
+
+ for (cur = value_history_chain; cur; cur = cur->next)
+ for (i = 0; i < VALUE_HISTORY_CHUNK; i++)
+ if (cur->values[i])
+ preserve_one_value (cur->values[i], objfile, copied_types);
+
+ for (var = internalvars; var; var = var->next)
+ preserve_one_value (var->value, objfile, copied_types);
+
+ htab_delete (copied_types);
}
static void
{
varseen = 1;
}
- printf_filtered ("$%s = ", var->name);
- value_print (var->value, gdb_stdout, 0, Val_pretty_default);
- printf_filtered ("\n");
+ printf_filtered (("$%s = "), var->name);
+ value_print (value_of_internalvar (var), gdb_stdout,
+ 0, Val_pretty_default);
+ printf_filtered (("\n"));
}
if (!varseen)
- printf_unfiltered ("No debugger convenience variables now defined.\n\
+ printf_unfiltered (_("\
+No debugger convenience variables now defined.\n\
Convenience variables have names starting with \"$\";\n\
-use \"set\" as in \"set $foo = 5\" to define them.\n");
+use \"set\" as in \"set $foo = 5\" to define them.\n"));
}
\f
/* Extract a value as a C number (either long or double).
foo = unpack_double (value_type (val), value_contents (val), &inv);
if (inv)
- error ("Invalid floating value found in program.");
+ error (_("Invalid floating value found in program."));
return foo;
}
/* Extract a value as a C pointer. Does not deallocate the value.
/* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
whether we want this to be true eventually. */
#if 0
- /* ADDR_BITS_REMOVE is wrong if we are being called for a
+ /* gdbarch_addr_bits_remove is wrong if we are being called for a
non-address (e.g. argument to "signal", "info break", etc.), or
for pointers to char, in which the low bits *are* significant. */
- return ADDR_BITS_REMOVE (value_as_long (val));
+ return gdbarch_addr_bits_remove (current_gdbarch, value_as_long (val));
#else
/* There are several targets (IA-64, PowerPC, and others) which
to an INT (or some size). After all, it is only an offset. */
LONGEST
-unpack_long (struct type *type, const char *valaddr)
+unpack_long (struct type *type, const gdb_byte *valaddr)
{
enum type_code code = TYPE_CODE (type);
int len = TYPE_LENGTH (type);
int nosign = TYPE_UNSIGNED (type);
- if (current_language->la_language == language_scm
- && is_scmvalue_type (type))
- return scm_unpack (type, valaddr, TYPE_CODE_INT);
-
switch (code)
{
case TYPE_CODE_TYPEDEF:
return unpack_long (check_typedef (type), valaddr);
case TYPE_CODE_ENUM:
+ case TYPE_CODE_FLAGS:
case TYPE_CODE_BOOL:
case TYPE_CODE_INT:
case TYPE_CODE_CHAR:
case TYPE_CODE_RANGE:
+ case TYPE_CODE_MEMBERPTR:
if (nosign)
return extract_unsigned_integer (valaddr, len);
else
whether we want this to be true eventually. */
return extract_typed_address (valaddr, type);
- case TYPE_CODE_MEMBER:
- error ("not implemented: member types in unpack_long");
-
default:
- error ("Value can't be converted to integer.");
+ error (_("Value can't be converted to integer."));
}
return 0; /* Placate lint. */
}
format, result is in host format. */
DOUBLEST
-unpack_double (struct type *type, const char *valaddr, int *invp)
+unpack_double (struct type *type, const gdb_byte *valaddr, int *invp)
{
enum type_code code;
int len;
only in a non-portable way. Fixing the portability problem
wouldn't help since the VAX floating-point code is also badly
bit-rotten. The target needs to add definitions for the
- methods TARGET_FLOAT_FORMAT and TARGET_DOUBLE_FORMAT - these
+ methods gdbarch_float_format and gdbarch_double_format - these
exactly describe the target floating-point format. The
problem here is that the corresponding floatformat_vax_f and
floatformat_vax_d values these methods should be set to are
to an INT (or some size). After all, it is only an offset. */
CORE_ADDR
-unpack_pointer (struct type *type, const char *valaddr)
+unpack_pointer (struct type *type, const gdb_byte *valaddr)
{
/* Assume a CORE_ADDR can fit in a LONGEST (for now). Not sure
whether we want this to be true eventually. */
/* SYM should never have a SYMBOL_CLASS which will require
read_var_value to use the FRAME parameter. */
if (symbol_read_needs_frame (sym))
- warning ("static field's value depends on the current "
- "frame - bad debug info?");
+ warning (_("static field's value depends on the current "
+ "frame - bad debug info?"));
retval = read_var_value (sym, NULL);
}
if (retval && VALUE_LVAL (retval) == lval_memory)
VALUE_LVAL (v) = VALUE_LVAL (arg1);
if (VALUE_LVAL (arg1) == lval_internalvar)
VALUE_LVAL (v) = lval_internalvar_component;
- VALUE_ADDRESS (v) = VALUE_ADDRESS (arg1);
+ v->location = arg1->location;
VALUE_REGNUM (v) = VALUE_REGNUM (arg1);
VALUE_FRAME_ID (v) = VALUE_FRAME_ID (arg1);
-/* VALUE_OFFSET (v) = VALUE_OFFSET (arg1) + offset
- + TYPE_FIELD_BITPOS (arg_type, fieldno) / 8; */
return v;
}
If the field is signed, we also do sign extension. */
LONGEST
-unpack_field_as_long (struct type *type, const char *valaddr, int fieldno)
+unpack_field_as_long (struct type *type, const gdb_byte *valaddr, int fieldno)
{
ULONGEST val;
ULONGEST valmask;
0 <= BITPOS, where lbits is the size of a LONGEST in bits. */
void
-modify_field (char *addr, LONGEST fieldval, int bitpos, int bitsize)
+modify_field (gdb_byte *addr, LONGEST fieldval, int bitpos, int bitsize)
{
ULONGEST oword;
ULONGEST mask = (ULONGEST) -1 >> (8 * sizeof (ULONGEST) - bitsize);
{
/* FIXME: would like to include fieldval in the message, but
we don't have a sprintf_longest. */
- warning ("Value does not fit in %d bits.", bitsize);
+ warning (_("Value does not fit in %d bits."), bitsize);
/* Truncate it, otherwise adjoining fields may be corrupted. */
fieldval &= mask;
store_unsigned_integer (addr, sizeof oword, oword);
}
\f
-/* Convert C numbers into newly allocated values */
+/* Pack NUM into BUF using a target format of TYPE. */
-struct value *
-value_from_longest (struct type *type, LONGEST num)
+void
+pack_long (gdb_byte *buf, struct type *type, LONGEST num)
{
- struct value *val = allocate_value (type);
- enum type_code code;
int len;
-retry:
- code = TYPE_CODE (type);
+
+ type = check_typedef (type);
len = TYPE_LENGTH (type);
- switch (code)
+ switch (TYPE_CODE (type))
{
- case TYPE_CODE_TYPEDEF:
- type = check_typedef (type);
- goto retry;
case TYPE_CODE_INT:
case TYPE_CODE_CHAR:
case TYPE_CODE_ENUM:
+ case TYPE_CODE_FLAGS:
case TYPE_CODE_BOOL:
case TYPE_CODE_RANGE:
- store_signed_integer (value_contents_raw (val), len, num);
+ case TYPE_CODE_MEMBERPTR:
+ store_signed_integer (buf, len, num);
break;
case TYPE_CODE_REF:
case TYPE_CODE_PTR:
- store_typed_address (value_contents_raw (val), type, (CORE_ADDR) num);
+ store_typed_address (buf, type, (CORE_ADDR) num);
break;
default:
- error ("Unexpected type (%d) encountered for integer constant.", code);
+ error (_("Unexpected type (%d) encountered for integer constant."),
+ TYPE_CODE (type));
}
+}
+
+
+/* Convert C numbers into newly allocated values. */
+
+struct value *
+value_from_longest (struct type *type, LONGEST num)
+{
+ struct value *val = allocate_value (type);
+
+ pack_long (value_contents_raw (val), type, num);
+
return val;
}
store_typed_floating (value_contents_raw (val), base_type, num);
}
else
- error ("Unexpected type encountered for floating constant.");
+ error (_("Unexpected type encountered for floating constant."));
return val;
}
\f
/* Should we use DEPRECATED_EXTRACT_STRUCT_VALUE_ADDRESS instead of
- EXTRACT_RETURN_VALUE? GCC_P is true if compiled with gcc and TYPE
+ gdbarch_extract_return_value? GCC_P is true if compiled with gcc and TYPE
is the type (which is known to be struct, union or array).
On most machines, the struct convention is used unless we are
enum type_code code = TYPE_CODE (value_type);
if (code == TYPE_CODE_ERROR)
- error ("Function return type unknown.");
+ error (_("Function return type unknown."));
if (code == TYPE_CODE_VOID)
/* A void return value is never in memory. See also corresponding
!= RETURN_VALUE_REGISTER_CONVENTION);
}
+/* Set the initialized field in a value struct. */
+
+void
+set_value_initialized (struct value *val, int status)
+{
+ val->initialized = status;
+}
+
+/* Return the initialized field in a value struct. */
+
+int
+value_initialized (struct value *val)
+{
+ return val->initialized;
+}
+
void
_initialize_values (void)
{
- add_cmd ("convenience", no_class, show_convenience,
- "Debugger convenience (\"$foo\") variables.\n\
+ add_cmd ("convenience", no_class, show_convenience, _("\
+Debugger convenience (\"$foo\") variables.\n\
These variables are created when you assign them values;\n\
-thus, \"print $foo=1\" gives \"$foo\" the value 1. Values may be any type.\n\n\
+thus, \"print $foo=1\" gives \"$foo\" the value 1. Values may be any type.\n\
+\n\
A few convenience variables are given values automatically:\n\
\"$_\"holds the last address examined with \"x\" or \"info lines\",\n\
-\"$__\" holds the contents of the last address examined with \"x\".",
+\"$__\" holds the contents of the last address examined with \"x\"."),
&showlist);
add_cmd ("values", no_class, show_values,
- "Elements of value history around item number IDX (or last ten).",
+ _("Elements of value history around item number IDX (or last ten)."),
&showlist);
+
+ add_com ("init-if-undefined", class_vars, init_if_undefined_command, _("\
+Initialize a convenience variable if necessary.\n\
+init-if-undefined VARIABLE = EXPRESSION\n\
+Set an internal VARIABLE to the result of the EXPRESSION if it does not\n\
+exist or does not contain a value. The EXPRESSION is not evaluated if the\n\
+VARIABLE is already initialized."));
}