diff options
author | Fernando Nasser <fnasser@redhat.com> | 2000-12-01 18:01:38 +0000 |
---|---|---|
committer | Fernando Nasser <fnasser@redhat.com> | 2000-12-01 18:01:38 +0000 |
commit | d318976c46b92e4d8640f1310bb7b6b517c8bcf7 (patch) | |
tree | bec3a2a019b3bfc7a4cbb67c250446b4a9217a8c /gdb/cli | |
parent | 4ce3447c164823aa64a96abb840d0886d1f84c63 (diff) | |
download | gdb-d318976c46b92e4d8640f1310bb7b6b517c8bcf7.zip gdb-d318976c46b92e4d8640f1310bb7b6b517c8bcf7.tar.gz gdb-d318976c46b92e4d8640f1310bb7b6b517c8bcf7.tar.bz2 |
2000-12-01 Fernando Nasser <fnasser@redhat.com>
* cli/cli-decode.c: New file. Handle lists of commands, their decoding
and documentation.
(add_cmd, deprecate_cmd, add_abbrev_cmd, add_alias_cmd, add_prefix_cmd,
add_abbrev_prefix_cmd, not_just_help_class_command, empty_sfunc,
add_set_cmd, add_set_enum_cmd, add_set_auto_boolean_cmd,
add_show_from_set, delete_cmd, apropos_cmd, help_cmd, help_list,
help_all, print_doc_line, help_cmd_list, find_cmd, lookup_cmd_1,
undef_cmd_error, lookup_cmd, deprecated_cmd_warning,
lookup_cmd_composition, complete_on_cmdlist, complete_on_enum):
Moved here from command.c.
(add_info, add_info_alias, add_com, add_com_alias): Moved here from
top.c.
* cli/cli-decode.h: Definitions/declarations for the above.
* cli/cli-cmds.c: New file. GDB CLI commands.
(error_no_arg, info_command, show_command, help_command, show_version,
quit_command, pwd_command, cd_command, echo_command, shell_escape,
make_command, show_user, set_debug, show_debug, init_cmd_lists):
Moved here from top.c.
(apropos_command): Moved here from command.c.
(complete_command, source_command): Moved here (part) from top.c.
(is_complete_command): New function. Checks if a command is the
"complete" command.
(init_cli_cmds): New function. Add commands to the CLI (from code
previously in top.c.
* cli/cli-cmds.h: Definitions/declarations for the above.
* cli/cli-script.c: New file. GDB CLI command scripting.
(build_command_line, get_command_line, print_command_lines,
print_command_line, execute_user_command, execute_control_command,
while_command, if_command, arg_cleanup, setup_user_args, locate_arg,
insert_args, realloc_body_list, read_next_line,
recurse_read_control_structure, read_command_lines, free_command_lines,
do_free_command_lines_cleanup, make_cleanup_free_command_lines,
validate_comname, user_defined_command, define_command,
document_command, source_cleanup_lines, do_fclose_cleanup,
show_user_1): Moved here from top.c.
(script_from_file): New function. Implements execution of a script
contained in a file (part of code for the source_command() that used
to exist in top.c).
* cli/cli-script.h: Definitions/declarations for the above.
* cli/cli-setshow.c: New file. Handle set and show GDB CLI commands.
(parse_auto_binary_operation, parse_binary_operation,
do_setshow_command, cmd_show_list): Moved here from command.c.
* cli/cli-setshow.h: Definitions/declarations for the above.
* top.c: Remove all CLI code, except the command loop.
(gdb_init): Call init_cli_cmds().
* command.c: Remove obsolete file.
* command.h: Mark as DEPRECATED.
* gdbcmd.h: Ditto.
* call-cmds.h: Ditto.
* Makefile.in (SFILES): Remove command.c.
(COMMON_OBS): Remove command.o.
(command.o): Remove obsolete target.
(cli_decode_h, cli_cmds_h, cli_script_h, cli_setshow_h): New macros.
Refer to CLI header files.
(cli-decode.o, cli-cmds.o, cli-setshow.o, cli-script.o): New targets.
(SUBDIR_CLI_OBS, SUBDIR_CLI_SRCS, SUBDIR_CLI_DEPS, SUBDIR_CLI_INITS,
SUBDIR_CLI_LDFLAGS, SUBDIR_CLI_CFLAGS, SUBDIR_CLI_ALL, SUBDIR_CLI_CLEAN,
SUBDIR_CLI_INSTALL, SUBDIR_CLI_UNINSTALL): New macros for new cli
subdirectory.
* configure.in (enable_gdbcli): New option. Include the CLI in the
executable (cannot be disabled yet).
(CONFIG_OBS, CONFIG_DEPS, CONFIG_SRCS, CONFIG_INITS, ENABLE_CFLAGS,
CONFIG_ALL, CONFIG_CLEAN, CONFIG_INSTALL, CONFIG_UNINSTALL): Add
the corresponding SUBDIR_CLI_* macros if CLI requested.
* configure: Regenerate.
Diffstat (limited to 'gdb/cli')
-rw-r--r-- | gdb/cli/cli-cmds.c | 801 | ||||
-rw-r--r-- | gdb/cli/cli-cmds.h | 125 | ||||
-rw-r--r-- | gdb/cli/cli-decode.c | 1492 | ||||
-rw-r--r-- | gdb/cli/cli-decode.h | 357 | ||||
-rw-r--r-- | gdb/cli/cli-script.c | 1315 | ||||
-rw-r--r-- | gdb/cli/cli-script.h | 50 | ||||
-rw-r--r-- | gdb/cli/cli-setshow.c | 462 | ||||
-rw-r--r-- | gdb/cli/cli-setshow.h | 32 |
8 files changed, 4634 insertions, 0 deletions
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c new file mode 100644 index 0000000..557e1fd --- /dev/null +++ b/gdb/cli/cli-cmds.c @@ -0,0 +1,801 @@ +/* GDB CLI commands. + Copyright 2000 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 "completer.h" +#include "target.h" /* For baud_rate, remote_debug and remote_timeout */ +#include "gdb_wait.h" /* For shell escape implementation */ +#include "gnu-regex.h" /* Used by apropos_command */ + +#ifdef UI_OUT +#include "ui-out.h" +#endif + +#include "top.h" +#include "cli/cli-decode.h" +#include "cli/cli-script.h" +#include "cli/cli-setshow.h" +#include "cli/cli-cmds.h" + +#ifndef GDBINIT_FILENAME +#define GDBINIT_FILENAME ".gdbinit" +#endif + +/* FIXME: this should be auto-configured! */ +#ifdef __MSDOS__ +# define CANT_FORK +#endif + +/* From gdb/top.c */ + +extern void dont_repeat (void); + +extern void set_verbose (char *, int, struct cmd_list_element *); + +extern void show_history (char *, int); + +extern void set_history (char *, int); + +extern void show_commands (char *, int); + +/* Prototypes for local functions */ + +static void complete_command (char *, int); + +static void echo_command (char *, int); + +static void pwd_command (char *, int); + +static void show_version (char *, int); + +static void validate_comname (char *); + +static void help_command (char *, int); + +static void show_command (char *, int); + +static void info_command (char *, int); + +static void show_debug (char *, int); + +static void set_debug (char *, int); + +static void show_user (char *, int); + +static void make_command (char *, int); + +static void shell_escape (char *, int); + +void apropos_command (char *, int); + +/* Define all cmd_list_elements. */ + +/* Chain containing all defined commands. */ + +struct cmd_list_element *cmdlist; + +/* Chain containing all defined info subcommands. */ + +struct cmd_list_element *infolist; + +/* Chain containing all defined enable subcommands. */ + +struct cmd_list_element *enablelist; + +/* Chain containing all defined disable subcommands. */ + +struct cmd_list_element *disablelist; + +/* Chain containing all defined toggle subcommands. */ + +struct cmd_list_element *togglelist; + +/* Chain containing all defined stop subcommands. */ + +struct cmd_list_element *stoplist; + +/* Chain containing all defined delete subcommands. */ + +struct cmd_list_element *deletelist; + +/* Chain containing all defined "enable breakpoint" subcommands. */ + +struct cmd_list_element *enablebreaklist; + +/* Chain containing all defined set subcommands */ + +struct cmd_list_element *setlist; + +/* Chain containing all defined unset subcommands */ + +struct cmd_list_element *unsetlist; + +/* Chain containing all defined show subcommands. */ + +struct cmd_list_element *showlist; + +/* Chain containing all defined \"set history\". */ + +struct cmd_list_element *sethistlist; + +/* Chain containing all defined \"show history\". */ + +struct cmd_list_element *showhistlist; + +/* Chain containing all defined \"unset history\". */ + +struct cmd_list_element *unsethistlist; + +/* Chain containing all defined maintenance subcommands. */ + +struct cmd_list_element *maintenancelist; + +/* Chain containing all defined "maintenance info" subcommands. */ + +struct cmd_list_element *maintenanceinfolist; + +/* Chain containing all defined "maintenance print" subcommands. */ + +struct cmd_list_element *maintenanceprintlist; + +struct cmd_list_element *setprintlist; + +struct cmd_list_element *showprintlist; + +struct cmd_list_element *setdebuglist; + +struct cmd_list_element *showdebuglist; + +struct cmd_list_element *setchecklist; + +struct cmd_list_element *showchecklist; + +/* Utility used everywhere when at least one argument is needed and + none is supplied. */ + +void +error_no_arg (char *why) +{ + error ("Argument required (%s).", why); +} + +/* The "info" command is defined as a prefix, with allow_unknown = 0. + Therefore, its own definition is called only for "info" with no args. */ + +/* ARGSUSED */ +static void +info_command (char *arg, int from_tty) +{ + printf_unfiltered ("\"info\" must be followed by the name of an info command.\n"); + help_list (infolist, "info ", -1, gdb_stdout); +} + +/* The "show" command with no arguments shows all the settings. */ + +/* ARGSUSED */ +static void +show_command (char *arg, int from_tty) +{ + cmd_show_list (showlist, from_tty, ""); +} + +/* Provide documentation on command or list given by COMMAND. FROM_TTY + is ignored. */ + +/* ARGSUSED */ +static void +help_command (char *command, int from_tty) +{ + help_cmd (command, gdb_stdout); +} + +/* The "complete" command is used by Emacs to implement completion. */ + +/* ARGSUSED */ +static void +complete_command (char *arg, int from_tty) +{ + int i; + int argpoint; + char *completion; + + dont_repeat (); + + if (arg == NULL) + arg = ""; + argpoint = strlen (arg); + + for (completion = line_completion_function (arg, i = 0, arg, argpoint); + completion; + completion = line_completion_function (arg, ++i, arg, argpoint)) + { + printf_unfiltered ("%s\n", completion); + free (completion); + } +} + +int is_complete_command (void (*func) (char *args, int from_tty)) +{ + return func == complete_command; +} + +/* ARGSUSED */ +static void +show_version (char *args, int from_tty) +{ + immediate_quit++; + print_gdb_version (gdb_stdout); + printf_filtered ("\n"); + immediate_quit--; +} + +/* Handle the quit command. */ + +void +quit_command (char *args, int from_tty) +{ + if (!quit_confirm ()) + error ("Not confirmed."); + quit_force (args, from_tty); +} + +/* ARGSUSED */ +static void +pwd_command (char *args, int from_tty) +{ + if (args) + error ("The \"pwd\" command does not take an argument: %s", args); + getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); + + if (!STREQ (gdb_dirbuf, current_directory)) + printf_unfiltered ("Working directory %s\n (canonically %s).\n", + current_directory, gdb_dirbuf); + else + printf_unfiltered ("Working directory %s.\n", current_directory); +} + +void +cd_command (char *dir, int from_tty) +{ + int len; + /* Found something other than leading repetitions of "/..". */ + int found_real_path; + char *p; + + /* If the new directory is absolute, repeat is a no-op; if relative, + repeat might be useful but is more likely to be a mistake. */ + dont_repeat (); + + if (dir == 0) + error_no_arg ("new working directory"); + + dir = tilde_expand (dir); + make_cleanup (free, dir); + + if (chdir (dir) < 0) + perror_with_name (dir); + +#if defined(_WIN32) || defined(__MSDOS__) + /* There's too much mess with DOSish names like "d:", "d:.", + "d:./foo" etc. Instead of having lots of special #ifdef'ed code, + simply get the canonicalized name of the current directory. */ + dir = getcwd (gdb_dirbuf, sizeof (gdb_dirbuf)); +#endif + + len = strlen (dir); + if (SLASH_P (dir[len - 1])) + { + /* Remove the trailing slash unless this is a root directory + (including a drive letter on non-Unix systems). */ + if (!(len == 1) /* "/" */ +#if defined(_WIN32) || defined(__MSDOS__) + && !(!SLASH_P (*dir) && ROOTED_P (dir) && len <= 3) /* "d:/" */ +#endif + ) + len--; + } + + dir = savestring (dir, len); + if (ROOTED_P (dir)) + current_directory = dir; + else + { + if (SLASH_P (current_directory[strlen (current_directory) - 1])) + current_directory = concat (current_directory, dir, NULL); + else + current_directory = concat (current_directory, SLASH_STRING, dir, NULL); + free (dir); + } + + /* Now simplify any occurrences of `.' and `..' in the pathname. */ + + found_real_path = 0; + for (p = current_directory; *p;) + { + if (SLASH_P (p[0]) && p[1] == '.' && (p[2] == 0 || SLASH_P (p[2]))) + strcpy (p, p + 2); + else if (SLASH_P (p[0]) && p[1] == '.' && p[2] == '.' + && (p[3] == 0 || SLASH_P (p[3]))) + { + if (found_real_path) + { + /* Search backwards for the directory just before the "/.." + and obliterate it and the "/..". */ + char *q = p; + while (q != current_directory && !SLASH_P (q[-1])) + --q; + + if (q == current_directory) + /* current_directory is + a relative pathname ("can't happen"--leave it alone). */ + ++p; + else + { + strcpy (q - 1, p + 3); + p = q - 1; + } + } + else + /* We are dealing with leading repetitions of "/..", for example + "/../..", which is the Mach super-root. */ + p += 3; + } + else + { + found_real_path = 1; + ++p; + } + } + + forget_cached_source_info (); + + if (from_tty) + pwd_command ((char *) 0, 1); +} + +void +source_command (char *args, int from_tty) +{ + FILE *stream; + struct cleanup *old_cleanups; + char *file = args; + + if (file == NULL) + { + error ("source command requires pathname of file to source."); + } + + file = tilde_expand (file); + old_cleanups = make_cleanup (free, file); + + stream = fopen (file, FOPEN_RT); + if (!stream) + { + if (from_tty) + perror_with_name (file); + else + return; + } + + script_from_file (stream, file); + + do_cleanups (old_cleanups); +} + +/* ARGSUSED */ +static void +echo_command (char *text, int from_tty) +{ + char *p = text; + register int c; + + if (text) + while ((c = *p++) != '\0') + { + if (c == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + if (*p == 0) + return; + + c = parse_escape (&p); + if (c >= 0) + printf_filtered ("%c", c); + } + else + printf_filtered ("%c", c); + } + + /* Force this output to appear now. */ + wrap_here (""); + gdb_flush (gdb_stdout); +} + +/* ARGSUSED */ +static void +shell_escape (char *arg, int from_tty) +{ +#ifdef CANT_FORK + /* If ARG is NULL, they want an inferior shell, but `system' just + reports if the shell is available when passed a NULL arg. */ + int rc = system (arg ? arg : ""); + + if (!arg) + arg = "inferior shell"; + + if (rc == -1) + { + fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", arg, + safe_strerror (errno)); + gdb_flush (gdb_stderr); + } + else if (rc) + { + fprintf_unfiltered (gdb_stderr, "%s exited with status %d\n", arg, rc); + gdb_flush (gdb_stderr); + } +#ifdef __DJGPP__ + /* Make sure to return to the directory GDB thinks it is, in case the + shell command we just ran changed it. */ + chdir (current_directory); +#endif +#else /* Can fork. */ + int rc, status, pid; + char *p, *user_shell; + + if ((user_shell = (char *) getenv ("SHELL")) == NULL) + user_shell = "/bin/sh"; + + /* Get the name of the shell for arg0 */ + if ((p = strrchr (user_shell, '/')) == NULL) + p = user_shell; + else + p++; /* Get past '/' */ + + if ((pid = fork ()) == 0) + { + if (!arg) + execl (user_shell, p, 0); + else + execl (user_shell, p, "-c", arg, 0); + + fprintf_unfiltered (gdb_stderr, "Cannot execute %s: %s\n", user_shell, + safe_strerror (errno)); + gdb_flush (gdb_stderr); + _exit (0177); + } + + if (pid != -1) + while ((rc = wait (&status)) != pid && rc != -1) + ; + else + error ("Fork failed"); +#endif /* Can fork. */ +} + +static void +make_command (char *arg, int from_tty) +{ + char *p; + + if (arg == 0) + p = "make"; + else + { + p = xmalloc (sizeof ("make ") + strlen (arg)); + strcpy (p, "make "); + strcpy (p + sizeof ("make ") - 1, arg); + } + + shell_escape (p, from_tty); +} + +/* ARGSUSED */ +static void +show_user (char *args, int from_tty) +{ + struct cmd_list_element *c; + extern struct cmd_list_element *cmdlist; + + if (args) + { + c = lookup_cmd (&args, cmdlist, "", 0, 1); + if (c->class != class_user) + error ("Not a user command."); + show_user_1 (c, gdb_stdout); + } + else + { + for (c = cmdlist; c; c = c->next) + { + if (c->class == class_user) + show_user_1 (c, gdb_stdout); + } + } +} + +/* Search through names of commands and documentations for a certain + regular expression. +*/ +void +apropos_command (char *searchstr, int from_tty) +{ + extern struct cmd_list_element *cmdlist; /*This is the main command list*/ + regex_t pattern; + char *pattern_fastmap; + char errorbuffer[512]; + pattern_fastmap=calloc(256,sizeof(char)); + if (searchstr == NULL) + error("REGEXP string is empty"); + + if (regcomp(&pattern,searchstr,REG_ICASE) == 0) + { + pattern.fastmap=pattern_fastmap; + re_compile_fastmap(&pattern); + apropos_cmd (gdb_stdout,cmdlist,&pattern,""); + } + else + { + regerror(regcomp(&pattern,searchstr,REG_ICASE),NULL,errorbuffer,512); + error("Error in regular expression:%s",errorbuffer); + } + free(pattern_fastmap); +} + +static void +set_debug (char *arg, int from_tty) +{ + printf_unfiltered ("\"set debug\" must be followed by the name of a print subcommand.\n"); + help_list (setdebuglist, "set debug ", -1, gdb_stdout); +} + +static void +show_debug (char *args, int from_tty) +{ + cmd_show_list (showdebuglist, from_tty, ""); +} + +void +init_cmd_lists (void) +{ + cmdlist = NULL; + infolist = NULL; + enablelist = NULL; + disablelist = NULL; + togglelist = NULL; + stoplist = NULL; + deletelist = NULL; + enablebreaklist = NULL; + setlist = NULL; + unsetlist = NULL; + showlist = NULL; + sethistlist = NULL; + showhistlist = NULL; + unsethistlist = NULL; + maintenancelist = NULL; + maintenanceinfolist = NULL; + maintenanceprintlist = NULL; + setprintlist = NULL; + showprintlist = NULL; + setchecklist = NULL; + showchecklist = NULL; +} + + +void +init_cli_cmds (void) +{ + struct cmd_list_element *c; + + /* Define the classes of commands. + They will appear in the help list in the reverse of this order. */ + + add_cmd ("internals", class_maintenance, NO_FUNCTION, + "Maintenance commands.\n\ +Some gdb commands are provided just for use by gdb maintainers.\n\ +These commands are subject to frequent change, and may not be as\n\ +well documented as user commands.", + &cmdlist); + add_cmd ("obscure", class_obscure, NO_FUNCTION, "Obscure features.", &cmdlist); + add_cmd ("aliases", class_alias, NO_FUNCTION, "Aliases of other commands.", &cmdlist); + add_cmd ("user-defined", class_user, NO_FUNCTION, "User-defined commands.\n\ +The commands in this class are those defined by the user.\n\ +Use the \"define\" command to define a command.", &cmdlist); + add_cmd ("support", class_support, NO_FUNCTION, "Support facilities.", &cmdlist); + if (!dbx_commands) + add_cmd ("status", class_info, NO_FUNCTION, "Status inquiries.", &cmdlist); + add_cmd ("files", class_files, NO_FUNCTION, "Specifying and examining files.", &cmdlist); + add_cmd ("breakpoints", class_breakpoint, NO_FUNCTION, "Making program stop at certain points.", &cmdlist); + add_cmd ("data", class_vars, NO_FUNCTION, "Examining data.", &cmdlist); + add_cmd ("stack", class_stack, NO_FUNCTION, "Examining the stack.\n\ +The stack is made up of stack frames. Gdb assigns numbers to stack frames\n\ +counting from zero for the innermost (currently executing) frame.\n\n\ +At any time gdb identifies one frame as the \"selected\" frame.\n\ +Variable lookups are done with respect to the selected frame.\n\ +When the program being debugged stops, gdb selects the innermost frame.\n\ +The commands below can be used to select other frames by number or address.", + &cmdlist); + add_cmd ("running", class_run, NO_FUNCTION, "Running the program.", &cmdlist); + + /* Define general commands. */ + + add_com ("pwd", class_files, pwd_command, + "Print working directory. This is used for your program as well."); + c = add_cmd ("cd", class_files, cd_command, + "Set working directory to DIR for debugger and program being debugged.\n\ +The change does not take effect for the program being debugged\n\ +until the next time it is started.", &cmdlist); + c->completer = filename_completer; + + add_com ("echo", class_support, echo_command, + "Print a constant string. Give string as argument.\n\ +C escape sequences may be used in the argument.\n\ +No newline is added at the end of the argument;\n\ +use \"\\n\" if you want a newline to be printed.\n\ +Since leading and trailing whitespace are ignored in command arguments,\n\ +if you want to print some you must use \"\\\" before leading whitespace\n\ +to be printed or after trailing whitespace."); + add_com ("document", class_support, document_command, + "Document a user-defined command.\n\ +Give command name as argument. Give documentation on following lines.\n\ +End with a line of just \"end\"."); + add_com ("define", class_support, define_command, + "Define a new command name. Command name is argument.\n\ +Definition appears on following lines, one command per line.\n\ +End with a line of just \"end\".\n\ +Use the \"document\" command to give documentation for the new command.\n\ +Commands defined in this way may have up to ten arguments."); + +#ifdef __STDC__ + c = add_cmd ("source", class_support, source_command, + "Read commands from a file named FILE.\n\ +Note that the file \"" GDBINIT_FILENAME "\" is read automatically in this way\n\ +when gdb is started.", &cmdlist); +#else + /* Punt file name, we can't help it easily. */ + c = add_cmd ("source", class_support, source_command, + "Read commands from a file named FILE.\n\ +Note that the file \".gdbinit\" is read automatically in this way\n\ +when gdb is started.", &cmdlist); +#endif + c->completer = filename_completer; + + add_com ("quit", class_support, quit_command, "Exit gdb."); + add_com ("help", class_support, help_command, "Print list of commands."); + add_com_alias ("q", "quit", class_support, 1); + add_com_alias ("h", "help", class_support, 1); + + c = add_set_cmd ("verbose", class_support, var_boolean, (char *) &info_verbose, + "Set ", + &setlist), + add_show_from_set (c, &showlist); + c->function.sfunc = set_verbose; + set_verbose (NULL, 0, c); + + add_prefix_cmd ("history", class_support, set_history, + "Generic command for setting command history parameters.", + &sethistlist, "set history ", 0, &setlist); + add_prefix_cmd ("history", class_support, show_history, + "Generic command for showing command history parameters.", + &showhistlist, "show history ", 0, &showlist); + + add_show_from_set + (add_set_cmd ("expansion", no_class, var_boolean, (char *) &history_expansion_p, + "Set history expansion on command input.\n\ +Without an argument, history expansion is enabled.", &sethistlist), + &showhistlist); + + add_prefix_cmd ("info", class_info, info_command, + "Generic command for showing things about the program being debugged.", + &infolist, "info ", 0, &cmdlist); + add_com_alias ("i", "info", class_info, 1); + + add_com ("complete", class_obscure, complete_command, + "List the completions for the rest of the line as a command."); + + add_prefix_cmd ("show", class_info, show_command, + "Generic command for showing things about the debugger.", + &showlist, "show ", 0, &cmdlist); + /* Another way to get at the same thing. */ + add_info ("set", show_command, "Show all GDB settings."); + + add_cmd ("commands", no_class, show_commands, + "Show the history of commands you typed.\n\ +You can supply a command number to start with, or a `+' to start after\n\ +the previous command number shown.", + &showlist); + + add_cmd ("version", no_class, show_version, + "Show what version of GDB this is.", &showlist); + + add_com ("while", class_support, while_command, + "Execute nested commands WHILE the conditional expression is non zero.\n\ +The conditional expression must follow the word `while' and must in turn be\n\ +followed by a new line. The nested commands must be entered one per line,\n\ +and should be terminated by the word `end'."); + + add_com ("if", class_support, if_command, + "Execute nested commands once IF the conditional expression is non zero.\n\ +The conditional expression must follow the word `if' and must in turn be\n\ +followed by a new line. The nested commands must be entered one per line,\n\ +and should be terminated by the word 'else' or `end'. If an else clause\n\ +is used, the same rules apply to its nested commands as to the first ones."); + + /* If target is open when baud changes, it doesn't take effect until the + next open (I think, not sure). */ + add_show_from_set (add_set_cmd ("remotebaud", no_class, + var_zinteger, (char *) &baud_rate, + "Set baud rate for remote serial I/O.\n\ +This value is used to set the speed of the serial port when debugging\n\ +using remote targets.", &setlist), + &showlist); + + c = add_set_cmd ("remotedebug", no_class, var_zinteger, + (char *) &remote_debug, + "Set debugging of remote protocol.\n\ +When enabled, each packet sent or received with the remote target\n\ +is displayed.", &setlist); + deprecate_cmd (c, "set debug remote"); + deprecate_cmd (add_show_from_set (c, &showlist), "show debug remote"); + + add_show_from_set (add_set_cmd ("remote", no_class, var_zinteger, + (char *) &remote_debug, + "Set debugging of remote protocol.\n\ +When enabled, each packet sent or received with the remote target\n\ +is displayed.", &setdebuglist), + &showdebuglist); + + add_show_from_set ( + add_set_cmd ("remotetimeout", no_class, var_integer, (char *) &remote_timeout, + "Set timeout limit to wait for target to respond.\n\ +This value is used to set the time limit for gdb to wait for a response\n\ +from the target.", &setlist), + &showlist); + + add_prefix_cmd ("debug", no_class, set_debug, + "Generic command for setting gdb debugging flags", + &setdebuglist, "set debug ", 0, &setlist); + + add_prefix_cmd ("debug", no_class, show_debug, + "Generic command for showing gdb debugging flags", + &showdebuglist, "show debug ", 0, &showlist); + + add_com ("shell", class_support, shell_escape, + "Execute the rest of the line as a shell command. \n\ +With no arguments, run an inferior shell."); + + /* NOTE: cagney/2000-03-20: Being able to enter ``(gdb) !ls'' would + be a really useful feature. Unfortunately, the below wont do + this. Instead it adds support for the form ``(gdb) ! ls'' + (i.e. the space is required). If the ``!'' command below is + added the complains about no ``!'' command would be replaced by + complains about how the ``!'' command is broken :-) */ + if (xdb_commands) + add_com_alias ("!", "shell", class_support, 0); + + add_com ("make", class_support, make_command, + "Run the ``make'' program using the rest of the line as arguments."); + add_cmd ("user", no_class, show_user, + "Show definitions of user defined commands.\n\ +Argument is the name of the user defined command.\n\ +With no argument, show definitions of all user defined commands.", &showlist); + add_com ("apropos", class_support, apropos_command, "Search for commands matching a REGEXP"); +} diff --git a/gdb/cli/cli-cmds.h b/gdb/cli/cli-cmds.h new file mode 100644 index 0000000..906277e --- /dev/null +++ b/gdb/cli/cli-cmds.h @@ -0,0 +1,125 @@ +/* Header file for GDB CLI command implementation library. + Copyright (C) 2000 Free Software Foundation, Inc. + + 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. */ + +#if !defined (CLI_CMDS_H) +#define CLI_CMDS_H 1 + +/* Chain containing all defined commands. */ + +extern struct cmd_list_element *cmdlist; + +/* Chain containing all defined info subcommands. */ + +extern struct cmd_list_element *infolist; + +/* Chain containing all defined enable subcommands. */ + +extern struct cmd_list_element *enablelist; + +/* Chain containing all defined disable subcommands. */ + +extern struct cmd_list_element *disablelist; + +/* Chain containing all defined delete subcommands. */ + +extern struct cmd_list_element *deletelist; + +/* Chain containing all defined toggle subcommands. */ + +extern struct cmd_list_element *togglelist; + +/* Chain containing all defined stop subcommands. */ + +extern struct cmd_list_element *stoplist; + +/* Chain containing all defined "enable breakpoint" subcommands. */ + +extern struct cmd_list_element *enablebreaklist; + +/* Chain containing all defined set subcommands */ + +extern struct cmd_list_element *setlist; + +/* Chain containing all defined unset subcommands */ + +extern struct cmd_list_element *unsetlist; + +/* Chain containing all defined show subcommands. */ + +extern struct cmd_list_element *showlist; + +/* Chain containing all defined \"set history\". */ + +extern struct cmd_list_element *sethistlist; + +/* Chain containing all defined \"show history\". */ + +extern struct cmd_list_element *showhistlist; + +/* Chain containing all defined \"unset history\". */ + +extern struct cmd_list_element *unsethistlist; + +/* Chain containing all defined maintenance subcommands. */ + +extern struct cmd_list_element *maintenancelist; + +/* Chain containing all defined "maintenance info" subcommands. */ + +extern struct cmd_list_element *maintenanceinfolist; + +/* Chain containing all defined "maintenance print" subcommands. */ + +extern struct cmd_list_element *maintenanceprintlist; + +extern struct cmd_list_element *setprintlist; + +extern struct cmd_list_element *showprintlist; + +extern struct cmd_list_element *setdebuglist; + +extern struct cmd_list_element *showdebuglist; + +extern struct cmd_list_element *setchecklist; + +extern struct cmd_list_element *showchecklist; + +/* Exported to gdb/top.c */ + +void init_cmd_lists (void); + +void init_cli_cmds (void); + +int is_complete_command (void (*func) (char *args, int from_tty)); + +/* Exported to gdb/main.c */ + +extern void cd_command (char *, int); + +/* Exported to gdb/top.c and gdb/main.c */ + +extern void quit_command (char *, int); + +extern void source_command (char *, int); + +/* Used everywhere whenever at least one parameter is required and + none is specified. */ + +extern NORETURN void error_no_arg (char *) ATTR_NORETURN; + +#endif /* !defined (CLI_CMDS_H) */ diff --git a/gdb/cli/cli-decode.c b/gdb/cli/cli-decode.c new file mode 100644 index 0000000..a09da91 --- /dev/null +++ b/gdb/cli/cli-decode.c @@ -0,0 +1,1492 @@ +/* Handle lists of commands, their decoding and documentation, for GDB. + Copyright 1986, 1989, 1990, 1991, 1998, 2000 Free Software Foundation, Inc. + + 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 "symtab.h" +#include <ctype.h> +#include "gnu-regex.h" + +#ifdef UI_OUT +#include "ui-out.h" +#endif + +#include "cli/cli-cmds.h" +#include "cli/cli-decode.h" + +/* Prototypes for local functions */ + +static void undef_cmd_error (char *, char *); + +static struct cmd_list_element *find_cmd (char *command, + int len, + struct cmd_list_element *clist, + int ignore_help_classes, + int *nfound); + +static void help_all (struct ui_file *stream); + +/* Add element named NAME. + CLASS is the top level category into which commands are broken down + for "help" purposes. + FUN should be the function to execute the command; + it will get a character string as argument, with leading + and trailing blanks already eliminated. + + DOC is a documentation string for the command. + Its first line should be a complete sentence. + It should start with ? for a command that is an abbreviation + or with * for a command that most users don't need to know about. + + Add this command to command list *LIST. + + Returns a pointer to the added command (not necessarily the head + of *LIST). */ + +struct cmd_list_element * +add_cmd (char *name, enum command_class class, void (*fun) (char *, int), + char *doc, struct cmd_list_element **list) +{ + register struct cmd_list_element *c + = (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element)); + struct cmd_list_element *p; + + delete_cmd (name, list); + + if (*list == NULL || STRCMP ((*list)->name, name) >= 0) + { + c->next = *list; + *list = c; + } + else + { + p = *list; + while (p->next && STRCMP (p->next->name, name) <= 0) + { + p = p->next; + } + c->next = p->next; + p->next = c; + } + + c->name = name; + c->class = class; + c->function.cfunc = fun; + c->doc = doc; + c->flags = 0; + c->replacement = NULL; + c->hook_pre = NULL; + c->hook_post = NULL; + c->hook_in = 0; + c->prefixlist = NULL; + c->prefixname = NULL; + c->allow_unknown = 0; + c->abbrev_flag = 0; + c->completer = make_symbol_completion_list; + c->type = not_set_cmd; + c->var = NULL; + c->var_type = var_boolean; + c->enums = NULL; + c->user_commands = NULL; + c->hookee_pre = NULL; + c->hookee_post = NULL; + c->cmd_pointer = NULL; + + return c; +} + + +/* Deprecates a command CMD. + REPLACEMENT is the name of the command which should be used in place + of this command, or NULL if no such command exists. + + This function does not check to see if command REPLACEMENT exists + since gdb may not have gotten around to adding REPLACEMENT when this + function is called. + + Returns a pointer to the deprecated command. */ + +struct cmd_list_element * +deprecate_cmd (struct cmd_list_element *cmd, char *replacement) +{ + cmd->flags |= (CMD_DEPRECATED | DEPRECATED_WARN_USER); + + if (replacement != NULL) + cmd->replacement = replacement; + else + cmd->replacement = NULL; + + return cmd; +} + + +/* Same as above, except that the abbrev_flag is set. */ + +#if 0 /* Currently unused */ + +struct cmd_list_element * +add_abbrev_cmd (char *name, enum command_class class, void (*fun) (char *, int), + char *doc, struct cmd_list_element **list) +{ + register struct cmd_list_element *c + = add_cmd (name, class, fun, doc, list); + + c->abbrev_flag = 1; + return c; +} + +#endif + +struct cmd_list_element * +add_alias_cmd (char *name, char *oldname, enum command_class class, + int abbrev_flag, struct cmd_list_element **list) +{ + /* Must do this since lookup_cmd tries to side-effect its first arg */ + char *copied_name; + register struct cmd_list_element *old; + register struct cmd_list_element *c; + copied_name = (char *) alloca (strlen (oldname) + 1); + strcpy (copied_name, oldname); + old = lookup_cmd (&copied_name, *list, "", 1, 1); + + if (old == 0) + { + delete_cmd (name, list); + return 0; + } + + c = add_cmd (name, class, old->function.cfunc, old->doc, list); + c->prefixlist = old->prefixlist; + c->prefixname = old->prefixname; + c->allow_unknown = old->allow_unknown; + c->abbrev_flag = abbrev_flag; + c->cmd_pointer = old; + return c; +} + +/* Like add_cmd but adds an element for a command prefix: + a name that should be followed by a subcommand to be looked up + in another command list. PREFIXLIST should be the address + of the variable containing that list. */ + +struct cmd_list_element * +add_prefix_cmd (char *name, enum command_class class, void (*fun) (char *, int), + char *doc, struct cmd_list_element **prefixlist, + char *prefixname, int allow_unknown, + struct cmd_list_element **list) +{ + register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list); + c->prefixlist = prefixlist; + c->prefixname = prefixname; + c->allow_unknown = allow_unknown; + return c; +} + +/* Like add_prefix_cmd but sets the abbrev_flag on the new command. */ + +struct cmd_list_element * +add_abbrev_prefix_cmd (char *name, enum command_class class, + void (*fun) (char *, int), char *doc, + struct cmd_list_element **prefixlist, char *prefixname, + int allow_unknown, struct cmd_list_element **list) +{ + register struct cmd_list_element *c = add_cmd (name, class, fun, doc, list); + c->prefixlist = prefixlist; + c->prefixname = prefixname; + c->allow_unknown = allow_unknown; + c->abbrev_flag = 1; + return c; +} + +/* This is an empty "cfunc". */ +void +not_just_help_class_command (char *args, int from_tty) +{ +} + +/* This is an empty "sfunc". */ +static void empty_sfunc (char *, int, struct cmd_list_element *); + +static void +empty_sfunc (char *args, int from_tty, struct cmd_list_element *c) +{ +} + +/* Add element named NAME to command list LIST (the list for set + or some sublist thereof). + CLASS is as in add_cmd. + VAR_TYPE is the kind of thing we are setting. + VAR is address of the variable being controlled by this command. + DOC is the documentation string. */ + +struct cmd_list_element * +add_set_cmd (char *name, + enum command_class class, + var_types var_type, + void *var, + char *doc, + struct cmd_list_element **list) +{ + struct cmd_list_element *c + = add_cmd (name, class, NO_FUNCTION, doc, list); + + c->type = set_cmd; + c->var_type = var_type; + c->var = var; + /* This needs to be something besides NO_FUNCTION so that this isn't + treated as a help class. */ + c->function.sfunc = empty_sfunc; + return c; +} + +/* Add element named NAME to command list LIST (the list for set + or some sublist thereof). + CLASS is as in add_cmd. + ENUMLIST is a list of strings which may follow NAME. + VAR is address of the variable which will contain the matching string + (from ENUMLIST). + DOC is the documentation string. */ + +struct cmd_list_element * +add_set_enum_cmd (char *name, + enum command_class class, + const char *enumlist[], + const char **var, + char *doc, + struct cmd_list_element **list) +{ + struct cmd_list_element *c + = add_set_cmd (name, class, var_enum, var, doc, list); + c->enums = enumlist; + + return c; +} + +/* Add element named NAME to command list LIST (the list for set + or some sublist thereof). + CLASS is as in add_cmd. + VAR is address of the variable which will contain the value. + DOC is the documentation string. */ +struct cmd_list_element * +add_set_auto_boolean_cmd (char *name, + enum command_class class, + enum cmd_auto_boolean *var, + char *doc, + struct cmd_list_element **list) +{ + static const char *auto_boolean_enums[] = { "on", "off", "auto", NULL }; + struct cmd_list_element *c; + c = add_set_cmd (name, class, var_auto_boolean, var, doc, list); + c->enums = auto_boolean_enums; + return c; +} + +/* Where SETCMD has already been added, add the corresponding show + command to LIST and return a pointer to the added command (not + necessarily the head of LIST). */ +struct cmd_list_element * +add_show_from_set (struct cmd_list_element *setcmd, + struct cmd_list_element **list) +{ + struct cmd_list_element *showcmd = + (struct cmd_list_element *) xmalloc (sizeof (struct cmd_list_element)); + struct cmd_list_element *p; + + memcpy (showcmd, setcmd, sizeof (struct cmd_list_element)); + delete_cmd (showcmd->name, list); + showcmd->type = show_cmd; + + /* Replace "set " at start of docstring with "show ". */ + if (setcmd->doc[0] == 'S' && setcmd->doc[1] == 'e' + && setcmd->doc[2] == 't' && setcmd->doc[3] == ' ') + showcmd->doc = concat ("Show ", setcmd->doc + 4, NULL); + else + fprintf_unfiltered (gdb_stderr, "GDB internal error: Bad docstring for set command\n"); + + if (*list == NULL || STRCMP ((*list)->name, showcmd->name) >= 0) + { + showcmd->next = *list; + *list = showcmd; + } + else + { + p = *list; + while (p->next && STRCMP (p->next->name, showcmd->name) <= 0) + { + p = p->next; + } + showcmd->next = p->next; + p->next = showcmd; + } + + return showcmd; +} + +/* Remove the command named NAME from the command list. */ + +void +delete_cmd (char *name, struct cmd_list_element **list) +{ + register struct cmd_list_element *c; + struct cmd_list_element *p; + + while (*list && STREQ ((*list)->name, name)) + { + if ((*list)->hookee_pre) + (*list)->hookee_pre->hook_pre = 0; /* Hook slips out of its mouth */ + if ((*list)->hookee_post) + (*list)->hookee_post->hook_post = 0; /* Hook slips out of its bottom */ + p = (*list)->next; + free ((PTR) * list); + *list = p; + } + + if (*list) + for (c = *list; c->next;) + { + if (STREQ (c->next->name, name)) + { + if (c->next->hookee_pre) + c->next->hookee_pre->hook_pre = 0; /* hooked cmd gets away. */ + if (c->next->hookee_post) + c->next->hookee_post->hook_post = 0; /* remove post hook */ + /* :( no fishing metaphore */ + p = c->next->next; + free ((PTR) c->next); + c->next = p; + } + else + c = c->next; + } +} + +/* Shorthands to the commands above. */ + +/* Add an element to the list of info subcommands. */ + +struct cmd_list_element * +add_info (char *name, void (*fun) (char *, int), char *doc) +{ + return add_cmd (name, no_class, fun, doc, &infolist); +} + +/* Add an alias to the list of info subcommands. */ + +struct cmd_list_element * +add_info_alias (char *name, char *oldname, int abbrev_flag) +{ + return add_alias_cmd (name, oldname, 0, abbrev_flag, &infolist); +} + +/* Add an element to the list of commands. */ + +struct cmd_list_element * +add_com (char *name, enum command_class class, void (*fun) (char *, int), + char *doc) +{ + return add_cmd (name, class, fun, doc, &cmdlist); +} + +/* Add an alias or abbreviation command to the list of commands. */ + +struct cmd_list_element * +add_com_alias (char *name, char *oldname, enum command_class class, + int abbrev_flag) +{ + return add_alias_cmd (name, oldname, class, abbrev_flag, &cmdlist); +} + +/* Recursively walk the commandlist structures, and print out the + documentation of commands that match our regex in either their + name, or their documentation. +*/ +void +apropos_cmd (struct ui_file *stream, struct cmd_list_element *commandlist, + struct re_pattern_buffer *regex, char *prefix) +{ + register struct cmd_list_element *c; + int returnvalue=1; /*Needed to avoid double printing*/ + /* Walk through the commands */ + for (c=commandlist;c;c=c->next) + { + if (c->name != NULL) + { + /* Try to match against the name*/ + returnvalue=re_search(regex,c->name,strlen(c->name),0,strlen(c->name),NULL); + if (returnvalue >= 0) + { + /* Stolen from help_cmd_list. We don't directly use + * help_cmd_list because it doesn't let us print out + * single commands + */ + fprintf_filtered (stream, "%s%s -- ", prefix, c->name); + print_doc_line (stream, c->doc); + fputs_filtered ("\n", stream); + returnvalue=0; /*Set this so we don't print it again.*/ + } + } + if (c->doc != NULL && returnvalue != 0) + { + /* Try to match against documentation */ + if (re_search(regex,c->doc,strlen(c->doc),0,strlen(c->doc),NULL) >=0) + { + /* Stolen from help_cmd_list. We don't directly use + * help_cmd_list because it doesn't let us print out + * single commands + */ + fprintf_filtered (stream, "%s%s -- ", prefix, c->name); + print_doc_line (stream, c->doc); + fputs_filtered ("\n", stream); + } + } + /* Check if this command has subcommands */ + if (c->prefixlist != NULL) + { + /* Recursively call ourselves on the subcommand list, + passing the right prefix in. + */ + apropos_cmd (stream,*c->prefixlist,regex,c->prefixname); + } + } +} + +/* This command really has to deal with two things: + * 1) I want documentation on *this string* (usually called by + * "help commandname"). + * 2) I want documentation on *this list* (usually called by + * giving a command that requires subcommands. Also called by saying + * just "help".) + * + * I am going to split this into two seperate comamnds, help_cmd and + * help_list. + */ + +void +help_cmd (char *command, struct ui_file *stream) +{ + struct cmd_list_element *c; + extern struct cmd_list_element *cmdlist; + + if (!command) + { + help_list (cmdlist, "", all_classes, stream); + return; + } + + if (strcmp (command, "all") == 0) + { + help_all (stream); + return; + } + + c = lookup_cmd (&command, cmdlist, "", 0, 0); + + if (c == 0) + return; + + /* There are three cases here. + If c->prefixlist is nonzero, we have a prefix command. + Print its documentation, then list its subcommands. + + If c->function is nonzero, we really have a command. + Print its documentation and return. + + If c->function is zero, we have a class name. + Print its documentation (as if it were a command) + and then set class to the number of this class + so that the commands in the class will be listed. */ + + fputs_filtered (c->doc, stream); + fputs_filtered ("\n", stream); + + if (c->prefixlist == 0 && c->function.cfunc != NULL) + return; + fprintf_filtered (stream, "\n"); + + /* If this is a prefix command, print it's subcommands */ + if (c->prefixlist) + help_list (*c->prefixlist, c->prefixname, all_commands, stream); + + /* If this is a class name, print all of the commands in the class */ + if (c->function.cfunc == NULL) + help_list (cmdlist, "", c->class, stream); + + if (c->hook_pre || c->hook_post) + fprintf_filtered (stream, + "\nThis command has a hook (or hooks) defined:\n"); + + if (c->hook_pre) + fprintf_filtered (stream, + "\tThis command is run after : %s (pre hook)\n", + c->hook_pre->name); + if (c->hook_post) + fprintf_filtered (stream, + "\tThis command is run before : %s (post hook)\n", + c->hook_post->name); +} + +/* + * Get a specific kind of help on a command list. + * + * LIST is the list. + * CMDTYPE is the prefix to use in the title string. + * CLASS is the class with which to list the nodes of this list (see + * documentation for help_cmd_list below), As usual, ALL_COMMANDS for + * everything, ALL_CLASSES for just classes, and non-negative for only things + * in a specific class. + * and STREAM is the output stream on which to print things. + * If you call this routine with a class >= 0, it recurses. + */ +void +help_list (struct cmd_list_element *list, char *cmdtype, + enum command_class class, struct ui_file *stream) +{ + int len; + char *cmdtype1, *cmdtype2; + + /* If CMDTYPE is "foo ", CMDTYPE1 gets " foo" and CMDTYPE2 gets "foo sub" */ + len = strlen (cmdtype); + cmdtype1 = (char *) alloca (len + 1); + cmdtype1[0] = 0; + cmdtype2 = (char *) alloca (len + 4); + cmdtype2[0] = 0; + if (len) + { + cmdtype1[0] = ' '; + strncpy (cmdtype1 + 1, cmdtype, len - 1); + cmdtype1[len] = 0; + strncpy (cmdtype2, cmdtype, len - 1); + strcpy (cmdtype2 + len - 1, " sub"); + } + + if (class == all_classes) + fprintf_filtered (stream, "List of classes of %scommands:\n\n", cmdtype2); + else + fprintf_filtered (stream, "List of %scommands:\n\n", cmdtype2); + + help_cmd_list (list, class, cmdtype, (int) class >= 0, stream); + + if (class == all_classes) + fprintf_filtered (stream, "\n\ +Type \"help%s\" followed by a class name for a list of commands in that class.", + cmdtype1); + + fprintf_filtered (stream, "\n\ +Type \"help%s\" followed by %scommand name for full documentation.\n\ +Command name abbreviations are allowed if unambiguous.\n", + cmdtype1, cmdtype2); +} + +static void +help_all (struct ui_file *stream) +{ + struct cmd_list_element *c; + extern struct cmd_list_element *cmdlist; + + for (c = cmdlist; c; c = c->next) + { + if (c->abbrev_flag) + continue; + /* If this is a prefix command, print it's subcommands */ + if (c->prefixlist) + help_cmd_list (*c->prefixlist, all_commands, c->prefixname, 0, stream); + + /* If this is a class name, print all of the commands in the class */ + else if (c->function.cfunc == NULL) + help_cmd_list (cmdlist, c->class, "", 0, stream); + } +} + +/* Print only the first line of STR on STREAM. */ +void +print_doc_line (struct ui_file *stream, char *str) +{ + static char *line_buffer = 0; + static int line_size; + register char *p; + + if (!line_buffer) + { + line_size = 80; + line_buffer = (char *) xmalloc (line_size); + } + + p = str; + while (*p && *p != '\n' && *p != '.' && *p != ',') + p++; + if (p - str > line_size - 1) + { + line_size = p - str + 1; + free ((PTR) line_buffer); + line_buffer = (char *) xmalloc (line_size); + } + strncpy (line_buffer, str, p - str); + line_buffer[p - str] = '\0'; + if (islower (line_buffer[0])) + line_buffer[0] = toupper (line_buffer[0]); +#ifdef UI_OUT + ui_out_text (uiout, line_buffer); +#else + fputs_filtered (line_buffer, stream); +#endif +} + +/* + * Implement a help command on command list LIST. + * RECURSE should be non-zero if this should be done recursively on + * all sublists of LIST. + * PREFIX is the prefix to print before each command name. + * STREAM is the stream upon which the output should be written. + * CLASS should be: + * A non-negative class number to list only commands in that + * class. + * ALL_COMMANDS to list all commands in list. + * ALL_CLASSES to list all classes in list. + * + * Note that RECURSE will be active on *all* sublists, not just the + * ones selected by the criteria above (ie. the selection mechanism + * is at the low level, not the high-level). + */ +void +help_cmd_list (struct cmd_list_element *list, enum command_class class, + char *prefix, int recurse, struct ui_file *stream) +{ + register struct cmd_list_element *c; + + for (c = list; c; c = c->next) + { + if (c->abbrev_flag == 0 && + (class == all_commands + || (class == all_classes && c->function.cfunc == NULL) + || (class == c->class && c->function.cfunc != NULL))) + { + fprintf_filtered (stream, "%s%s -- ", prefix, c->name); + print_doc_line (stream, c->doc); + fputs_filtered ("\n", stream); + } + if (recurse + && c->prefixlist != 0 + && c->abbrev_flag == 0) + help_cmd_list (*c->prefixlist, class, c->prefixname, 1, stream); + } +} + + +/* Search the input clist for 'command'. Return the command if + found (or NULL if not), and return the number of commands + found in nfound */ + +static struct cmd_list_element * +find_cmd (char *command, int len, struct cmd_list_element *clist, + int ignore_help_classes, int *nfound) +{ + struct cmd_list_element *found, *c; + + found = (struct cmd_list_element *) NULL; + *nfound = 0; + for (c = clist; c; c = c->next) + if (!strncmp (command, c->name, len) + && (!ignore_help_classes || c->function.cfunc)) + { + found = c; + (*nfound)++; + if (c->name[len] == '\0') + { + *nfound = 1; + break; + } + } + return found; +} + +/* This routine takes a line of TEXT and a CLIST in which to start the + lookup. When it returns it will have incremented the text pointer past + the section of text it matched, set *RESULT_LIST to point to the list in + which the last word was matched, and will return a pointer to the cmd + list element which the text matches. It will return NULL if no match at + all was possible. It will return -1 (cast appropriately, ick) if ambigous + matches are possible; in this case *RESULT_LIST will be set to point to + the list in which there are ambiguous choices (and *TEXT will be set to + the ambiguous text string). + + If the located command was an abbreviation, this routine returns the base + command of the abbreviation. + + It does no error reporting whatsoever; control will always return + to the superior routine. + + In the case of an ambiguous return (-1), *RESULT_LIST will be set to point + at the prefix_command (ie. the best match) *or* (special case) will be NULL + if no prefix command was ever found. For example, in the case of "info a", + "info" matches without ambiguity, but "a" could be "args" or "address", so + *RESULT_LIST is set to the cmd_list_element for "info". So in this case + RESULT_LIST should not be interpeted as a pointer to the beginning of a + list; it simply points to a specific command. In the case of an ambiguous + return *TEXT is advanced past the last non-ambiguous prefix (e.g. + "info t" can be "info types" or "info target"; upon return *TEXT has been + advanced past "info "). + + If RESULT_LIST is NULL, don't set *RESULT_LIST (but don't otherwise + affect the operation). + + This routine does *not* modify the text pointed to by TEXT. + + If IGNORE_HELP_CLASSES is nonzero, ignore any command list elements which + are actually help classes rather than commands (i.e. the function field of + the struct cmd_list_element is NULL). */ + +struct cmd_list_element * +lookup_cmd_1 (char **text, struct cmd_list_element *clist, + struct cmd_list_element **result_list, int ignore_help_classes) +{ + char *p, *command; + int len, tmp, nfound; + struct cmd_list_element *found, *c; + char *line = *text; + + while (**text == ' ' || **text == '\t') + (*text)++; + + /* Treating underscores as part of command words is important + so that "set args_foo()" doesn't get interpreted as + "set args _foo()". */ + for (p = *text; + *p && (isalnum (*p) || *p == '-' || *p == '_' || + (tui_version && + (*p == '+' || *p == '<' || *p == '>' || *p == '$')) || + (xdb_commands && (*p == '!' || *p == '/' || *p == '?'))); + p++) + ; + + /* If nothing but whitespace, return 0. */ + if (p == *text) + return 0; + + len = p - *text; + + /* *text and p now bracket the first command word to lookup (and + it's length is len). We copy this into a local temporary */ + + + command = (char *) alloca (len + 1); + for (tmp = 0; tmp < len; tmp++) + { + char x = (*text)[tmp]; + command[tmp] = x; + } + command[len] = '\0'; + + /* Look it up. */ + found = 0; + nfound = 0; + found = find_cmd (command, len, clist, ignore_help_classes, &nfound); + + /* + ** We didn't find the command in the entered case, so lower case it + ** and search again. + */ + if (!found || nfound == 0) + { + for (tmp = 0; tmp < len; tmp++) + { + char x = command[tmp]; + command[tmp] = isupper (x) ? tolower (x) : x; + } + found = find_cmd (command, len, clist, ignore_help_classes, &nfound); + } + + /* If nothing matches, we have a simple failure. */ + if (nfound == 0) + return 0; + + if (nfound > 1) + { + if (result_list != NULL) + /* Will be modified in calling routine + if we know what the prefix command is. */ + *result_list = 0; + return (struct cmd_list_element *) -1; /* Ambiguous. */ + } + + /* We've matched something on this list. Move text pointer forward. */ + + *text = p; + + if (found->cmd_pointer) + { + /* We drop the alias (abbreviation) in favor of the command it is + pointing to. If the alias is deprecated, though, we need to + warn the user about it before we drop it. Note that while we + are warning about the alias, we may also warn about the command + itself and we will adjust the appropriate DEPRECATED_WARN_USER + flags */ + + if (found->flags & DEPRECATED_WARN_USER) + deprecated_cmd_warning (&line); + found = found->cmd_pointer; + } + /* If we found a prefix command, keep looking. */ + + if (found->prefixlist) + { + c = lookup_cmd_1 (text, *found->prefixlist, result_list, + ignore_help_classes); + if (!c) + { + /* Didn't find anything; this is as far as we got. */ + if (result_list != NULL) + *result_list = clist; + return found; + } + else if (c == (struct cmd_list_element *) -1) + { + /* We've gotten this far properly, but the next step + is ambiguous. We need to set the result list to the best + we've found (if an inferior hasn't already set it). */ + if (result_list != NULL) + if (!*result_list) + /* This used to say *result_list = *found->prefixlist + If that was correct, need to modify the documentation + at the top of this function to clarify what is supposed + to be going on. */ + *result_list = found; + return c; + } + else + { + /* We matched! */ + return c; + } + } + else + { + if (result_list != NULL) + *result_list = clist; + return found; + } +} + +/* All this hair to move the space to the front of cmdtype */ + +static void +undef_cmd_error (char *cmdtype, char *q) +{ + error ("Undefined %scommand: \"%s\". Try \"help%s%.*s\".", + cmdtype, + q, + *cmdtype ? " " : "", + strlen (cmdtype) - 1, + cmdtype); +} + +/* Look up the contents of *LINE as a command in the command list LIST. + LIST is a chain of struct cmd_list_element's. + If it is found, return the struct cmd_list_element for that command + and update *LINE to point after the command name, at the first argument. + If not found, call error if ALLOW_UNKNOWN is zero + otherwise (or if error returns) return zero. + Call error if specified command is ambiguous, + unless ALLOW_UNKNOWN is negative. + CMDTYPE precedes the word "command" in the error message. + + If INGNORE_HELP_CLASSES is nonzero, ignore any command list + elements which are actually help classes rather than commands (i.e. + the function field of the struct cmd_list_element is 0). */ + +struct cmd_list_element * +lookup_cmd (char **line, struct cmd_list_element *list, char *cmdtype, + int allow_unknown, int ignore_help_classes) +{ + struct cmd_list_element *last_list = 0; + struct cmd_list_element *c = + lookup_cmd_1 (line, list, &last_list, ignore_help_classes); +#if 0 + /* This is wrong for complete_command. */ + char *ptr = (*line) + strlen (*line) - 1; + + /* Clear off trailing whitespace. */ + while (ptr >= *line && (*ptr == ' ' || *ptr == '\t')) + ptr--; + *(ptr + 1) = '\0'; +#endif + + if (!c) + { + if (!allow_unknown) + { + if (!*line) + error ("Lack of needed %scommand", cmdtype); + else + { + char *p = *line, *q; + + while (isalnum (*p) || *p == '-') + p++; + + q = (char *) alloca (p - *line + 1); + strncpy (q, *line, p - *line); + q[p - *line] = '\0'; + undef_cmd_error (cmdtype, q); + } + } + else + return 0; + } + else if (c == (struct cmd_list_element *) -1) + { + /* Ambigous. Local values should be off prefixlist or called + values. */ + int local_allow_unknown = (last_list ? last_list->allow_unknown : + allow_unknown); + char *local_cmdtype = last_list ? last_list->prefixname : cmdtype; + struct cmd_list_element *local_list = + (last_list ? *(last_list->prefixlist) : list); + + if (local_allow_unknown < 0) + { + if (last_list) + return last_list; /* Found something. */ + else + return 0; /* Found nothing. */ + } + else + { + /* Report as error. */ + int amb_len; + char ambbuf[100]; + + for (amb_len = 0; + ((*line)[amb_len] && (*line)[amb_len] != ' ' + && (*line)[amb_len] != '\t'); + amb_len++) + ; + + ambbuf[0] = 0; + for (c = local_list; c; c = c->next) + if (!strncmp (*line, c->name, amb_len)) + { + if (strlen (ambbuf) + strlen (c->name) + 6 < (int) sizeof ambbuf) + { + if (strlen (ambbuf)) + strcat (ambbuf, ", "); + strcat (ambbuf, c->name); + } + else + { + strcat (ambbuf, ".."); + break; + } + } + error ("Ambiguous %scommand \"%s\": %s.", local_cmdtype, + *line, ambbuf); + return 0; /* lint */ + } + } + else + { + /* We've got something. It may still not be what the caller + wants (if this command *needs* a subcommand). */ + while (**line == ' ' || **line == '\t') + (*line)++; + + if (c->prefixlist && **line && !c->allow_unknown) + undef_cmd_error (c->prefixname, *line); + + /* Seems to be what he wants. Return it. */ + return c; + } + return 0; +} + +/* We are here presumably because an alias or command in *TEXT is + deprecated and a warning message should be generated. This function + decodes *TEXT and potentially generates a warning message as outlined + below. + + Example for 'set endian big' which has a fictitious alias 'seb'. + + If alias wasn't used in *TEXT, and the command is deprecated: + "warning: 'set endian big' is deprecated." + + If alias was used, and only the alias is deprecated: + "warning: 'seb' an alias for the command 'set endian big' is deprecated." + + If alias was used and command is deprecated (regardless of whether the + alias itself is deprecated: + + "warning: 'set endian big' (seb) is deprecated." + + After the message has been sent, clear the appropriate flags in the + command and/or the alias so the user is no longer bothered. + +*/ +void +deprecated_cmd_warning (char **text) +{ + struct cmd_list_element *alias = NULL; + struct cmd_list_element *prefix_cmd = NULL; + struct cmd_list_element *cmd = NULL; + struct cmd_list_element *c; + char *type; + + if (!lookup_cmd_composition (*text, &alias, &prefix_cmd, &cmd)) + /* return if text doesn't evaluate to a command */ + return; + + if (!((alias ? (alias->flags & DEPRECATED_WARN_USER) : 0) + || (cmd->flags & DEPRECATED_WARN_USER) ) ) + /* return if nothing is deprecated */ + return; + + printf_filtered ("Warning:"); + + if (alias && !(cmd->flags & CMD_DEPRECATED)) + printf_filtered (" '%s', an alias for the", alias->name); + + printf_filtered (" command '"); + + if (prefix_cmd) + printf_filtered ("%s", prefix_cmd->prefixname); + + printf_filtered ("%s", cmd->name); + + if (alias && (cmd->flags & CMD_DEPRECATED)) + printf_filtered ("' (%s) is deprecated.\n", alias->name); + else + printf_filtered ("' is deprecated.\n"); + + + /* if it is only the alias that is deprecated, we want to indicate the + new alias, otherwise we'll indicate the new command */ + + if (alias && !(cmd->flags & CMD_DEPRECATED)) + { + if (alias->replacement) + printf_filtered ("Use '%s'.\n\n", alias->replacement); + else + printf_filtered ("No alternative known.\n\n"); + } + else + { + if (cmd->replacement) + printf_filtered ("Use '%s'.\n\n", cmd->replacement); + else + printf_filtered ("No alternative known.\n\n"); + } + + /* We've warned you, now we'll keep quiet */ + if (alias) + alias->flags &= ~DEPRECATED_WARN_USER; + + cmd->flags &= ~DEPRECATED_WARN_USER; +} + + + +/* Look up the contents of LINE as a command in the command list 'cmdlist'. + Return 1 on success, 0 on failure. + + If LINE refers to an alias, *alias will point to that alias. + + If LINE is a postfix command (i.e. one that is preceeded by a prefix + command) set *prefix_cmd. + + Set *cmd to point to the command LINE indicates. + + If any of *alias, *prefix_cmd, or *cmd cannot be determined or do not + exist, they are NULL when we return. + +*/ +int +lookup_cmd_composition (char *text, + struct cmd_list_element **alias, + struct cmd_list_element **prefix_cmd, + struct cmd_list_element **cmd) +{ + char *p, *command; + int len, tmp, nfound; + struct cmd_list_element *cur_list; + struct cmd_list_element *prev_cmd; + *alias = NULL; + *prefix_cmd = NULL; + *cmd = NULL; + + cur_list = cmdlist; + + while (1) + { + /* Go through as many command lists as we need to + to find the command TEXT refers to. */ + + prev_cmd = *cmd; + + while (*text == ' ' || *text == '\t') + (text)++; + + /* Treating underscores as part of command words is important + so that "set args_foo()" doesn't get interpreted as + "set args _foo()". */ + for (p = text; + *p && (isalnum (*p) || *p == '-' || *p == '_' || + (tui_version && + (*p == '+' || *p == '<' || *p == '>' || *p == '$')) || + (xdb_commands && (*p == '!' || *p == '/' || *p == '?'))); + p++) + ; + + /* If nothing but whitespace, return. */ + if (p == text) + return 0; + + len = p - text; + + /* text and p now bracket the first command word to lookup (and + it's length is len). We copy this into a local temporary */ + + command = (char *) alloca (len + 1); + for (tmp = 0; tmp < len; tmp++) + { + char x = text[tmp]; + command[tmp] = x; + } + command[len] = '\0'; + + /* Look it up. */ + *cmd = 0; + nfound = 0; + *cmd = find_cmd (command, len, cur_list, 1, &nfound); + + /* We didn't find the command in the entered case, so lower case it + and search again. + */ + if (!*cmd || nfound == 0) + { + for (tmp = 0; tmp < len; tmp++) + { + char x = command[tmp]; + command[tmp] = isupper (x) ? tolower (x) : x; + } + *cmd = find_cmd (command, len, cur_list, 1, &nfound); + } + + if (*cmd == (struct cmd_list_element *) -1) + { + return 0; /* ambiguous */ + } + + if (*cmd == NULL) + return 0; /* nothing found */ + else + { + if ((*cmd)->cmd_pointer) + { + /* cmd was actually an alias, we note that an alias was used + (by assigning *alais) and we set *cmd. + */ + *alias = *cmd; + *cmd = (*cmd)->cmd_pointer; + } + *prefix_cmd = prev_cmd; + } + if ((*cmd)->prefixlist) + cur_list = *(*cmd)->prefixlist; + else + return 1; + + text = p; + } +} + + + + +#if 0 +/* Look up the contents of *LINE as a command in the command list LIST. + LIST is a chain of struct cmd_list_element's. + If it is found, return the struct cmd_list_element for that command + and update *LINE to point after the command name, at the first argument. + If not found, call error if ALLOW_UNKNOWN is zero + otherwise (or if error returns) return zero. + Call error if specified command is ambiguous, + unless ALLOW_UNKNOWN is negative. + CMDTYPE precedes the word "command" in the error message. */ + +struct cmd_list_element * +lookup_cmd (char **line, struct cmd_list_element *list, char *cmdtype, + int allow_unknown) +{ + register char *p; + register struct cmd_list_element *c, *found; + int nfound; + char ambbuf[100]; + char *processed_cmd; + int i, cmd_len; + + /* Skip leading whitespace. */ + + while (**line == ' ' || **line == '\t') + (*line)++; + + /* Clear out trailing whitespace. */ + + p = *line + strlen (*line); + while (p != *line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = 0; + + /* Find end of command name. */ + + p = *line; + while (*p == '-' || isalnum (*p)) + p++; + + /* Look up the command name. + If exact match, keep that. + Otherwise, take command abbreviated, if unique. Note that (in my + opinion) a null string does *not* indicate ambiguity; simply the + end of the argument. */ + + if (p == *line) + { + if (!allow_unknown) + error ("Lack of needed %scommand", cmdtype); + return 0; + } + + /* Copy over to a local buffer, converting to lowercase on the way. + This is in case the command being parsed is a subcommand which + doesn't match anything, and that's ok. We want the original + untouched for the routine of the original command. */ + + processed_cmd = (char *) alloca (p - *line + 1); + for (cmd_len = 0; cmd_len < p - *line; cmd_len++) + { + char x = (*line)[cmd_len]; + if (isupper (x)) + processed_cmd[cmd_len] = tolower (x); + else + processed_cmd[cmd_len] = x; + } + processed_cmd[cmd_len] = '\0'; + + /* Check all possibilities in the current command list. */ + found = 0; + nfound = 0; + for (c = list; c; c = c->next) + { + if (!strncmp (processed_cmd, c->name, cmd_len)) + { + found = c; + nfound++; + if (c->name[cmd_len] == 0) + { + nfound = 1; + break; + } + } + } + + /* Report error for undefined command name. */ + + if (nfound != 1) + { + if (nfound > 1 && allow_unknown >= 0) + { + ambbuf[0] = 0; + for (c = list; c; c = c->next) + if (!strncmp (processed_cmd, c->name, cmd_len)) + { + if (strlen (ambbuf) + strlen (c->name) + 6 < sizeof ambbuf) + { + if (strlen (ambbuf)) + strcat (ambbuf, ", "); + strcat (ambbuf, c->name); + } + else + { + strcat (ambbuf, ".."); + break; + } + } + error ("Ambiguous %scommand \"%s\": %s.", cmdtype, + processed_cmd, ambbuf); + } + else if (!allow_unknown) + error ("Undefined %scommand: \"%s\".", cmdtype, processed_cmd); + return 0; + } + + /* Skip whitespace before the argument. */ + + while (*p == ' ' || *p == '\t') + p++; + *line = p; + + if (found->prefixlist && *p) + { + c = lookup_cmd (line, *found->prefixlist, found->prefixname, + found->allow_unknown); + if (c) + return c; + } + + return found; +} +#endif + +/* Helper function for SYMBOL_COMPLETION_FUNCTION. */ + +/* Return a vector of char pointers which point to the different + possible completions in LIST of TEXT. + + WORD points in the same buffer as TEXT, and completions should be + returned relative to this position. For example, suppose TEXT is "foo" + and we want to complete to "foobar". If WORD is "oo", return + "oobar"; if WORD is "baz/foo", return "baz/foobar". */ + +char ** +complete_on_cmdlist (struct cmd_list_element *list, char *text, char *word) +{ + struct cmd_list_element *ptr; + char **matchlist; + int sizeof_matchlist; + int matches; + int textlen = strlen (text); + + sizeof_matchlist = 10; + matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *)); + matches = 0; + + for (ptr = list; ptr; ptr = ptr->next) + if (!strncmp (ptr->name, text, textlen) + && !ptr->abbrev_flag + && (ptr->function.cfunc + || ptr->prefixlist)) + { + if (matches == sizeof_matchlist) + { + sizeof_matchlist *= 2; + matchlist = (char **) xrealloc ((char *) matchlist, + (sizeof_matchlist + * sizeof (char *))); + } + + matchlist[matches] = (char *) + xmalloc (strlen (word) + strlen (ptr->name) + 1); + if (word == text) + strcpy (matchlist[matches], ptr->name); + else if (word > text) + { + /* Return some portion of ptr->name. */ + strcpy (matchlist[matches], ptr->name + (word - text)); + } + else + { + /* Return some of text plus ptr->name. */ + strncpy (matchlist[matches], word, text - word); + matchlist[matches][text - word] = '\0'; + strcat (matchlist[matches], ptr->name); + } + ++matches; + } + + if (matches == 0) + { + free ((PTR) matchlist); + matchlist = 0; + } + else + { + matchlist = (char **) xrealloc ((char *) matchlist, ((matches + 1) + * sizeof (char *))); + matchlist[matches] = (char *) 0; + } + + return matchlist; +} + +/* Helper function for SYMBOL_COMPLETION_FUNCTION. */ + +/* Return a vector of char pointers which point to the different + possible completions in CMD of TEXT. + + WORD points in the same buffer as TEXT, and completions should be + returned relative to this position. For example, suppose TEXT is "foo" + and we want to complete to "foobar". If WORD is "oo", return + "oobar"; if WORD is "baz/foo", return "baz/foobar". */ + +char ** +complete_on_enum (const char *enumlist[], + char *text, + char *word) +{ + char **matchlist; + int sizeof_matchlist; + int matches; + int textlen = strlen (text); + int i; + const char *name; + + sizeof_matchlist = 10; + matchlist = (char **) xmalloc (sizeof_matchlist * sizeof (char *)); + matches = 0; + + for (i = 0; (name = enumlist[i]) != NULL; i++) + if (strncmp (name, text, textlen) == 0) + { + if (matches == sizeof_matchlist) + { + sizeof_matchlist *= 2; + matchlist = (char **) xrealloc ((char *) matchlist, + (sizeof_matchlist + * sizeof (char *))); + } + + matchlist[matches] = (char *) + xmalloc (strlen (word) + strlen (name) + 1); + if (word == text) + strcpy (matchlist[matches], name); + else if (word > text) + { + /* Return some portion of name. */ + strcpy (matchlist[matches], name + (word - text)); + } + else + { + /* Return some of text plus name. */ + strncpy (matchlist[matches], word, text - word); + matchlist[matches][text - word] = '\0'; + strcat (matchlist[matches], name); + } + ++matches; + } + + if (matches == 0) + { + free ((PTR) matchlist); + matchlist = 0; + } + else + { + matchlist = (char **) xrealloc ((char *) matchlist, ((matches + 1) + * sizeof (char *))); + matchlist[matches] = (char *) 0; + } + + return matchlist; +} + diff --git a/gdb/cli/cli-decode.h b/gdb/cli/cli-decode.h new file mode 100644 index 0000000..a456e9a --- /dev/null +++ b/gdb/cli/cli-decode.h @@ -0,0 +1,357 @@ +/* Header file for GDB command decoding library. + Copyright (C) 2000 Free Software Foundation, Inc. + + 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. */ + +#if !defined (CLI_DECODE_H) +#define CLI_DECODE_H 1 + +#include "gnu-regex.h" /* Needed by apropos_cmd. */ + +/* Command classes are top-level categories into which commands are broken + down for "help" purposes. + Notes on classes: class_alias is for alias commands which are not + abbreviations of the original command. class-pseudo is for + commands which are not really commands nor help topics ("stop"). */ + +enum command_class +{ + /* Special args to help_list */ + class_deprecated, all_classes = -2, all_commands = -1, + /* Classes of commands */ + no_class = -1, class_run = 0, class_vars, class_stack, + class_files, class_support, class_info, class_breakpoint, class_trace, + class_alias, class_obscure, class_user, class_maintenance, + class_pseudo, class_tui, class_xdb +}; + +/* Not a set/show command. Note that some commands which begin with + "set" or "show" might be in this category, if their syntax does + not fall into one of the following categories. */ +typedef enum cmd_types + { + not_set_cmd, + set_cmd, + show_cmd + } +cmd_types; + +/* Reasonable values for an AUTO_BOOLEAN variable. */ +enum cmd_auto_boolean +{ + CMD_AUTO_BOOLEAN_TRUE, + CMD_AUTO_BOOLEAN_FALSE, + CMD_AUTO_BOOLEAN_AUTO +}; + +/* Types of "set" or "show" command. */ +typedef enum var_types + { + /* "on" or "off". *VAR is an integer which is nonzero for on, + zero for off. */ + var_boolean, + + /* "on" / "true" / "enable" or "off" / "false" / "disable" or + "auto. *VAR is an ``enum cmd_auto_boolean''. NOTE: In general + a custom show command will need to be implemented - one that + for "auto" prints both the "auto" and the current auto-selected + value. */ + var_auto_boolean, + + /* Unsigned Integer. *VAR is an unsigned int. The user can type 0 + to mean "unlimited", which is stored in *VAR as UINT_MAX. */ + var_uinteger, + + /* Like var_uinteger but signed. *VAR is an int. The user can type 0 + to mean "unlimited", which is stored in *VAR as INT_MAX. */ + var_integer, + + /* String which the user enters with escapes (e.g. the user types \n and + it is a real newline in the stored string). + *VAR is a malloc'd string, or NULL if the string is empty. */ + var_string, + /* String which stores what the user types verbatim. + *VAR is a malloc'd string, or NULL if the string is empty. */ + var_string_noescape, + /* String which stores a filename. + *VAR is a malloc'd string, or NULL if the string is empty. */ + var_filename, + /* ZeroableInteger. *VAR is an int. Like Unsigned Integer except + that zero really means zero. */ + var_zinteger, + /* Enumerated type. Can only have one of the specified values. *VAR is a + char pointer to the name of the element that we find. */ + var_enum + } +var_types; + +/* This structure records one command'd definition. */ + + +/* This flag is used by the code executing commands to warn the user + the first time a deprecated command is used, see the 'flags' field in + the following struct. +*/ +#define CMD_DEPRECATED 0x1 +#define DEPRECATED_WARN_USER 0x2 +#define MALLOCED_REPLACEMENT 0x4 + +struct cmd_list_element + { + /* Points to next command in this list. */ + struct cmd_list_element *next; + + /* Name of this command. */ + char *name; + + /* Command class; class values are chosen by application program. */ + enum command_class class; + + /* Function definition of this command. + NO_FUNCTION for command class names and for help topics that + are not really commands. */ + union + { + /* If type is not_set_cmd, call it like this: */ + void (*cfunc) (char *args, int from_tty); + + /* If type is cmd_set or show_cmd, first set the variables, and + then call this. */ + void (*sfunc) (char *args, int from_tty, struct cmd_list_element * c); + } + function; +#define NO_FUNCTION ((void (*) (char *args, int from_tty)) 0) + + /* Documentation of this command (or help topic). + First line is brief documentation; remaining lines form, with it, + the full documentation. First line should end with a period. + Entire string should also end with a period, not a newline. */ + char *doc; + + /* flags : a bitfield + + bit 0: (LSB) CMD_DEPRECATED, when 1 indicated that this command + is deprecated. It may be removed from gdb's command set in the + future. + + bit 1: DEPRECATED_WARN_USER, the user needs to be warned that + this is a deprecated command. The user should only be warned + the first time a command is used. + + bit 2: MALLOCED_REPLACEMENT, when functions are deprecated at + compile time (this is the way it should, in general, be done) + the memory containing the replacement string is statically + allocated. In some cases it makes sense to deprecate commands + at runtime (the testsuite is one example). In this case the + memory for replacement is malloc'ed. When a command is + undeprecated or re-deprecated at runtime we don't want to risk + calling free on statically allocated memory, so we check this + flag. + */ + int flags; + + /* if this command is deprecated, this is the replacement name */ + char *replacement; + + /* Hook for another command to be executed before this command. */ + struct cmd_list_element *hook_pre; + + /* Hook for another command to be executed after this command. */ + struct cmd_list_element *hook_post; + + /* Flag that specifies if this command is already running it's hook. */ + /* Prevents the possibility of hook recursion. */ + int hook_in; + + /* Nonzero identifies a prefix command. For them, the address + of the variable containing the list of subcommands. */ + struct cmd_list_element **prefixlist; + + /* For prefix commands only: + String containing prefix commands to get here: this one + plus any others needed to get to it. Should end in a space. + It is used before the word "command" in describing the + commands reached through this prefix. */ + char *prefixname; + + /* For prefix commands only: + nonzero means do not get an error if subcommand is not + recognized; call the prefix's own function in that case. */ + char allow_unknown; + + /* Nonzero says this is an abbreviation, and should not + be mentioned in lists of commands. + This allows "br<tab>" to complete to "break", which it + otherwise wouldn't. */ + char abbrev_flag; + + /* Completion routine for this command. TEXT is the text beyond + what was matched for the command itself (leading whitespace is + skipped). It stops where we are supposed to stop completing + (rl_point) and is '\0' terminated. + + Return value is a malloc'd vector of pointers to possible completions + terminated with NULL. If there are no completions, returning a pointer + to a NULL would work but returning NULL itself is also valid. + WORD points in the same buffer as TEXT, and completions should be + returned relative to this position. For example, suppose TEXT is "foo" + and we want to complete to "foobar". If WORD is "oo", return + "oobar"; if WORD is "baz/foo", return "baz/foobar". */ + char **(*completer) (char *text, char *word); + + /* Type of "set" or "show" command (or SET_NOT_SET if not "set" + or "show"). */ + cmd_types type; + + /* Pointer to variable affected by "set" and "show". Doesn't matter + if type is not_set. */ + void *var; + + /* What kind of variable is *VAR? */ + var_types var_type; + + /* Pointer to NULL terminated list of enumerated values (like argv). */ + const char **enums; + + /* Pointer to command strings of user-defined commands */ + struct command_line *user_commands; + + /* Pointer to command that is hooked by this one, (by hook_pre) + so the hook can be removed when this one is deleted. */ + struct cmd_list_element *hookee_pre; + + /* Pointer to command that is hooked by this one, (by hook_post) + so the hook can be removed when this one is deleted. */ + struct cmd_list_element *hookee_post; + + /* Pointer to command that is aliased by this one, so the + aliased command can be located in case it has been hooked. */ + struct cmd_list_element *cmd_pointer; + }; + +/* API to the manipulation of command lists. */ + +extern struct cmd_list_element *add_cmd (char *, enum command_class, + void (*fun) (char *, int), char *, + struct cmd_list_element **); + +extern struct cmd_list_element *add_alias_cmd (char *, char *, + enum command_class, int, + struct cmd_list_element **); + +extern struct cmd_list_element *add_prefix_cmd (char *, enum command_class, + void (*fun) (char *, int), + char *, + struct cmd_list_element **, + char *, int, + struct cmd_list_element **); + +extern struct cmd_list_element *add_abbrev_prefix_cmd (char *, + enum command_class, + void (*fun) (char *, + int), + char *, + struct cmd_list_element + **, char *, int, + struct cmd_list_element + **); + +extern struct cmd_list_element *lookup_cmd (char **, + struct cmd_list_element *, char *, + int, int); + +extern struct cmd_list_element *lookup_cmd_1 (char **, + struct cmd_list_element *, + struct cmd_list_element **, + int); + +extern struct cmd_list_element * + deprecate_cmd (struct cmd_list_element *, char * ); + +extern void + deprecated_cmd_warning (char **); + +extern int + lookup_cmd_composition (char *text, + struct cmd_list_element **alias, + struct cmd_list_element **prefix_cmd, + struct cmd_list_element **cmd); + +extern struct cmd_list_element *add_com (char *, enum command_class, + void (*fun) (char *, int), char *); + +extern struct cmd_list_element *add_com_alias (char *, char *, + enum command_class, int); + +extern struct cmd_list_element *add_info (char *, void (*fun) (char *, int), + char *); + +extern struct cmd_list_element *add_info_alias (char *, char *, int); + +extern char **complete_on_cmdlist (struct cmd_list_element *, char *, char *); + +extern char **complete_on_enum (const char *enumlist[], char *, char *); + +extern void delete_cmd (char *, struct cmd_list_element **); + +extern void help_cmd_list (struct cmd_list_element *, enum command_class, + char *, int, struct ui_file *); + +extern struct cmd_list_element *add_set_cmd (char *name, enum + command_class class, + var_types var_type, void *var, + char *doc, + struct cmd_list_element **list); + +extern struct cmd_list_element *add_set_enum_cmd (char *name, + enum command_class class, + const char *enumlist[], + const char **var, + char *doc, + struct cmd_list_element **list); + +extern struct cmd_list_element *add_set_auto_boolean_cmd (char *name, + enum command_class class, + enum cmd_auto_boolean *var, + char *doc, + struct cmd_list_element **list); + +extern struct cmd_list_element *add_show_from_set (struct cmd_list_element *, + struct cmd_list_element + **); + +/* Functions that implement commands about CLI commands. */ + +extern void help_cmd (char *, struct ui_file *); + +extern void help_list (struct cmd_list_element *, char *, + enum command_class, struct ui_file *); + +extern void apropos_cmd (struct ui_file *, struct cmd_list_element *, + struct re_pattern_buffer *, char *); + +/* Used to mark commands that don't do anything. If we just leave the + function field NULL, the command is interpreted as a help topic, or + as a class of commands. */ + +extern void not_just_help_class_command (char *arg, int from_tty); + +/* Exported to cli/cli-setshow.c */ + +extern void print_doc_line (struct ui_file *, char *); + + +#endif /* !defined (CLI_DECODE_H) */ diff --git a/gdb/cli/cli-script.c b/gdb/cli/cli-script.c new file mode 100644 index 0000000..67a6515 --- /dev/null +++ b/gdb/cli/cli-script.c @@ -0,0 +1,1315 @@ +/* GDB CLI command scripting. + Copyright 1986-2000 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 "language.h" /* For value_true */ +#include <ctype.h> + +#ifdef UI_OUT +#include "ui-out.h" +#endif + +#include "top.h" +#include "cli/cli-cmds.h" +#include "cli/cli-decode.h" +#include "cli/cli-script.h" + +/* From gdb/top.c */ + +extern void dont_repeat (void); + +extern void do_restore_instream_cleanup (void *stream); + +/* Prototypes for local functions */ + +static struct cleanup * + make_cleanup_free_command_lines (struct command_line **arg); + +static enum command_control_type + recurse_read_control_structure (struct command_line *current_cmd); + +static char *insert_args (char *line); + +static struct cleanup * setup_user_args (char *p); + +static void validate_comname (char *); + +/* Level of control structure. */ +static int control_level; + +/* Source command state variable. */ +static int source_error_allocated; + +/* Structure for arguments to user defined functions. */ +#define MAXUSERARGS 10 +struct user_args + { + struct user_args *next; + struct + { + char *arg; + int len; + } + a[MAXUSERARGS]; + int count; + } + *user_args; + + +/* Allocate, initialize a new command line structure for one of the + control commands (if/while). */ + +static struct command_line * +build_command_line (enum command_control_type type, char *args) +{ + struct command_line *cmd; + + if (args == NULL) + error ("if/while commands require arguments.\n"); + + cmd = (struct command_line *) xmalloc (sizeof (struct command_line)); + cmd->next = NULL; + cmd->control_type = type; + + cmd->body_count = 1; + cmd->body_list + = (struct command_line **) xmalloc (sizeof (struct command_line *) + * cmd->body_count); + memset (cmd->body_list, 0, sizeof (struct command_line *) * cmd->body_count); + cmd->line = savestring (args, strlen (args)); + return cmd; +} + +/* Build and return a new command structure for the control commands + such as "if" and "while". */ + +static struct command_line * +get_command_line (enum command_control_type type, char *arg) +{ + struct command_line *cmd; + struct cleanup *old_chain = NULL; + + /* Allocate and build a new command line structure. */ + cmd = build_command_line (type, arg); + + old_chain = make_cleanup_free_command_lines (&cmd); + + /* Read in the body of this command. */ + if (recurse_read_control_structure (cmd) == invalid_control) + { + warning ("error reading in control structure\n"); + do_cleanups (old_chain); + return NULL; + } + + discard_cleanups (old_chain); + return cmd; +} + +/* Recursively print a command (including full control structures). */ +#ifdef UI_OUT +void +print_command_lines (struct ui_out *uiout, struct command_line *cmd, + unsigned int depth) +{ + struct command_line *list; + + list = cmd; + while (list) + { + + if (depth) + ui_out_spaces (uiout, 2 * depth); + + /* A simple command, print it and continue. */ + if (list->control_type == simple_control) + { + ui_out_field_string (uiout, NULL, list->line); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* loop_continue to jump to the start of a while loop, print it + and continue. */ + if (list->control_type == continue_control) + { + ui_out_field_string (uiout, NULL, "loop_continue"); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* loop_break to break out of a while loop, print it and continue. */ + if (list->control_type == break_control) + { + ui_out_field_string (uiout, NULL, "loop_break"); + ui_out_text (uiout, "\n"); + list = list->next; + continue; + } + + /* A while command. Recursively print its subcommands and continue. */ + if (list->control_type == while_control) + { + ui_out_text (uiout, "while "); + ui_out_field_fmt (uiout, NULL, "while %s", list->line); + ui_out_text (uiout, "\n"); + print_command_lines (uiout, *list->body_list, depth + 1); + ui_out_field_string (uiout, NULL, "end"); + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_text (uiout, "end\n"); + list = list->next; + continue; + } + + /* An if command. Recursively print both arms before continueing. */ + if (list->control_type == if_control) + { + ui_out_text (uiout, "if "); + ui_out_field_fmt (uiout, NULL, "if %s", list->line); + ui_out_text (uiout, "\n"); + /* The true arm. */ + print_command_lines (uiout, list->body_list[0], depth + 1); + + /* Show the false arm if it exists. */ + if (list->body_count == 2) + { + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_field_string (uiout, NULL, "else"); + ui_out_text (uiout, "else\n"); + print_command_lines (uiout, list->body_list[1], depth + 1); + } + + ui_out_field_string (uiout, NULL, "end"); + if (depth) + ui_out_spaces (uiout, 2 * depth); + ui_out_text (uiout, "end\n"); + list = list->next; + continue; + } + + /* ignore illegal command type and try next */ + list = list->next; + } /* while (list) */ +} +#else +void +print_command_line (struct command_line *cmd, unsigned int depth, + struct ui_file *stream) +{ + unsigned int i; + + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + + /* A simple command, print it and return. */ + if (cmd->control_type == simple_control) + { + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + return; + } + + /* loop_continue to jump to the start of a while loop, print it + and return. */ + if (cmd->control_type == continue_control) + { + fputs_filtered ("loop_continue\n", stream); + return; + } + + /* loop_break to break out of a while loop, print it and return. */ + if (cmd->control_type == break_control) + { + fputs_filtered ("loop_break\n", stream); + return; + } + + /* A while command. Recursively print its subcommands before returning. */ + if (cmd->control_type == while_control) + { + struct command_line *list; + fputs_filtered ("while ", stream); + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + list = *cmd->body_list; + while (list) + { + print_command_line (list, depth + 1, stream); + list = list->next; + } + } + + /* An if command. Recursively print both arms before returning. */ + if (cmd->control_type == if_control) + { + fputs_filtered ("if ", stream); + fputs_filtered (cmd->line, stream); + fputs_filtered ("\n", stream); + /* The true arm. */ + print_command_line (cmd->body_list[0], depth + 1, stream); + + /* Show the false arm if it exists. */ + if (cmd->body_count == 2) + { + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + fputs_filtered ("else\n", stream); + print_command_line (cmd->body_list[1], depth + 1, stream); + } + if (depth) + { + for (i = 0; i < depth; i++) + fputs_filtered (" ", stream); + } + fputs_filtered ("end\n", stream); + } +} +#endif + +/* Execute the command in CMD. */ + +void +execute_user_command (struct cmd_list_element *c, char *args) +{ + register struct command_line *cmdlines; + struct cleanup *old_chain; + enum command_control_type ret; + + old_chain = setup_user_args (args); + + cmdlines = c->user_commands; + if (cmdlines == 0) + /* Null command */ + return; + + /* Set the instream to 0, indicating execution of a + user-defined function. */ + old_chain = make_cleanup (do_restore_instream_cleanup, instream); + instream = (FILE *) 0; + while (cmdlines) + { + ret = execute_control_command (cmdlines); + if (ret != simple_control && ret != break_control) + { + warning ("Error in control structure.\n"); + break; + } + cmdlines = cmdlines->next; + } + do_cleanups (old_chain); +} + +enum command_control_type +execute_control_command (struct command_line *cmd) +{ + struct expression *expr; + struct command_line *current; + struct cleanup *old_chain = 0; + value_ptr val; + value_ptr val_mark; + int loop; + enum command_control_type ret; + char *new_line; + + switch (cmd->control_type) + { + case simple_control: + /* A simple command, execute it and return. */ + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + execute_command (new_line, 0); + ret = cmd->control_type; + break; + + case continue_control: + case break_control: + /* Return for "continue", and "break" so we can either + continue the loop at the top, or break out. */ + ret = cmd->control_type; + break; + + case while_control: + { + /* Parse the loop control expression for the while statement. */ + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + expr = parse_expression (new_line); + make_cleanup (free_current_contents, &expr); + + ret = simple_control; + loop = 1; + + /* Keep iterating so long as the expression is true. */ + while (loop == 1) + { + int cond_result; + + QUIT; + + /* Evaluate the expression. */ + val_mark = value_mark (); + val = evaluate_expression (expr); + cond_result = value_true (val); + value_free_to_mark (val_mark); + + /* If the value is false, then break out of the loop. */ + if (!cond_result) + break; + + /* Execute the body of the while statement. */ + current = *cmd->body_list; + while (current) + { + ret = execute_control_command (current); + + /* If we got an error, or a "break" command, then stop + looping. */ + if (ret == invalid_control || ret == break_control) + { + loop = 0; + break; + } + + /* If we got a "continue" command, then restart the loop + at this point. */ + if (ret == continue_control) + break; + + /* Get the next statement. */ + current = current->next; + } + } + + /* Reset RET so that we don't recurse the break all the way down. */ + if (ret == break_control) + ret = simple_control; + + break; + } + + case if_control: + { + new_line = insert_args (cmd->line); + if (!new_line) + return invalid_control; + old_chain = make_cleanup (free_current_contents, &new_line); + /* Parse the conditional for the if statement. */ + expr = parse_expression (new_line); + make_cleanup (free_current_contents, &expr); + + current = NULL; + ret = simple_control; + + /* Evaluate the conditional. */ + val_mark = value_mark (); + val = evaluate_expression (expr); + + /* Choose which arm to take commands from based on the value of the + conditional expression. */ + if (value_true (val)) + current = *cmd->body_list; + else if (cmd->body_count == 2) + current = *(cmd->body_list + 1); + value_free_to_mark (val_mark); + + /* Execute commands in the given arm. */ + while (current) + { + ret = execute_control_command (current); + + /* If we got an error, get out. */ + if (ret != simple_control) + break; + + /* Get the next statement in the body. */ + current = current->next; + } + + break; + } + + default: + warning ("Invalid control type in command structure."); + return invalid_control; + } + + if (old_chain) + do_cleanups (old_chain); + + return ret; +} + +/* "while" command support. Executes a body of statements while the + loop condition is nonzero. */ + +void +while_command (char *arg, int from_tty) +{ + struct command_line *command = NULL; + + control_level = 1; + command = get_command_line (while_control, arg); + + if (command == NULL) + return; + + execute_control_command (command); + free_command_lines (&command); +} + +/* "if" command support. Execute either the true or false arm depending + on the value of the if conditional. */ + +void +if_command (char *arg, int from_tty) +{ + struct command_line *command = NULL; + + control_level = 1; + command = get_command_line (if_control, arg); + + if (command == NULL) + return; + + execute_control_command (command); + free_command_lines (&command); +} + +/* Cleanup */ +static void +arg_cleanup (void *ignore) +{ + struct user_args *oargs = user_args; + if (!user_args) + internal_error ("Internal error, arg_cleanup called with no user args.\n"); + + user_args = user_args->next; + free (oargs); +} + +/* Bind the incomming arguments for a user defined command to + $arg0, $arg1 ... $argMAXUSERARGS. */ + +static struct cleanup * +setup_user_args (char *p) +{ + struct user_args *args; + struct cleanup *old_chain; + unsigned int arg_count = 0; + + args = (struct user_args *) xmalloc (sizeof (struct user_args)); + memset (args, 0, sizeof (struct user_args)); + + args->next = user_args; + user_args = args; + + old_chain = make_cleanup (arg_cleanup, 0/*ignored*/); + + if (p == NULL) + return old_chain; + + while (*p) + { + char *start_arg; + int squote = 0; + int dquote = 0; + int bsquote = 0; + + if (arg_count >= MAXUSERARGS) + { + error ("user defined function may only have %d arguments.\n", + MAXUSERARGS); + return old_chain; + } + + /* Strip whitespace. */ + while (*p == ' ' || *p == '\t') + p++; + + /* P now points to an argument. */ + start_arg = p; + user_args->a[arg_count].arg = p; + + /* Get to the end of this argument. */ + while (*p) + { + if (((*p == ' ' || *p == '\t')) && !squote && !dquote && !bsquote) + break; + else + { + if (bsquote) + bsquote = 0; + else if (*p == '\\') + bsquote = 1; + else if (squote) + { + if (*p == '\'') + squote = 0; + } + else if (dquote) + { + if (*p == '"') + dquote = 0; + } + else + { + if (*p == '\'') + squote = 1; + else if (*p == '"') + dquote = 1; + } + p++; + } + } + + user_args->a[arg_count].len = p - start_arg; + arg_count++; + user_args->count++; + } + return old_chain; +} + +/* Given character string P, return a point to the first argument ($arg), + or NULL if P contains no arguments. */ + +static char * +locate_arg (char *p) +{ + while ((p = strchr (p, '$'))) + { + if (strncmp (p, "$arg", 4) == 0 && isdigit (p[4])) + return p; + p++; + } + return NULL; +} + +/* Insert the user defined arguments stored in user_arg into the $arg + arguments found in line, with the updated copy being placed into nline. */ + +static char * +insert_args (char *line) +{ + char *p, *save_line, *new_line; + unsigned len, i; + + /* First we need to know how much memory to allocate for the new line. */ + save_line = line; + len = 0; + while ((p = locate_arg (line))) + { + len += p - line; + i = p[4] - '0'; + + if (i >= user_args->count) + { + error ("Missing argument %d in user function.\n", i); + return NULL; + } + len += user_args->a[i].len; + line = p + 5; + } + + /* Don't forget the tail. */ + len += strlen (line); + + /* Allocate space for the new line and fill it in. */ + new_line = (char *) xmalloc (len + 1); + if (new_line == NULL) + return NULL; + + /* Restore pointer to beginning of old line. */ + line = save_line; + + /* Save pointer to beginning of new line. */ + save_line = new_line; + + while ((p = locate_arg (line))) + { + int i, len; + + memcpy (new_line, line, p - line); + new_line += p - line; + i = p[4] - '0'; + + len = user_args->a[i].len; + if (len) + { + memcpy (new_line, user_args->a[i].arg, len); + new_line += len; + } + line = p + 5; + } + /* Don't forget the tail. */ + strcpy (new_line, line); + + /* Return a pointer to the beginning of the new line. */ + return save_line; +} + + +/* Expand the body_list of COMMAND so that it can hold NEW_LENGTH + code bodies. This is typically used when we encounter an "else" + clause for an "if" command. */ + +static void +realloc_body_list (struct command_line *command, int new_length) +{ + int n; + struct command_line **body_list; + + n = command->body_count; + + /* Nothing to do? */ + if (new_length <= n) + return; + + body_list = (struct command_line **) + xmalloc (sizeof (struct command_line *) * new_length); + + memcpy (body_list, command->body_list, sizeof (struct command_line *) * n); + + free (command->body_list); + command->body_list = body_list; + command->body_count = new_length; +} + +/* Read one line from the input stream. If the command is an "else" or + "end", return such an indication to the caller. */ + +static enum misc_command_type +read_next_line (struct command_line **command) +{ + char *p, *p1, *prompt_ptr, control_prompt[256]; + int i = 0; + + if (control_level >= 254) + error ("Control nesting too deep!\n"); + + /* Set a prompt based on the nesting of the control commands. */ + if (instream == stdin || (instream == 0 && readline_hook != NULL)) + { + for (i = 0; i < control_level; i++) + control_prompt[i] = ' '; + control_prompt[i] = '>'; + control_prompt[i + 1] = '\0'; + prompt_ptr = (char *) &control_prompt[0]; + } + else + prompt_ptr = NULL; + + p = command_line_input (prompt_ptr, instream == stdin, "commands"); + + /* Not sure what to do here. */ + if (p == NULL) + return end_command; + + /* Strip leading and trailing whitespace. */ + while (*p == ' ' || *p == '\t') + p++; + + p1 = p + strlen (p); + while (p1 != p && (p1[-1] == ' ' || p1[-1] == '\t')) + p1--; + + /* Blanks and comments don't really do anything, but we need to + distinguish them from else, end and other commands which can be + executed. */ + if (p1 == p || p[0] == '#') + return nop_command; + + /* Is this the end of a simple, while, or if control structure? */ + if (p1 - p == 3 && !strncmp (p, "end", 3)) + return end_command; + + /* Is the else clause of an if control structure? */ + if (p1 - p == 4 && !strncmp (p, "else", 4)) + return else_command; + + /* Check for while, if, break, continue, etc and build a new command + line structure for them. */ + if (p1 - p > 5 && !strncmp (p, "while", 5)) + *command = build_command_line (while_control, p + 6); + else if (p1 - p > 2 && !strncmp (p, "if", 2)) + *command = build_command_line (if_control, p + 3); + else if (p1 - p == 10 && !strncmp (p, "loop_break", 10)) + { + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = NULL; + (*command)->control_type = break_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + else if (p1 - p == 13 && !strncmp (p, "loop_continue", 13)) + { + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = NULL; + (*command)->control_type = continue_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + else + { + /* A normal command. */ + *command = (struct command_line *) + xmalloc (sizeof (struct command_line)); + (*command)->next = NULL; + (*command)->line = savestring (p, p1 - p); + (*command)->control_type = simple_control; + (*command)->body_count = 0; + (*command)->body_list = NULL; + } + + /* Nothing special. */ + return ok_command; +} + +/* Recursively read in the control structures and create a command_line + structure from them. + + The parent_control parameter is the control structure in which the + following commands are nested. */ + +static enum command_control_type +recurse_read_control_structure (struct command_line *current_cmd) +{ + int current_body, i; + enum misc_command_type val; + enum command_control_type ret; + struct command_line **body_ptr, *child_tail, *next; + + child_tail = NULL; + current_body = 1; + + /* Sanity checks. */ + if (current_cmd->control_type == simple_control) + { + error ("Recursed on a simple control type\n"); + return invalid_control; + } + + if (current_body > current_cmd->body_count) + { + error ("Allocated body is smaller than this command type needs\n"); + return invalid_control; + } + + /* Read lines from the input stream and build control structures. */ + while (1) + { + dont_repeat (); + + next = NULL; + val = read_next_line (&next); + + /* Just skip blanks and comments. */ + if (val == nop_command) + continue; + + if (val == end_command) + { + if (current_cmd->control_type == while_control + || current_cmd->control_type == if_control) + { + /* Success reading an entire control structure. */ + ret = simple_control; + break; + } + else + { + ret = invalid_control; + break; + } + } + + /* Not the end of a control structure. */ + if (val == else_command) + { + if (current_cmd->control_type == if_control + && current_body == 1) + { + realloc_body_list (current_cmd, 2); + current_body = 2; + child_tail = NULL; + continue; + } + else + { + ret = invalid_control; + break; + } + } + + if (child_tail) + { + child_tail->next = next; + } + else + { + body_ptr = current_cmd->body_list; + for (i = 1; i < current_body; i++) + body_ptr++; + + *body_ptr = next; + + } + + child_tail = next; + + /* If the latest line is another control structure, then recurse + on it. */ + if (next->control_type == while_control + || next->control_type == if_control) + { + control_level++; + ret = recurse_read_control_structure (next); + control_level--; + + if (ret != simple_control) + break; + } + } + + dont_repeat (); + + return ret; +} + +/* Read lines from the input stream and accumulate them in a chain of + struct command_line's, which is then returned. For input from a + terminal, the special command "end" is used to mark the end of the + input, and is not included in the returned chain of commands. */ + +#define END_MESSAGE "End with a line saying just \"end\"." + +struct command_line * +read_command_lines (char *prompt_arg, int from_tty) +{ + struct command_line *head, *tail, *next; + struct cleanup *old_chain; + enum command_control_type ret; + enum misc_command_type val; + + control_level = 0; + if (readline_begin_hook) + { + /* Note - intentional to merge messages with no newline */ + (*readline_begin_hook) ("%s %s\n", prompt_arg, END_MESSAGE); + } + else if (from_tty && input_from_terminal_p ()) + { + printf_unfiltered ("%s\n%s\n", prompt_arg, END_MESSAGE); + gdb_flush (gdb_stdout); + } + + head = tail = NULL; + old_chain = NULL; + + while (1) + { + val = read_next_line (&next); + + /* Ignore blank lines or comments. */ + if (val == nop_command) + continue; + + if (val == end_command) + { + ret = simple_control; + break; + } + + if (val != ok_command) + { + ret = invalid_control; + break; + } + + if (next->control_type == while_control + || next->control_type == if_control) + { + control_level++; + ret = recurse_read_control_structure (next); + control_level--; + + if (ret == invalid_control) + break; + } + + if (tail) + { + tail->next = next; + } + else + { + head = next; + old_chain = make_cleanup_free_command_lines (&head); + } + tail = next; + } + + dont_repeat (); + + if (head) + { + if (ret != invalid_control) + { + discard_cleanups (old_chain); + } + else + do_cleanups (old_chain); + } + + if (readline_end_hook) + { + (*readline_end_hook) (); + } + return (head); +} + +/* Free a chain of struct command_line's. */ + +void +free_command_lines (struct command_line **lptr) +{ + register struct command_line *l = *lptr; + register struct command_line *next; + struct command_line **blist; + int i; + + while (l) + { + if (l->body_count > 0) + { + blist = l->body_list; + for (i = 0; i < l->body_count; i++, blist++) + free_command_lines (blist); + } + next = l->next; + free (l->line); + free ((PTR) l); + l = next; + } +} + +static void +do_free_command_lines_cleanup (void *arg) +{ + free_command_lines (arg); +} + +static struct cleanup * +make_cleanup_free_command_lines (struct command_line **arg) +{ + return make_cleanup (do_free_command_lines_cleanup, arg); +} + +static void +validate_comname (char *comname) +{ + register char *p; + + if (comname == 0) + error_no_arg ("name of command to define"); + + p = comname; + while (*p) + { + if (!isalnum (*p) && *p != '-' && *p != '_') + error ("Junk in argument list: \"%s\"", p); + p++; + } +} + +/* This is just a placeholder in the command data structures. */ +static void +user_defined_command (char *ignore, int from_tty) +{ +} + +void +define_command (char *comname, int from_tty) +{ +#define MAX_TMPBUF 128 + enum cmd_hook_type + { + CMD_NO_HOOK = 0, + CMD_PRE_HOOK, + CMD_POST_HOOK + }; + register struct command_line *cmds; + register struct cmd_list_element *c, *newc, *oldc, *hookc = 0; + char *tem = comname; + char *tem2; + char tmpbuf[MAX_TMPBUF]; + int hook_type = CMD_NO_HOOK; + int hook_name_size = 0; + +#define HOOK_STRING "hook-" +#define HOOK_LEN 5 +#define HOOK_POST_STRING "hookpost-" +#define HOOK_POST_LEN 9 + + validate_comname (comname); + + /* Look it up, and verify that we got an exact match. */ + c = lookup_cmd (&tem, cmdlist, "", -1, 1); + if (c && !STREQ (comname, c->name)) + c = 0; + + if (c) + { + if (c->class == class_user || c->class == class_alias) + tem = "Redefine command \"%s\"? "; + else + tem = "Really redefine built-in command \"%s\"? "; + if (!query (tem, c->name)) + error ("Command \"%s\" not redefined.", c->name); + } + + /* If this new command is a hook, then mark the command which it + is hooking. Note that we allow hooking `help' commands, so that + we can hook the `stop' pseudo-command. */ + + if (!strncmp (comname, HOOK_STRING, HOOK_LEN)) + { + hook_type = CMD_PRE_HOOK; + hook_name_size = HOOK_LEN; + } + else if (!strncmp (comname, HOOK_POST_STRING, HOOK_POST_LEN)) + { + hook_type = CMD_POST_HOOK; + hook_name_size = HOOK_POST_LEN; + } + + if (hook_type != CMD_NO_HOOK) + { + /* Look up cmd it hooks, and verify that we got an exact match. */ + tem = comname + hook_name_size; + hookc = lookup_cmd (&tem, cmdlist, "", -1, 0); + if (hookc && !STREQ (comname + hook_name_size, hookc->name)) + hookc = 0; + if (!hookc) + { + warning ("Your new `%s' command does not hook any existing command.", + comname); + if (!query ("Proceed? ")) + error ("Not confirmed."); + } + } + + comname = savestring (comname, strlen (comname)); + + /* If the rest of the commands will be case insensitive, this one + should behave in the same manner. */ + for (tem = comname; *tem; tem++) + if (isupper (*tem)) + *tem = tolower (*tem); + + sprintf (tmpbuf, "Type commands for definition of \"%s\".", comname); + cmds = read_command_lines (tmpbuf, from_tty); + + if (c && c->class == class_user) + free_command_lines (&c->user_commands); + + newc = add_cmd (comname, class_user, user_defined_command, + (c && c->class == class_user) + ? c->doc : savestring ("User-defined.", 13), &cmdlist); + newc->user_commands = cmds; + + /* If this new command is a hook, then mark both commands as being + tied. */ + if (hookc) + { + switch (hook_type) + { + case CMD_PRE_HOOK: + hookc->hook_pre = newc; /* Target gets hooked. */ + newc->hookee_pre = hookc; /* We are marked as hooking target cmd. */ + break; + case CMD_POST_HOOK: + hookc->hook_pre = newc; /* Target gets hooked. */ + newc->hookee_pre = hookc; /* We are marked as hooking target cmd. */ + break; + default: + /* Should never come here as hookc would be 0. */ + } + } +} + +void +document_command (char *comname, int from_tty) +{ + struct command_line *doclines; + register struct cmd_list_element *c; + char *tem = comname; + char tmpbuf[128]; + + validate_comname (comname); + + c = lookup_cmd (&tem, cmdlist, "", 0, 1); + + if (c->class != class_user) + error ("Command \"%s\" is built-in.", comname); + + sprintf (tmpbuf, "Type documentation for \"%s\".", comname); + doclines = read_command_lines (tmpbuf, from_tty); + + if (c->doc) + free (c->doc); + + { + register struct command_line *cl1; + register int len = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + len += strlen (cl1->line) + 1; + + c->doc = (char *) xmalloc (len + 1); + *c->doc = 0; + + for (cl1 = doclines; cl1; cl1 = cl1->next) + { + strcat (c->doc, cl1->line); + if (cl1->next) + strcat (c->doc, "\n"); + } + } + + free_command_lines (&doclines); +} + +struct source_cleanup_lines_args +{ + int old_line; + char *old_file; + char *old_pre_error; + char *old_error_pre_print; +}; + +static void +source_cleanup_lines (PTR args) +{ + struct source_cleanup_lines_args *p = + (struct source_cleanup_lines_args *) args; + source_line_number = p->old_line; + source_file_name = p->old_file; + source_pre_error = p->old_pre_error; + error_pre_print = p->old_error_pre_print; +} + +/* ARGSUSED */ +static void +do_fclose_cleanup (void *stream) +{ + fclose (stream); +} + +/* Used to implement source_command */ + +void +script_from_file (FILE *stream, char *file) +{ + struct cleanup *old_cleanups; + struct source_cleanup_lines_args old_lines; + int needed_length; + + if (stream == NULL) + { + internal_error ("called with NULL file pointer!"); + } + + old_cleanups = make_cleanup (do_fclose_cleanup, stream); + + old_lines.old_line = source_line_number; + old_lines.old_file = source_file_name; + old_lines.old_pre_error = source_pre_error; + old_lines.old_error_pre_print = error_pre_print; + make_cleanup (source_cleanup_lines, &old_lines); + source_line_number = 0; + source_file_name = file; + source_pre_error = error_pre_print == NULL ? "" : error_pre_print; + source_pre_error = savestring (source_pre_error, strlen (source_pre_error)); + make_cleanup (free, source_pre_error); + /* This will get set every time we read a line. So it won't stay "" for + long. */ + error_pre_print = ""; + + needed_length = strlen (source_file_name) + strlen (source_pre_error) + 80; + if (source_error_allocated < needed_length) + { + source_error_allocated *= 2; + if (source_error_allocated < needed_length) + source_error_allocated = needed_length; + if (source_error == NULL) + source_error = xmalloc (source_error_allocated); + else + source_error = xrealloc (source_error, source_error_allocated); + } + + read_command_file (stream); + + do_cleanups (old_cleanups); +} + +void +show_user_1 (struct cmd_list_element *c, struct ui_file *stream) +{ + register struct command_line *cmdlines; + + cmdlines = c->user_commands; + if (!cmdlines) + return; + fputs_filtered ("User command ", stream); + fputs_filtered (c->name, stream); + fputs_filtered (":\n", stream); + +#ifdef UI_OUT + print_command_lines (uiout, cmdlines, 1); + fputs_filtered ("\n", stream); +#else + while (cmdlines) + { + print_command_line (cmdlines, 4, stream); + cmdlines = cmdlines->next; + } + fputs_filtered ("\n", stream); +#endif +} + diff --git a/gdb/cli/cli-script.h b/gdb/cli/cli-script.h new file mode 100644 index 0000000..de08428 --- /dev/null +++ b/gdb/cli/cli-script.h @@ -0,0 +1,50 @@ +/* Header file for GDB CLI command implementation library. + Copyright (C) 2000 Free Software Foundation, Inc. + + 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. */ + +#if !defined (CLI_SCRIPT_H) +#define CLI_SCRIPT_H 1 + +/* Exported to cli/cli-cmds.c */ + +extern void script_from_file (FILE *stream, char *file); + +extern void document_command (char *, int); + +extern void define_command (char *, int); + +extern void while_command (char *arg, int from_tty); + +extern void if_command (char *arg, int from_tty); + +extern void show_user_1 (struct cmd_list_element *c, struct ui_file *stream); + +/* Exported to gdb/breakpoint.c */ + +extern enum command_control_type + execute_control_command (struct command_line *cmd); + +#ifdef UI_OUT +extern void print_command_lines (struct ui_out *, + struct command_line *, unsigned int); +#endif + +/* Exported to gdb/infrun.c */ + +extern void execute_user_command (struct cmd_list_element *c, char *args); + +#endif /* !defined (CLI_SCRIPT_H) */ diff --git a/gdb/cli/cli-setshow.c b/gdb/cli/cli-setshow.c new file mode 100644 index 0000000..eebcf5e --- /dev/null +++ b/gdb/cli/cli-setshow.c @@ -0,0 +1,462 @@ +/* Handle set and show GDB commands. + Copyright 2000 Free Software Foundation, Inc. + + 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 <ctype.h> +#if 0 +#include "gdb_string.h" +#endif + +#ifdef UI_OUT +#include "ui-out.h" +#endif + +#include "cli/cli-decode.h" +#include "cli/cli-cmds.h" +#include "cli/cli-setshow.h" + +/* Prototypes for local functions */ + +static int parse_binary_operation (char *); + +static enum cmd_auto_boolean parse_auto_binary_operation (const char *arg); + +static enum cmd_auto_boolean +parse_auto_binary_operation (const char *arg) +{ + if (arg != NULL && *arg != '\0') + { + int length = strlen (arg); + while (isspace (arg[length - 1]) && length > 0) + length--; + if (strncmp (arg, "on", length) == 0 + || strncmp (arg, "1", length) == 0 + || strncmp (arg, "yes", length) == 0 + || strncmp (arg, "enable", length) == 0) + return CMD_AUTO_BOOLEAN_TRUE; + else if (strncmp (arg, "off", length) == 0 + || strncmp (arg, "0", length) == 0 + || strncmp (arg, "no", length) == 0 + || strncmp (arg, "disable", length) == 0) + return CMD_AUTO_BOOLEAN_FALSE; + else if (strncmp (arg, "auto", length) == 0 + || (strncmp (arg, "-1", length) == 0 && length > 1)) + return CMD_AUTO_BOOLEAN_AUTO; + } + error ("\"on\", \"off\" or \"auto\" expected."); + return CMD_AUTO_BOOLEAN_AUTO; /* pacify GCC */ +} + +static int +parse_binary_operation (char *arg) +{ + int length; + + if (!arg || !*arg) + return 1; + + length = strlen (arg); + + while (arg[length - 1] == ' ' || arg[length - 1] == '\t') + length--; + + if (strncmp (arg, "on", length) == 0 + || strncmp (arg, "1", length) == 0 + || strncmp (arg, "yes", length) == 0 + || strncmp (arg, "enable", length) == 0) + return 1; + else if (strncmp (arg, "off", length) == 0 + || strncmp (arg, "0", length) == 0 + || strncmp (arg, "no", length) == 0 + || strncmp (arg, "disable", length) == 0) + return 0; + else + { + error ("\"on\" or \"off\" expected."); + return 0; + } +} + +/* Do a "set" or "show" command. ARG is NULL if no argument, or the text + of the argument, and FROM_TTY is nonzero if this command is being entered + directly by the user (i.e. these are just like any other + command). C is the command list element for the command. */ + +void +do_setshow_command (char *arg, int from_tty, struct cmd_list_element *c) +{ + if (c->type == set_cmd) + { + switch (c->var_type) + { + case var_string: + { + char *new; + char *p; + char *q; + int ch; + + if (arg == NULL) + arg = ""; + new = (char *) xmalloc (strlen (arg) + 2); + p = arg; + q = new; + while ((ch = *p++) != '\000') + { + if (ch == '\\') + { + /* \ at end of argument is used after spaces + so they won't be lost. */ + /* This is obsolete now that we no longer strip + trailing whitespace and actually, the backslash + didn't get here in my test, readline or + something did something funky with a backslash + right before a newline. */ + if (*p == 0) + break; + ch = parse_escape (&p); + if (ch == 0) + break; /* C loses */ + else if (ch > 0) + *q++ = ch; + } + else + *q++ = ch; + } +#if 0 + if (*(p - 1) != '\\') + *q++ = ' '; +#endif + *q++ = '\0'; + new = (char *) xrealloc (new, q - new); + if (*(char **) c->var != NULL) + free (*(char **) c->var); + *(char **) c->var = new; + } + break; + case var_string_noescape: + if (arg == NULL) + arg = ""; + if (*(char **) c->var != NULL) + free (*(char **) c->var); + *(char **) c->var = savestring (arg, strlen (arg)); + break; + case var_filename: + if (arg == NULL) + error_no_arg ("filename to set it to."); + if (*(char **) c->var != NULL) + free (*(char **) c->var); + *(char **) c->var = tilde_expand (arg); + break; + case var_boolean: + *(int *) c->var = parse_binary_operation (arg); + break; + case var_auto_boolean: + *(enum cmd_auto_boolean *) c->var = parse_auto_binary_operation (arg); + break; + case var_uinteger: + if (arg == NULL) + error_no_arg ("integer to set it to."); + *(unsigned int *) c->var = parse_and_eval_long (arg); + if (*(unsigned int *) c->var == 0) + *(unsigned int *) c->var = UINT_MAX; + break; + case var_integer: + { + unsigned int val; + if (arg == NULL) + error_no_arg ("integer to set it to."); + val = parse_and_eval_long (arg); + if (val == 0) + *(int *) c->var = INT_MAX; + else if (val >= INT_MAX) + error ("integer %u out of range", val); + else + *(int *) c->var = val; + break; + } + case var_zinteger: + if (arg == NULL) + error_no_arg ("integer to set it to."); + *(int *) c->var = parse_and_eval_long (arg); + break; + case var_enum: + { + int i; + int len; + int nmatches; + const char *match = NULL; + char *p; + + /* if no argument was supplied, print an informative error message */ + if (arg == NULL) + { + char msg[1024]; + strcpy (msg, "Requires an argument. Valid arguments are "); + for (i = 0; c->enums[i]; i++) + { + if (i != 0) + strcat (msg, ", "); + strcat (msg, c->enums[i]); + } + strcat (msg, "."); + error (msg); + } + + p = strchr (arg, ' '); + + if (p) + len = p - arg; + else + len = strlen (arg); + + nmatches = 0; + for (i = 0; c->enums[i]; i++) + if (strncmp (arg, c->enums[i], len) == 0) + { + if (c->enums[i][len] == '\0') + { + match = c->enums[i]; + nmatches = 1; + break; /* exact match. */ + } + else + { + match = c->enums[i]; + nmatches++; + } + } + + if (nmatches <= 0) + error ("Undefined item: \"%s\".", arg); + + if (nmatches > 1) + error ("Ambiguous item \"%s\".", arg); + + *(const char **) c->var = match; + } + break; + default: + error ("gdb internal error: bad var_type in do_setshow_command"); + } + } + else if (c->type == show_cmd) + { +#ifdef UI_OUT + struct cleanup *old_chain; + struct ui_stream *stb; + int quote; + + stb = ui_out_stream_new (uiout); + old_chain = make_cleanup_ui_out_stream_delete (stb); +#endif /* UI_OUT */ + + /* Print doc minus "show" at start. */ + print_doc_line (gdb_stdout, c->doc + 5); + +#ifdef UI_OUT + ui_out_text (uiout, " is "); + ui_out_wrap_hint (uiout, " "); + quote = 0; + switch (c->var_type) + { + case var_string: + { + unsigned char *p; + + if (*(unsigned char **) c->var) + fputstr_filtered (*(unsigned char **) c->var, '"', stb->stream); + quote = 1; + } + break; + case var_string_noescape: + case var_filename: + case var_enum: + if (*(char **) c->var) + fputs_filtered (*(char **) c->var, stb->stream); + quote = 1; + break; + case var_boolean: + fputs_filtered (*(int *) c->var ? "on" : "off", stb->stream); + break; + case var_auto_boolean: + switch (*(enum cmd_auto_boolean*) c->var) + { + case CMD_AUTO_BOOLEAN_TRUE: + fputs_filtered ("on", stb->stream); + break; + case CMD_AUTO_BOOLEAN_FALSE: + fputs_filtered ("off", stb->stream); + break; + case CMD_AUTO_BOOLEAN_AUTO: + fputs_filtered ("auto", stb->stream); + break; + default: + internal_error ("do_setshow_command: invalid var_auto_boolean"); + break; + } + break; + case var_uinteger: + if (*(unsigned int *) c->var == UINT_MAX) + { + fputs_filtered ("unlimited", stb->stream); + break; + } + /* else fall through */ + case var_zinteger: + fprintf_filtered (stb->stream, "%u", *(unsigned int *) c->var); + break; + case var_integer: + if (*(int *) c->var == INT_MAX) + { + fputs_filtered ("unlimited", stb->stream); + } + else + fprintf_filtered (stb->stream, "%d", *(int *) c->var); + break; + + default: + error ("gdb internal error: bad var_type in do_setshow_command"); + } + if (quote) + ui_out_text (uiout, "\""); + ui_out_field_stream (uiout, "value", stb); + if (quote) + ui_out_text (uiout, "\""); + ui_out_text (uiout, ".\n"); + do_cleanups (old_chain); +#else + fputs_filtered (" is ", gdb_stdout); + wrap_here (" "); + switch (c->var_type) + { + case var_string: + { + fputs_filtered ("\"", gdb_stdout); + if (*(unsigned char **) c->var) + fputstr_filtered (*(unsigned char **) c->var, '"', gdb_stdout); + fputs_filtered ("\"", gdb_stdout); + } + break; + case var_string_noescape: + case var_filename: + case var_enum: + fputs_filtered ("\"", gdb_stdout); + if (*(char **) c->var) + fputs_filtered (*(char **) c->var, gdb_stdout); + fputs_filtered ("\"", gdb_stdout); + break; + case var_boolean: + fputs_filtered (*(int *) c->var ? "on" : "off", gdb_stdout); + break; + case var_auto_boolean: + switch (*(enum cmd_auto_boolean*) c->var) + { + case CMD_AUTO_BOOLEAN_TRUE: + fputs_filtered ("on", gdb_stdout); + break; + case CMD_AUTO_BOOLEAN_FALSE: + fputs_filtered ("off", gdb_stdout); + break; + case CMD_AUTO_BOOLEAN_AUTO: + fputs_filtered ("auto", gdb_stdout); + break; + default: + internal_error ("do_setshow_command: invalid var_auto_boolean"); + break; + } + break; + case var_uinteger: + if (*(unsigned int *) c->var == UINT_MAX) + { + fputs_filtered ("unlimited", gdb_stdout); + break; + } + /* else fall through */ + case var_zinteger: + fprintf_filtered (gdb_stdout, "%u", *(unsigned int *) c->var); + break; + case var_integer: + if (*(int *) c->var == INT_MAX) + { + fputs_filtered ("unlimited", gdb_stdout); + } + else + fprintf_filtered (gdb_stdout, "%d", *(int *) c->var); + break; + + default: + error ("gdb internal error: bad var_type in do_setshow_command"); + } + fputs_filtered (".\n", gdb_stdout); +#endif + } + else + error ("gdb internal error: bad cmd_type in do_setshow_command"); + (*c->function.sfunc) (NULL, from_tty, c); + if (c->type == set_cmd && set_hook) + set_hook (c); +} + +/* Show all the settings in a list of show commands. */ + +void +cmd_show_list (struct cmd_list_element *list, int from_tty, char *prefix) +{ +#ifdef UI_OUT + ui_out_list_begin (uiout, "showlist"); +#endif + for (; list != NULL; list = list->next) + { + /* If we find a prefix, run its list, prefixing our output by its + prefix (with "show " skipped). */ +#ifdef UI_OUT + if (list->prefixlist && !list->abbrev_flag) + { + ui_out_list_begin (uiout, "optionlist"); + ui_out_field_string (uiout, "prefix", list->prefixname + 5); + cmd_show_list (*list->prefixlist, from_tty, list->prefixname + 5); + ui_out_list_end (uiout); + } + if (list->type == show_cmd) + { + ui_out_list_begin (uiout, "option"); + ui_out_text (uiout, prefix); + ui_out_field_string (uiout, "name", list->name); + ui_out_text (uiout, ": "); + do_setshow_command ((char *) NULL, from_tty, list); + ui_out_list_end (uiout); + } +#else + if (list->prefixlist && !list->abbrev_flag) + cmd_show_list (*list->prefixlist, from_tty, list->prefixname + 5); + if (list->type == show_cmd) + { + fputs_filtered (prefix, gdb_stdout); + fputs_filtered (list->name, gdb_stdout); + fputs_filtered (": ", gdb_stdout); + do_setshow_command ((char *) NULL, from_tty, list); + } +#endif + } +#ifdef UI_OUT + ui_out_list_end (uiout); +#endif +} + diff --git a/gdb/cli/cli-setshow.h b/gdb/cli/cli-setshow.h new file mode 100644 index 0000000..bb2fbe9 --- /dev/null +++ b/gdb/cli/cli-setshow.h @@ -0,0 +1,32 @@ +/* Header file for GDB CLI set and show commands implementation. + Copyright (C) 2000 Free Software Foundation, Inc. + + 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. */ + +#if !defined (CLI_SETSHOW_H) +#define CLI_SETSHOW_H 1 + +/* Exported to cli/cli-cmds.c and gdb/top.c */ + +extern void do_setshow_command (char *arg, int from_tty, + struct cmd_list_element *c); + +/* Exported to cli/cli-cmds.c and gdb/top.c, language.c and valprint.c */ + +extern void cmd_show_list (struct cmd_list_element *list, int from_tty, + char *prefix); + +#endif /* !defined (CLI_SETSHOW_H) */ |