diff options
Diffstat (limited to 'gdb/printcmd.c')
-rw-r--r-- | gdb/printcmd.c | 1960 |
1 files changed, 1960 insertions, 0 deletions
diff --git a/gdb/printcmd.c b/gdb/printcmd.c new file mode 100644 index 0000000..b8bc298 --- /dev/null +++ b/gdb/printcmd.c @@ -0,0 +1,1960 @@ +/* Print values for GNU debugger GDB. + Copyright (C) 1986-1991 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB 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 1, or (at your option) +any later version. + +GDB 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 GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include <string.h> +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "symtab.h" +#include "value.h" +#include "expression.h" +#include "gdbcore.h" +#include "gdbcmd.h" +#include "target.h" + +extern int asm_demangle; /* Whether to demangle syms in asm printouts */ + +extern struct block *get_current_block (); + +static void print_frame_nameless_args (); + +struct format_data +{ + int count; + char format; + char size; +}; + +/* Last specified output format. */ + +static char last_format = 'x'; + +/* Last specified examination size. 'b', 'h', 'w' or `q'. */ + +static char last_size = 'w'; + +/* Default address to examine next. */ + +static CORE_ADDR next_address; + +/* Last address examined. */ + +static CORE_ADDR last_examine_address; + +/* Contents of last address examined. + This is not valid past the end of the `x' command! */ + +static value last_examine_value; + +/* Number of auto-display expression currently being displayed. + So that we can deleted it if we get an error or a signal within it. + -1 when not doing one. */ + +int current_display_number; + +/* Flag to low-level print routines that this value is being printed + in an epoch window. We'd like to pass this as a parameter, but + every routine would need to take it. Perhaps we can encapsulate + this in the I/O stream once we have GNU stdio. */ + +int inspect_it = 0; + +static void do_one_display (); + +void do_displays (); +void print_scalar_formatted (); + + +/* Decode a format specification. *STRING_PTR should point to it. + OFORMAT and OSIZE are used as defaults for the format and size + if none are given in the format specification. + If OSIZE is zero, then the size field of the returned value + should be set only if a size is explicitly specified by the + user. + The structure returned describes all the data + found in the specification. In addition, *STRING_PTR is advanced + past the specification and past all whitespace following it. */ + +struct format_data +decode_format (string_ptr, oformat, osize) + char **string_ptr; + char oformat; + char osize; +{ + struct format_data val; + register char *p = *string_ptr; + + val.format = '?'; + val.size = '?'; + val.count = 1; + + if (*p >= '0' && *p <= '9') + val.count = atoi (p); + while (*p >= '0' && *p <= '9') p++; + + /* Now process size or format letters that follow. */ + + while (1) + { + if (*p == 'b' || *p == 'h' || *p == 'w' || *p == 'g') + val.size = *p++; +#ifdef LONG_LONG + else if (*p == 'l') + { + val.size = 'g'; + p++; + } +#endif + else if (*p >= 'a' && *p <= 'z') + val.format = *p++; + else + break; + } + +#ifndef LONG_LONG + /* Make sure 'g' size is not used on integer types. + Well, actually, we can handle hex. */ + if (val.size == 'g' && val.format != 'f' && val.format != 'x') + val.size = 'w'; +#endif + + while (*p == ' ' || *p == '\t') p++; + *string_ptr = p; + + /* Set defaults for format and size if not specified. */ + if (val.format == '?') + { + if (val.size == '?') + { + /* Neither has been specified. */ + val.format = oformat; + val.size = osize; + } + else + /* If a size is specified, any format makes a reasonable + default except 'i'. */ + val.format = oformat == 'i' ? 'x' : oformat; + } + else if (val.size == '?') + switch (val.format) + { + case 'a': + case 's': + /* Addresses must be words. */ + val.size = osize ? 'w' : osize; + break; + case 'f': + /* Floating point has to be word or giantword. */ + if (osize == 'w' || osize == 'g') + val.size = osize; + else + /* Default it to giantword if the last used size is not + appropriate. */ + val.size = osize ? 'g' : osize; + break; + case 'c': + /* Characters default to one byte. */ + val.size = osize ? 'b' : osize; + break; + default: + /* The default is the size most recently specified. */ + val.size = osize; + } + + return val; +} + +/* Print value VAL on stdout according to FORMAT, a letter or 0. + Do not end with a newline. + 0 means print VAL according to its own type. + SIZE is the letter for the size of datum being printed. + This is used to pad hex numbers so they line up. */ + +static void +print_formatted (val, format, size) + register value val; + register char format; + char size; +{ + int len = TYPE_LENGTH (VALUE_TYPE (val)); + + if (VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val) + len; + + switch (format) + { + case 's': + next_address = VALUE_ADDRESS (val) + + value_print (value_addr (val), stdout, format, Val_pretty_default); + break; + + case 'i': + next_address = VALUE_ADDRESS (val) + + print_insn (VALUE_ADDRESS (val), stdout); + break; + + default: + if (format == 0 + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_ARRAY + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_STRUCT + || TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_UNION + || VALUE_REPEATED (val)) + value_print (val, stdout, format, Val_pretty_default); + else + print_scalar_formatted (VALUE_CONTENTS (val), VALUE_TYPE (val), + format, size, stdout); + } +} + +/* Print a scalar of data of type TYPE, pointed to in GDB by VALADDR, + according to letters FORMAT and SIZE on STREAM. + FORMAT may not be zero. Formats s and i are not supported at this level. + + This is how the elements of an array or structure are printed + with a format. */ + +void +print_scalar_formatted (valaddr, type, format, size, stream) + char *valaddr; + struct type *type; + char format; + int size; + FILE *stream; +{ + LONGEST val_long; + int len = TYPE_LENGTH (type); + + if (size == 'g' && sizeof (LONGEST) < 8 + && format == 'x') + { + /* ok, we're going to have to get fancy here. Assumption: a + long is four bytes. FIXME. */ + unsigned long v1, v2, tmp; + + v1 = unpack_long (builtin_type_long, valaddr); + v2 = unpack_long (builtin_type_long, valaddr + 4); + +#if TARGET_BYTE_ORDER == LITTLE_ENDIAN + /* Swap the two for printing */ + tmp = v1; + v1 = v2; + v2 = tmp; +#endif + + switch (format) + { + case 'x': + fprintf_filtered (stream, "0x%08x%08x", v1, v2); + break; + default: + error ("Output size \"g\" unimplemented for format \"%c\".", + format); + } + return; + } + + val_long = unpack_long (type, valaddr); + + /* If value is unsigned, truncate it in case negative. */ + if (format != 'd') + { + if (len == sizeof (char)) + val_long &= (1 << 8 * sizeof(char)) - 1; + else if (len == sizeof (short)) + val_long &= (1 << 8 * sizeof(short)) - 1; + else if (len == sizeof (long)) + val_long &= (unsigned long) - 1; + } + + switch (format) + { + case 'x': + if (!size) + { + /* no size specified, like in print. Print varying # of digits. */ +#if defined (LONG_LONG) + fprintf_filtered (stream, "0x%llx", val_long); +#else /* not LONG_LONG. */ + fprintf_filtered (stream, "0x%lx", val_long); +#endif /* not LONG_LONG. */ + } + else +#if defined (LONG_LONG) + switch (size) + { + case 'b': + fprintf_filtered (stream, "0x%02llx", val_long); + break; + case 'h': + fprintf_filtered (stream, "0x%04llx", val_long); + break; + case 'w': + fprintf_filtered (stream, "0x%08llx", val_long); + break; + case 'g': + fprintf_filtered (stream, "0x%016llx", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#else /* not LONG_LONG. */ + switch (size) + { + case 'b': + fprintf_filtered (stream, "0x%02x", val_long); + break; + case 'h': + fprintf_filtered (stream, "0x%04x", val_long); + break; + case 'w': + fprintf_filtered (stream, "0x%08x", val_long); + break; + case 'g': + fprintf_filtered (stream, "0x%016x", val_long); + break; + default: + error ("Undefined output size \"%c\".", size); + } +#endif /* not LONG_LONG */ + break; + + case 'd': +#ifdef LONG_LONG + fprintf_filtered (stream, "%lld", val_long); +#else + fprintf_filtered (stream, "%d", val_long); +#endif + break; + + case 'u': +#ifdef LONG_LONG + fprintf_filtered (stream, "%llu", val_long); +#else + fprintf_filtered (stream, "%u", val_long); +#endif + break; + + case 'o': + if (val_long) +#ifdef LONG_LONG + fprintf_filtered (stream, "0%llo", val_long); +#else + fprintf_filtered (stream, "0%o", val_long); +#endif + else + fprintf_filtered (stream, "0"); + break; + + case 'a': + print_address ((CORE_ADDR) val_long, stream); + break; + + case 'c': + value_print (value_from_long (builtin_type_char, val_long), stream, 0, + Val_pretty_default); + break; + + case 'f': + if (len == sizeof (float)) + type = builtin_type_float; + else if (len == sizeof (double)) + type = builtin_type_double; + print_floating (valaddr, type, stream); + break; + + case 0: + abort (); + + default: + error ("Undefined output format \"%c\".", format); + } +} + +/* Specify default address for `x' command. + `info lines' uses this. */ + +void +set_next_address (addr) + CORE_ADDR addr; +{ + next_address = addr; + + /* Make address available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, (LONGEST) addr)); +} + +/* Optionally print address ADDR symbolically as <SYMBOL+OFFSET> on STREAM. + DO_DEMANGLE controls whether to print a symbol in its native "raw" form, + or to interpret it as a possible C++ name and convert it back to source + form. */ + +void +print_address_symbolic (addr, stream, do_demangle) + CORE_ADDR addr; + FILE *stream; + int do_demangle; +{ + int name_location; + register int i = find_pc_misc_function (addr); + + /* If nothing comes out, don't print anything symbolic. */ + + if (i < 0) + return; + + fputs_filtered (" <", stream); + if (do_demangle) + fputs_demangled (misc_function_vector[i].name, stream, 1); + else + fputs_filtered (misc_function_vector[i].name, stream); + name_location = misc_function_vector[i].address; + if (addr - name_location) + fprintf_filtered (stream, "+%d>", addr - name_location); + else + fputs_filtered (">", stream); +} + +/* Print address ADDR symbolically on STREAM. + First print it as a number. Then perhaps print + <SYMBOL + OFFSET> after the number. */ + +void +print_address (addr, stream) + CORE_ADDR addr; + FILE *stream; +{ + fprintf_filtered (stream, "0x%x", addr); + print_address_symbolic (addr, stream, asm_demangle); +} + +/* Print address ADDR symbolically on STREAM. Parameter DEMANGLE + controls whether to print the symbolic name "raw" or demangled. */ + +void +print_address_demangle (addr, stream, do_demangle) + CORE_ADDR addr; + FILE *stream; + int do_demangle; +{ + fprintf_filtered (stream, "0x%x", addr); + print_address_symbolic (addr, stream, do_demangle); +} + + + +/* Examine data at address ADDR in format FMT. + Fetch it from memory and print on stdout. */ + +static void +do_examine (fmt, addr) + struct format_data fmt; + CORE_ADDR addr; +{ + register char format = 0; + register char size; + register int count = 1; + struct type *val_type; + register int i; + register int maxelts; + + format = fmt.format; + size = fmt.size; + count = fmt.count; + next_address = addr; + + /* String or instruction format implies fetch single bytes + regardless of the specified size. */ + if (format == 's' || format == 'i') + size = 'b'; + + if (size == 'b') + val_type = builtin_type_char; + else if (size == 'h') + val_type = builtin_type_short; + else if (size == 'w') + val_type = builtin_type_long; + else if (size == 'g') +#ifndef LONG_LONG + val_type = builtin_type_double; +#else + val_type = builtin_type_long_long; +#endif + + maxelts = 8; + if (size == 'w') + maxelts = 4; + if (size == 'g') + maxelts = 2; + if (format == 's' || format == 'i') + maxelts = 1; + + /* Print as many objects as specified in COUNT, at most maxelts per line, + with the address of the next one at the start of each line. */ + + while (count > 0) + { + print_address (next_address, stdout); + printf_filtered (":"); + for (i = maxelts; + i > 0 && count > 0; + i--, count--) + { + printf_filtered ("\t"); + /* Note that print_formatted sets next_address for the next + object. */ + last_examine_address = next_address; + last_examine_value = value_at (val_type, next_address); + print_formatted (last_examine_value, format, size); + } + printf_filtered ("\n"); + fflush (stdout); + } +} + +static void +validate_format (fmt, cmdname) + struct format_data fmt; + char *cmdname; +{ + if (fmt.size != 0) + error ("Size letters are meaningless in \"%s\" command.", cmdname); + if (fmt.count != 1) + error ("Item count other than 1 is meaningless in \"%s\" command.", + cmdname); + if (fmt.format == 'i' || fmt.format == 's') + error ("Format letter \"%c\" is meaningless in \"%s\" command.", + fmt.format, cmdname); +} + +static void +print_command_1 (exp, inspect, voidprint) + char *exp; + int inspect; + int voidprint; +{ + struct expression *expr; + register struct cleanup *old_chain = 0; + register char format = 0; + register value val; + struct format_data fmt; + int cleanup = 0; + + /* Pass inspect flag to the rest of the print routines in a global (sigh). */ + inspect_it = inspect; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, 0); + validate_format (fmt, "print"); + last_format = format = fmt.format; + } + else + { + fmt.count = 1; + fmt.format = 0; + fmt.size = 0; + } + + if (exp && *exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + cleanup = 1; + val = evaluate_expression (expr); + } + else + val = access_value_history (0); + + if (voidprint || (val && VALUE_TYPE (val) && + TYPE_CODE (VALUE_TYPE (val)) != TYPE_CODE_VOID)) + { + int histindex = record_latest_value (val); + + if (inspect) + printf ("\031(gdb-makebuffer \"%s\" %d '(\"", exp, histindex); + else + if (histindex >= 0) printf_filtered ("$%d = ", histindex); + + print_formatted (val, format, fmt.size); + printf_filtered ("\n"); + if (inspect) + printf("\") )\030"); + } + + if (cleanup) + do_cleanups (old_chain); + inspect_it = 0; /* Reset print routines to normal */ +} + +static void +print_command (exp, from_tty) + char *exp; + int from_tty; +{ + print_command_1 (exp, 0, 1); +} + +/* Same as print, except in epoch, it gets its own window */ +static void +inspect_command (exp, from_tty) + char *exp; + int from_tty; +{ + extern int epoch_interface; + + print_command_1 (exp, epoch_interface, 1); +} + +/* Same as print, except it doesn't print void results. */ +static void +call_command (exp, from_tty) + char *exp; + int from_tty; +{ + print_command_1 (exp, 0, 0); +} + +static void +output_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr; + register struct cleanup *old_chain; + register char format = 0; + register value val; + struct format_data fmt; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + validate_format (fmt, "print"); + format = fmt.format; + } + + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + + val = evaluate_expression (expr); + + print_formatted (val, format, fmt.size); + + do_cleanups (old_chain); +} + +static void +set_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr = parse_c_expression (exp); + register struct cleanup *old_chain + = make_cleanup (free_current_contents, &expr); + evaluate_expression (expr); + do_cleanups (old_chain); +} + +static void +address_info (exp, from_tty) + char *exp; + int from_tty; +{ + register struct symbol *sym; + register CORE_ADDR val; + int is_a_field_of_this; /* C++: lookup_symbol sets this to nonzero + if exp is a field of `this'. */ + + if (exp == 0) + error ("Argument required."); + + sym = lookup_symbol (exp, get_selected_block (), VAR_NAMESPACE, + &is_a_field_of_this, (struct symtab **)NULL); + if (sym == 0) + { + register int i; + + if (is_a_field_of_this) + { + printf ("Symbol \"%s\" is a field of the local class variable `this'\n", exp); + return; + } + + for (i = 0; i < misc_function_count; i++) + if (!strcmp (misc_function_vector[i].name, exp)) + break; + + if (i < misc_function_count) + printf ("Symbol \"%s\" is at 0x%x in a file compiled without -g.\n", + exp, misc_function_vector[i].address); + else + error ("No symbol \"%s\" in current context.", exp); + return; + } + + printf ("Symbol \"%s\" is ", SYMBOL_NAME (sym)); + val = SYMBOL_VALUE (sym); + + switch (SYMBOL_CLASS (sym)) + { + case LOC_CONST: + case LOC_CONST_BYTES: + printf ("constant"); + break; + + case LOC_LABEL: + printf ("a label at address 0x%x", SYMBOL_VALUE_ADDRESS (sym)); + break; + + case LOC_REGISTER: + printf ("a variable in register %s", reg_names[val]); + break; + + case LOC_STATIC: + printf ("static at address 0x%x", SYMBOL_VALUE_ADDRESS (sym)); + break; + + case LOC_REGPARM: + printf ("an argument in register %s", reg_names[val]); + break; + + case LOC_ARG: + printf ("an argument at offset %d", (int)val); + break; + + case LOC_LOCAL_ARG: + printf ("an argument at frame offset %d", (int)val); + break; + + case LOC_LOCAL: + printf ("a local variable at frame offset %d", (int)val); + break; + + case LOC_REF_ARG: + printf ("a reference argument at offset %d", (int)val); + break; + + case LOC_TYPEDEF: + printf ("a typedef"); + break; + + case LOC_BLOCK: + printf ("a function at address 0x%x", + BLOCK_START (SYMBOL_BLOCK_VALUE (sym))); + break; + + case LOC_EXTERNAL: + printf ("an external symbol at address 0x%x", + SYMBOL_VALUE_ADDRESS (sym)); + break; + + default: + printf ("of unknown (botched) type"); + break; + } + printf (".\n"); +} + +static void +x_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct expression *expr; + struct format_data fmt; + struct cleanup *old_chain; + struct value *val; + + fmt.format = last_format; + fmt.size = last_size; + fmt.count = 1; + + if (exp && *exp == '/') + { + exp++; + fmt = decode_format (&exp, last_format, last_size); + last_size = fmt.size; + last_format = fmt.format; + } + + /* If we have an expression, evaluate it and use it as the address. */ + + if (exp != 0 && *exp != 0) + { + expr = parse_c_expression (exp); + /* Cause expression not to be there any more + if this command is repeated with Newline. + But don't clobber a user-defined command's definition. */ + if (from_tty) + *exp = 0; + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_expression (expr); + /* In rvalue contexts, such as this, functions are coerced into + pointers to functions. This makes "x/i main" work. */ + if (/* last_format == 'i' + && */ TYPE_CODE (VALUE_TYPE (val)) == TYPE_CODE_FUNC + && VALUE_LVAL (val) == lval_memory) + next_address = VALUE_ADDRESS (val); + else + next_address = (CORE_ADDR) value_as_long (val); + do_cleanups (old_chain); + } + + do_examine (fmt, next_address); + + /* Set a couple of internal variables if appropriate. */ + if (last_examine_value) + { + /* Make last address examined available to the user as $_. */ + set_internalvar (lookup_internalvar ("_"), + value_from_long (builtin_type_int, + (LONGEST) last_examine_address)); + + /* Make contents of last address examined available to the user as $__.*/ + set_internalvar (lookup_internalvar ("__"), last_examine_value); + } +} + +/* Commands for printing types of things. */ + +/* Print type of EXP, or last thing in value history if EXP == NULL. + show is passed to type_print. */ +static void +whatis_exp (exp, show) + char *exp; + int show; +{ + struct expression *expr; + register value val; + register struct cleanup *old_chain; + + if (exp) + { + expr = parse_c_expression (exp); + old_chain = make_cleanup (free_current_contents, &expr); + val = evaluate_type (expr); + } + else + val = access_value_history (0); + + printf_filtered ("type = "); + type_print (VALUE_TYPE (val), "", stdout, show); + printf_filtered ("\n"); + + if (exp) + do_cleanups (old_chain); +} + +static void +whatis_command (exp, from_tty) + char *exp; + int from_tty; +{ + /* Most of the time users do not want to see all the fields + in a structure. If they do they can use the "ptype" command. + Hence the "-1" below. */ + whatis_exp (exp, -1); +} + +/* TYPENAME is either the name of a type, or an expression. */ +static void +ptype_command (typename, from_tty) + char *typename; + int from_tty; +{ + register char *p = typename; + register int len; + register struct block *b + = target_has_stack ? get_current_block () : 0; + register struct type *type; + + if (typename == 0) + { + whatis_exp (typename, 1); + return; + } + + while (*p && *p != ' ' && *p != '\t') p++; + len = p - typename; + while (*p == ' ' || *p == '\t') p++; + + if (len == 6 && !strncmp (typename, "struct", 6)) + type = lookup_struct (p, b); + else if (len == 5 && !strncmp (typename, "union", 5)) + type = lookup_union (p, b); + else if (len == 4 && !strncmp (typename, "enum", 4)) + type = lookup_enum (p, b); + else + { + type = lookup_typename (typename, b, 1); + if (type == 0) + { + register struct symbol *sym + = lookup_symbol (typename, b, STRUCT_NAMESPACE, 0, + (struct symtab **)NULL); + if (sym == 0) + { + whatis_exp (typename, 1 /* FIXME: right? */); + return; + } + printf_filtered ("No type named %s, but there is a ", + typename); + switch (TYPE_CODE (SYMBOL_TYPE (sym))) + { + case TYPE_CODE_STRUCT: + printf_filtered ("struct"); + break; + + case TYPE_CODE_UNION: + printf_filtered ("union"); + break; + + case TYPE_CODE_ENUM: + printf_filtered ("enum"); + break; + + default: + printf_filtered ("(Internal error in gdb)"); + break; + } + printf_filtered (" %s. Type \"help ptype\".\n", typename); + type = SYMBOL_TYPE (sym); + } + } + + type_print (type, "", stdout, 1); + printf_filtered ("\n"); +} + +#if 0 +/* This is not necessary. Instead, decode_line_1 takes any variable, + so "info line foo" is a close equivalent to "whereis foo". */ +static void +whereis_command (var, from_tty) + char *var; + int from_tty; +{ + struct symtab *s; + struct symbol *sym; + + if (var == NULL) + error_no_arg ("Variable name."); + + sym = lookup_symbol (var, get_selected_block (), VAR_NAMESPACE, + NULL, &s); + + if (sym != NULL && s != NULL) + printf_filtered ("Symbol \"%s\" is at line %d of file %s\n", + var, sym->line, s->filename); + else + { + if (lookup_misc_func (var) >= 0) + printf_filtered ("Symbol \"%s\" is in a file compiled without -g.", + var); + else + error ("No symbol \"%s\" in current context.", var); + } +} +#endif /* 0 */ + +enum display_status {disabled, enabled}; + +struct display +{ + /* Chain link to next auto-display item. */ + struct display *next; + /* Expression to be evaluated and displayed. */ + struct expression *exp; + /* Item number of this auto-display item. */ + int number; + /* Display format specified. */ + struct format_data format; + /* Innermost block required by this expression when evaluated */ + struct block *block; + /* Status of this display (enabled or disabled) */ + enum display_status status; +}; + +/* Chain of expressions whose values should be displayed + automatically each time the program stops. */ + +static struct display *display_chain; + +static int display_number; + +/* Add an expression to the auto-display chain. + Specify the expression. */ + +static void +display_command (exp, from_tty) + char *exp; + int from_tty; +{ + struct format_data fmt; + register struct expression *expr; + register struct display *new; + + if (exp == 0) + { + do_displays (); + return; + } + + if (*exp == '/') + { + exp++; + fmt = decode_format (&exp, 0, 0); + if (fmt.size && fmt.format == 0) + fmt.format = 'x'; + if (fmt.format == 'i' || fmt.format == 's') + fmt.size = 'b'; + } + else + { + fmt.format = 0; + fmt.size = 0; + fmt.count = 0; + } + + innermost_block = 0; + expr = parse_c_expression (exp); + + new = (struct display *) xmalloc (sizeof (struct display)); + + new->exp = expr; + new->block = innermost_block; + new->next = display_chain; + new->number = ++display_number; + new->format = fmt; + new->status = enabled; + display_chain = new; + + if (from_tty && target_has_execution) + do_one_display (new); + + dont_repeat (); +} + +static void +free_display (d) + struct display *d; +{ + free (d->exp); + free (d); +} + +/* Clear out the display_chain. + Done when new symtabs are loaded, since this invalidates + the types stored in many expressions. */ + +void +clear_displays () +{ + register struct display *d; + + while (d = display_chain) + { + free (d->exp); + display_chain = d->next; + free (d); + } +} + +/* Delete the auto-display number NUM. */ + +void +delete_display (num) + int num; +{ + register struct display *d1, *d; + + if (!display_chain) + error ("No display number %d.", num); + + if (display_chain->number == num) + { + d1 = display_chain; + display_chain = d1->next; + free_display (d1); + } + else + for (d = display_chain; ; d = d->next) + { + if (d->next == 0) + error ("No display number %d.", num); + if (d->next->number == num) + { + d1 = d->next; + d->next = d1->next; + free_display (d1); + break; + } + } +} + +/* Delete some values from the auto-display chain. + Specify the element numbers. */ + +static void +undisplay_command (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + + if (args == 0) + { + if (query ("Delete all auto-display expressions? ")) + clear_displays (); + dont_repeat (); + return; + } + + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + delete_display (num); + + p = p1; + while (*p == ' ' || *p == '\t') p++; + } + dont_repeat (); +} + +/* Display a single auto-display. + Do nothing if the display cannot be printed in the current context, + or if the display is disabled. */ + +static void +do_one_display (d) + struct display *d; +{ + int within_current_scope; + + if (d->status == disabled) + return; + + if (d->block) + within_current_scope = contained_in (get_selected_block (), d->block); + else + within_current_scope = 1; + if (!within_current_scope) + return; + + current_display_number = d->number; + + printf_filtered ("%d: ", d->number); + if (d->format.size) + { + CORE_ADDR addr; + + printf_filtered ("x/"); + if (d->format.count != 1) + printf_filtered ("%d", d->format.count); + printf_filtered ("%c", d->format.format); + if (d->format.format != 'i' && d->format.format != 's') + printf_filtered ("%c", d->format.size); + printf_filtered (" "); + print_expression (d->exp, stdout); + if (d->format.count != 1) + printf_filtered ("\n"); + else + printf_filtered (" "); + + addr = (CORE_ADDR) value_as_long (evaluate_expression (d->exp)); + if (d->format.format == 'i') + addr = ADDR_BITS_REMOVE (addr); + + do_examine (d->format, addr); + } + else + { + if (d->format.format) + printf_filtered ("/%c ", d->format.format); + print_expression (d->exp, stdout); + printf_filtered (" = "); + print_formatted (evaluate_expression (d->exp), + d->format.format, d->format.size); + printf_filtered ("\n"); + } + + fflush (stdout); + current_display_number = -1; +} + +/* Display all of the values on the auto-display chain which can be + evaluated in the current scope. */ + +void +do_displays () +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + do_one_display (d); +} + +/* Delete the auto-display which we were in the process of displaying. + This is done when there is an error or a signal. */ + +void +disable_display (num) + int num; +{ + register struct display *d; + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = disabled; + return; + } + printf ("No display number %d.\n", num); +} + +void +disable_current_display () +{ + if (current_display_number >= 0) + { + disable_display (current_display_number); + fprintf (stderr, "Disabling display %d to avoid infinite recursion.\n", + current_display_number); + } + current_display_number = -1; +} + +static void +display_info () +{ + register struct display *d; + + if (!display_chain) + printf ("There are no auto-display expressions now.\n"); + else + printf_filtered ("Auto-display expressions now in effect:\n\ +Num Enb Expression\n"); + + for (d = display_chain; d; d = d->next) + { + printf_filtered ("%d: %c ", d->number, "ny"[(int)d->status]); + if (d->format.size) + printf_filtered ("/%d%c%c ", d->format.count, d->format.size, + d->format.format); + else if (d->format.format) + printf_filtered ("/%c ", d->format.format); + print_expression (d->exp, stdout); + if (d->block && !contained_in (get_selected_block (), d->block)) + printf_filtered (" (cannot be evaluated in the current context)"); + printf_filtered ("\n"); + fflush (stdout); + } +} + +void +enable_display (args) + char *args; +{ + register char *p = args; + register char *p1; + register int num; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->status = enabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + num = atoi (p); + + for (d = display_chain; d; d = d->next) + if (d->number == num) + { + d->status = enabled; + goto win; + } + printf ("No display number %d.\n", num); + win: + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + +void +disable_display_command (args, from_tty) + char *args; + int from_tty; +{ + register char *p = args; + register char *p1; + register struct display *d; + + if (p == 0) + { + for (d = display_chain; d; d = d->next) + d->status = disabled; + } + else + while (*p) + { + p1 = p; + while (*p1 >= '0' && *p1 <= '9') + p1++; + if (*p1 && *p1 != ' ' && *p1 != '\t') + error ("Arguments must be display numbers."); + + disable_display (atoi (p)); + + p = p1; + while (*p == ' ' || *p == '\t') + p++; + } +} + + +/* Print the value in stack frame FRAME of a variable + specified by a struct symbol. */ + +void +print_variable_value (var, frame, stream) + struct symbol *var; + FRAME frame; + FILE *stream; +{ + value val = read_var_value (var, frame); + value_print (val, stream, 0, Val_pretty_default); +} + +/* Print the arguments of a stack frame, given the function FUNC + running in that frame (as a symbol), the info on the frame, + and the number of args according to the stack frame (or -1 if unknown). */ + +/* References here and elsewhere to "number of args according to the + stack frame" appear in all cases to refer to "number of ints of args + according to the stack frame". At least for VAX, i386, isi. */ + +void +print_frame_args (func, fi, num, stream) + struct symbol *func; + struct frame_info *fi; + int num; + FILE *stream; +{ + struct block *b; + int nsyms = 0; + int first = 1; + register int i; + register struct symbol *sym; + register value val; + /* Offset of next stack argument beyond the one we have seen that is + at the highest offset. + -1 if we haven't come to a stack argument yet. */ + int highest_offset = -1; + int arg_size; + /* Number of ints of arguments that we have printed so far. */ + int args_printed = 0; + + if (func) + { + b = SYMBOL_BLOCK_VALUE (func); + nsyms = BLOCK_NSYMS (b); + } + + for (i = 0; i < nsyms; i++) + { + QUIT; + sym = BLOCK_SYM (b, i); + + if (SYMBOL_CLASS (sym) != LOC_REGPARM + && SYMBOL_CLASS (sym) != LOC_ARG + && SYMBOL_CLASS (sym) != LOC_LOCAL_ARG + && SYMBOL_CLASS (sym) != LOC_REF_ARG) + continue; + + /* We have to re-look-up the symbol because arguments often have + two entries (one a parameter, one a register or local), and the one + we want is the non-parm, which lookup_symbol will find for + us. After this, sym could be any SYMBOL_CLASS... */ + sym = lookup_symbol (SYMBOL_NAME (sym), + b, VAR_NAMESPACE, (int *)NULL, (struct symtab **)NULL); + + switch (SYMBOL_CLASS (sym)) { + + /* Keep track of the highest stack argument offset seen */ + case LOC_ARG: + case LOC_REF_ARG: + { + int current_offset = SYMBOL_VALUE (sym); + + arg_size = TYPE_LENGTH (SYMBOL_TYPE (sym)); + + /* Compute address of next argument by adding the size of + this argument and rounding to an int boundary. */ + current_offset + = ((current_offset + arg_size + sizeof (int) - 1) + & ~(sizeof (int) - 1)); + + /* If this is the highest offset seen yet, set highest_offset. */ + if (highest_offset == -1 + || (current_offset > highest_offset)) + highest_offset = current_offset; + + /* Add the number of ints we're about to print to args_printed. */ + args_printed += (arg_size + sizeof (int) - 1) / sizeof (int); + } + + /* Other types of symbols don't need to be kept track of. */ + default: + break; + } + + /* Print the current arg. */ + if (! first) + fprintf_filtered (stream, ", "); + wrap_here (" "); + fprint_symbol (stream, SYMBOL_NAME (sym)); + fputs_filtered ("=", stream); + + /* Avoid value_print because it will deref ref parameters. We just + want to print their addresses. Print ??? for args whose address + we do not know. */ + val = read_var_value (sym, FRAME_INFO_ID (fi)); + if (val) + val_print (VALUE_TYPE (val), VALUE_CONTENTS (val), VALUE_ADDRESS (val), + stream, 0, 0, 0, Val_no_prettyprint); + else + fputs_filtered ("???", stream); + first = 0; + } + + /* Don't print nameless args in situations where we don't know + enough about the stack to find them. */ + if (num != -1) + { + int start; + CORE_ADDR addr; + + if (highest_offset == -1) + start = FRAME_ARGS_SKIP; + else + start = highest_offset; + + addr = FRAME_ARGS_ADDRESS (fi); + if (addr) + print_frame_nameless_args (addr, start, num - args_printed, + first, stream); + } +} + +/* Print nameless args on STREAM. + ARGSADDR is the address of the arglist, START is the offset + of the first nameless arg, and NUM is the number of nameless args to + print. FIRST is nonzero if this is the first argument (not just + the first nameless arg). */ +static void +print_frame_nameless_args (argsaddr, start, num, first, stream) + CORE_ADDR argsaddr; + int start; + int num; + int first; + FILE *stream; +{ + int i; + for (i = 0; i < num; i++) + { + QUIT; + if (!first) + fprintf_filtered (stream, ", "); +#ifndef PRINT_TYPELESS_INTEGER + fprintf_filtered (stream, "%d", + read_memory_integer (argsaddr + start, sizeof (int))); +#else + PRINT_TYPELESS_INTEGER (stream, builtin_type_int, + (LONGEST) + read_memory_integer (argsaddr + start, + sizeof (int))); +#endif + first = 0; + start += sizeof (int); + } +} + +static void +printf_command (arg, from_tty) + char *arg; + int from_tty; +{ + register char *f; + register char *s = arg; + char *string; + value *val_args; + int nargs = 0; + int allocated_args = 20; + char *arg_bytes; + + val_args = (value *) xmalloc (allocated_args * sizeof (value)); + + if (s == 0) + error_no_arg ("format-control string and values to print"); + + /* Skip white space before format string */ + while (*s == ' ' || *s == '\t') s++; + + /* A format string should follow, enveloped in double quotes */ + if (*s++ != '"') + error ("Bad format string, missing '\"'."); + + /* Parse the format-control string and copy it into the string STRING, + processing some kinds of escape sequence. */ + + f = string = (char *) alloca (strlen (s) + 1); + while (*s != '"') + { + int c = *s++; + switch (c) + { + case '\0': + error ("Bad format string, non-terminated '\"'."); + /* doesn't return */ + + case '\\': + switch (c = *s++) + { + case '\\': + *f++ = '\\'; + break; + case 'n': + *f++ = '\n'; + break; + case 't': + *f++ = '\t'; + break; + case 'r': + *f++ = '\r'; + break; + case '"': + *f++ = '"'; + break; + default: + /* ??? TODO: handle other escape sequences */ + error ("Unrecognized \\ escape character in format string."); + } + break; + + default: + *f++ = c; + } + } + + /* Skip over " and following space and comma. */ + s++; + *f++ = '\0'; + while (*s == ' ' || *s == '\t') s++; + + if (*s != ',' && *s != 0) + error ("Invalid argument syntax"); + + if (*s == ',') s++; + while (*s == ' ' || *s == '\t') s++; + + { + /* Now scan the string for %-specs and see what kinds of args they want. + argclass[I] classifies the %-specs so we can give vprintf something + of the right size. */ + + enum argclass {int_arg, string_arg, double_arg, long_long_arg}; + enum argclass *argclass; + int nargs_wanted; + int argindex; + int lcount; + int i; + + argclass = (enum argclass *) alloca (strlen (s) * sizeof *argclass); + nargs_wanted = 0; + f = string; + while (*f) + if (*f++ == '%') + { + lcount = 0; + while (strchr ("0123456789.hlL-+ #", *f)) + { + if (*f == 'l' || *f == 'L') + lcount++; + f++; + } + if (*f == 's') + argclass[nargs_wanted++] = string_arg; + else if (*f == 'e' || *f == 'f' || *f == 'g') + argclass[nargs_wanted++] = double_arg; + else if (lcount > 1) + argclass[nargs_wanted++] = long_long_arg; + else if (*f != '%') + argclass[nargs_wanted++] = int_arg; + f++; + } + + /* Now, parse all arguments and evaluate them. + Store the VALUEs in VAL_ARGS. */ + + while (*s != '\0') + { + char *s1; + if (nargs == allocated_args) + val_args = (value *) xrealloc (val_args, + (allocated_args *= 2) + * sizeof (value)); + s1 = s; + val_args[nargs] = parse_to_comma_and_eval (&s1); + + /* If format string wants a float, unchecked-convert the value to + floating point of the same size */ + + if (argclass[nargs] == double_arg) + { + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (float)) + VALUE_TYPE (val_args[nargs]) = builtin_type_float; + if (TYPE_LENGTH (VALUE_TYPE (val_args[nargs])) == sizeof (double)) + VALUE_TYPE (val_args[nargs]) = builtin_type_double; + } + nargs++; + s = s1; + if (*s == ',') + s++; + } + + if (nargs != nargs_wanted) + error ("Wrong number of arguments for specified format-string"); + + /* Now lay out an argument-list containing the arguments + as doubles, integers and C pointers. */ + + arg_bytes = (char *) alloca (sizeof (double) * nargs); + argindex = 0; + for (i = 0; i < nargs; i++) + { + if (argclass[i] == string_arg) + { + char *str; + int tem, j; + tem = value_as_long (val_args[i]); + + /* This is a %s argument. Find the length of the string. */ + for (j = 0; ; j++) + { + char c; + QUIT; + read_memory (tem + j, &c, 1); + if (c == 0) + break; + } + + /* Copy the string contents into a string inside GDB. */ + str = (char *) alloca (j + 1); + read_memory (tem, str, j); + str[j] = 0; + + /* Pass address of internal copy as the arg to vprintf. */ + *((int *) &arg_bytes[argindex]) = (int) str; + argindex += sizeof (int); + } + else if (VALUE_TYPE (val_args[i])->code == TYPE_CODE_FLT) + { + *((double *) &arg_bytes[argindex]) = value_as_double (val_args[i]); + argindex += sizeof (double); + } + else +#ifdef LONG_LONG + if (argclass[i] == long_long_arg) + { + *(long long *) &arg_bytes[argindex] = value_as_long (val_args[i]); + argindex += sizeof (long long); + } + else +#endif + { + *((int *) &arg_bytes[argindex]) = value_as_long (val_args[i]); + argindex += sizeof (int); + } + } + } + + /* There is not a standard way to make a va_list, so we need + to do various things for different systems. */ +#if defined (__INT_VARARGS_H) + { + va_list list; + + list.__va_arg = 0; + list.__va_stk = (int *) arg_bytes; + list.__va_reg = (int *) arg_bytes; + vprintf (string, list); + } +#else /* No __INT_VARARGS_H. */ + vprintf (string, arg_bytes); +#endif /* No __INT_VARARGS_H. */ +} + +/* Helper function for asdump_command. Finds the bounds of a function + for a specified section of text. PC is an address within the + function which you want bounds for; *LOW and *HIGH are set to the + beginning (inclusive) and end (exclusive) of the function. This + function returns 1 on success and 0 on failure. */ + +static int +containing_function_bounds (pc, low, high) + CORE_ADDR pc, *low, *high; +{ + int scan; + + if (!find_pc_partial_function (pc, 0, low)) + return 0; + + scan = *low; + do { + scan++; + if (!find_pc_partial_function (scan, 0, high)) + return 0; + } while (*low == *high); + + return 1; +} + +/* Dump a specified section of assembly code. With no command line + arguments, this command will dump the assembly code for the + function surrounding the pc value in the selected frame. With one + argument, it will dump the assembly code surrounding that pc value. + Two arguments are interpeted as bounds within which to dump + assembly. */ + +static void +disassemble_command (arg, from_tty) + char *arg; + int from_tty; +{ + CORE_ADDR low, high; + CORE_ADDR pc; + char *space_index; + + if (!arg) + { + if (!selected_frame) + error ("No frame selected.\n"); + + pc = get_frame_pc (selected_frame); + if (!containing_function_bounds (pc, &low, &high)) + error ("No function contains pc specified by selected frame.\n"); + } + else if (!(space_index = (char *) strchr (arg, ' '))) + { + /* One argument. */ + pc = parse_and_eval_address (arg); + if (!containing_function_bounds (pc, &low, &high)) + error ("No function contains specified pc.\n"); + } + else + { + /* Two arguments. */ + *space_index = '\0'; + low = parse_and_eval_address (arg); + high = parse_and_eval_address (space_index + 1); + } + + printf_filtered ("Dump of assembler code "); + if (!space_index) + { + char *name; + find_pc_partial_function (pc, &name, 0); + printf_filtered ("for function %s:\n", name); + } + else + printf_filtered ("from 0x%x to 0x%x:\n", low, high); + + /* Dump the specified range. */ + for (pc = low; pc < high; ) + { + QUIT; + print_address (pc, stdout); + printf_filtered (":\t"); + pc += print_insn (pc, stdout); + printf_filtered ("\n"); + } + printf_filtered ("End of assembler dump.\n"); + fflush (stdout); +} + + +void +_initialize_printcmd () +{ + current_display_number = -1; + + add_info ("address", address_info, + "Describe where variable VAR is stored."); + + add_com ("x", class_vars, x_command, + "Examine memory: x/FMT ADDRESS.\n\ +ADDRESS is an expression for the memory address to examine.\n\ +FMT is a repeat count followed by a format letter and a size letter.\n\ +Format letters are o(octal), x(hex), d(decimal), u(unsigned decimal),\n\ + f(float), a(address), i(instruction), c(char) and s(string).\n\ +Size letters are b(byte), h(halfword), w(word), g(giant, 8 bytes).\n\ + g is meaningful only with f, for type double.\n\ +The specified number of objects of the specified size are printed\n\ +according to the format.\n\n\ +Defaults for format and size letters are those previously used.\n\ +Default count is 1. Default address is following last thing printed\n\ +with this command or \"print\"."); + + add_com ("disassemble", class_vars, disassemble_command, + "Disassemble a specified section of memory.\n\ +Default is the function surrounding the pc of the selected frame.\n\ +With a single argument, the function surrounding that address is dumped.\n\ +Two arguments are taken as a range of memory to dump."); + + add_com ("ptype", class_vars, ptype_command, + "Print definition of type TYPE.\n\ +Argument may be a type name defined by typedef, or \"struct STRUCTNAME\"\n\ +or \"union UNIONNAME\" or \"enum ENUMNAME\".\n\ +The selected stack frame's lexical context is used to look up the name."); + + add_com ("whatis", class_vars, whatis_command, + "Print data type of expression EXP."); + +#if 0 + add_com ("whereis", class_vars, whereis_command, + "Print line number and file of definition of variable."); +#endif + + add_info ("display", display_info, + "Expressions to display when program stops, with code numbers."); + + add_cmd ("undisplay", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +\"delete display\" has the same effect as this command.\n\ +Do \"info display\" to see current list of code numbers.", + &cmdlist); + + add_com ("display", class_vars, display_command, + "Print value of expression EXP each time the program stops.\n\ +/FMT may be used before EXP as in the \"print\" command.\n\ +/FMT \"i\" or \"s\" or including a size-letter is allowed,\n\ +as in the \"x\" command, and then EXP is used to get the address to examine\n\ +and examining is done as in the \"x\" command.\n\n\ +With no argument, display all currently requested auto-display expressions.\n\ +Use \"undisplay\" to cancel display requests previously made."); + + add_cmd ("display", class_vars, enable_display, + "Enable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to resume displaying.\n\ +No argument means enable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &enablelist); + + add_cmd ("display", class_vars, disable_display_command, + "Disable some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means disable all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &disablelist); + + add_cmd ("display", class_vars, undisplay_command, + "Cancel some expressions to be displayed when program stops.\n\ +Arguments are the code numbers of the expressions to stop displaying.\n\ +No argument means cancel all automatic-display expressions.\n\ +Do \"info display\" to see current list of code numbers.", &deletelist); + + add_com ("printf", class_vars, printf_command, + "printf \"printf format string\", arg1, arg2, arg3, ..., argn\n\ +This is useful for formatted output in user-defined commands."); + add_com ("output", class_vars, output_command, + "Like \"print\" but don't put in value history and don't print newline.\n\ +This is useful in user-defined commands."); + + add_prefix_cmd ("set", class_vars, set_command, +"Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +Use \"set variable\" for variables with names identical to set subcommands.\n\ +\nWith a subcommand, this command modifies parts of the gdb environment.\n\ +You can see these environment settings with the \"show\" command.", + &setlist, "set ", 1, &cmdlist); + + /* "call" is the same as "set", but handy for dbx users to call fns. */ + add_com ("call", class_vars, call_command, + "Call a function in the inferior process.\n\ +The argument is the function name and arguments, in standard C notation.\n\ +The result is printed and saved in the value history, if it is not void."); + + add_cmd ("variable", class_vars, set_command, + "Perform an assignment VAR = EXP.\n\ +You must type the \"=\". VAR may be a debugger \"convenience\" variable\n\ +(names starting with $), a register (a few standard names starting with $),\n\ +or an actual variable in the program being debugged. EXP is any expression.\n\ +This may usually be abbreviated to simply \"set\".", + &setlist); + + add_com ("print", class_vars, print_command, + concat ("Print value of expression EXP.\n\ +Variables accessible are those of the lexical environment of the selected\n\ +stack frame, plus all those whose scope is global or an entire file.\n\ +\n\ +$NUM gets previous value number NUM. $ and $$ are the last two values.\n\ +$$NUM refers to NUM'th value back from the last one.\n\ +Names starting with $ refer to registers (with the values they would have\n\ +if the program were to return to the stack frame now selected, restoring\n\ +all registers saved by frames farther in) or else to debugger\n\ +\"convenience\" variables (any such name not a known register).\n\ +Use assignment expressions to give values to convenience variables.\n", + "\n\ +{TYPE}ADREXP refers to a datum of data type TYPE, located at address ADREXP.\n\ +@ is a binary operator for treating consecutive data objects\n\ +anywhere in memory as an array. FOO@NUM gives an array whose first\n\ +element is FOO, whose second element is stored in the space following\n\ +where FOO is stored, etc. FOO must be an expression whose value\n\ +resides in memory.\n", + "\n\ +EXP may be preceded with /FMT, where FMT is a format letter\n\ +but no count or size letter (see \"x\" command).")); + add_com_alias ("p", "print", class_vars, 1); + + add_com ("inspect", class_vars, inspect_command, +"Same as \"print\" command, except that if you are running in the epoch\n\ +environment, the value is printed in its own window."); +} |