diff options
-rw-r--r-- | gdb/cli/cli-cmds.c | 109 | ||||
-rw-r--r-- | gdb/cli/cli-option.c | 85 | ||||
-rw-r--r-- | gdb/cli/cli-option.h | 21 |
3 files changed, 200 insertions, 15 deletions
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c index bc32fbb..59fd519 100644 --- a/gdb/cli/cli-cmds.c +++ b/gdb/cli/cli-cmds.c @@ -960,32 +960,68 @@ edit_command (const char *arg, int from_tty) xfree (p); } +/* The options for the "pipe" command. */ + +struct pipe_cmd_opts +{ + /* For "-d". */ + char *delimiter = nullptr; + + ~pipe_cmd_opts () + { + xfree (delimiter); + } +}; + +static const gdb::option::option_def pipe_cmd_option_defs[] = { + + gdb::option::string_option_def<pipe_cmd_opts> { + "d", + [] (pipe_cmd_opts *opts) { return &opts->delimiter; }, + nullptr, + N_("Indicates to use the specified delimiter string to separate\n\ +COMMAND from SHELL_COMMAND, in alternative to |. This is useful in\n\ +case COMMAND contains a | character."), + }, + +}; + +/* Create an option_def_group for the "pipe" command's options, with + OPTS as context. */ + +static inline gdb::option::option_def_group +make_pipe_cmd_options_def_group (pipe_cmd_opts *opts) +{ + return {{pipe_cmd_option_defs}, opts}; +} + /* Implementation of the "pipe" command. */ static void pipe_command (const char *arg, int from_tty) { - std::string delim ("|"); + pipe_cmd_opts opts; - if (arg != nullptr && check_for_argument (&arg, "-d", 2)) - { - delim = extract_arg (&arg); - if (delim.empty ()) - error (_("Missing delimiter DELIM after -d")); - } + auto grp = make_pipe_cmd_options_def_group (&opts); + gdb::option::process_options + (&arg, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp); + + const char *delim = "|"; + if (opts.delimiter != nullptr) + delim = opts.delimiter; const char *command = arg; if (command == nullptr) error (_("Missing COMMAND")); - arg = strstr (arg, delim.c_str ()); + arg = strstr (arg, delim); if (arg == nullptr) error (_("Missing delimiter before SHELL_COMMAND")); std::string gdb_cmd (command, arg - command); - arg += delim.length (); /* Skip the delimiter. */ + arg += strlen (delim); /* Skip the delimiter. */ if (gdb_cmd.empty ()) gdb_cmd = repeat_previous (); @@ -1019,6 +1055,43 @@ pipe_command (const char *arg, int from_tty) exit_status_set_internal_vars (exit_status); } +/* Completer for the pipe command. */ + +static void +pipe_command_completer (struct cmd_list_element *ignore, + completion_tracker &tracker, + const char *text, const char *word_ignored) +{ + pipe_cmd_opts opts; + + const char *org_text = text; + auto grp = make_pipe_cmd_options_def_group (&opts); + if (gdb::option::complete_options + (tracker, &text, gdb::option::PROCESS_OPTIONS_UNKNOWN_IS_OPERAND, grp)) + return; + + const char *delimiter = "|"; + if (opts.delimiter != nullptr) + delimiter = opts.delimiter; + + /* Check if we're past option values already. */ + if (text > org_text && !isspace (text[-1])) + return; + + const char *delim = strstr (text, delimiter); + + /* If we're still not past the delimiter, complete the gdb + command. */ + if (delim == nullptr || delim == text) + { + complete_nested_command_line (tracker, text); + return; + } + + /* We're past the delimiter. What follows is a shell command, which + we don't know how to complete. */ +} + static void list_command (const char *arg, int from_tty) { @@ -2029,7 +2102,10 @@ Uses EDITOR environment variable contents as editor (or ex as default).")); c->completer = location_completer; - c = add_com ("pipe", class_support, pipe_command, _("\ + const auto pipe_cmd_opts = make_pipe_cmd_options_def_group (nullptr); + + static std::string pipe_cmd_help + = gdb::option::build_help (_("\ Send the output of a gdb command to a shell command.\n\ Usage: | [COMMAND] | SHELL_COMMAND\n\ Usage: | -d DELIM COMMAND DELIM SHELL_COMMAND\n\ @@ -2038,12 +2114,15 @@ Usage: pipe -d DELIM COMMAND DELIM SHELL_COMMAND\n\ \n\ Executes COMMAND and sends its output to SHELL_COMMAND.\n\ \n\ -The -d option indicates to use the string DELIM to separate COMMAND\n\ -from SHELL_COMMAND, in alternative to |. This is useful in\n\ -case COMMAND contains a | character.\n\ -\n\ +Options:\n\ +%OPTIONS%\ With no COMMAND, repeat the last executed command\n\ -and send its output to SHELL_COMMAND.")); +and send its output to SHELL_COMMAND."), + pipe_cmd_opts); + + c = add_com ("pipe", class_support, pipe_command, + pipe_cmd_help.c_str ()); + set_cmd_completer_handle_brkchars (c, pipe_command_completer); add_com_alias ("|", "pipe", class_support, 0); add_com ("list", class_files, list_command, _("\ diff --git a/gdb/cli/cli-option.c b/gdb/cli/cli-option.c index eccabd2..fed3cd3 100644 --- a/gdb/cli/cli-option.c +++ b/gdb/cli/cli-option.c @@ -43,6 +43,9 @@ union option_value /* For var_enum options. */ const char *enumeration; + + /* For var_string options. This is malloc-allocated. */ + char *string; }; /* Holds an options definition and its value. */ @@ -56,6 +59,53 @@ struct option_def_and_value /* The option's value, if any. */ gdb::optional<option_value> value; + + option_def_and_value (const option_def &option_, void *ctx_, + gdb::optional<option_value> &&value_) + : option (option_), + ctx (ctx_), + value (std::move (value_)) + { + clear_value (option_, value_); + } + + option_def_and_value (const option_def &option_, void *ctx_) + : option (option_), + ctx (ctx_) + { + } + + option_def_and_value (option_def_and_value &&rval) + : option (rval.option), + ctx (rval.ctx), + value (std::move (rval.value)) + { + clear_value (rval.option, rval.value); + } + + ~option_def_and_value () + { + if (value.has_value ()) + { + if (option.type == var_string) + xfree (value->string); + } + } + +private: + + /* Clear the option_value, without releasing it. This is used after + the value has been moved to some other option_def_and_value + instance. */ + static void clear_value (const option_def &option, + gdb::optional<option_value> &value) + { + if (value.has_value ()) + { + if (option.type == var_string) + value->string = nullptr; + } + } }; static void save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov); @@ -372,6 +422,25 @@ parse_option (gdb::array_view<const option_def_group> options_group, val.enumeration = parse_cli_var_enum (args, match->enums); return option_def_and_value {*match, match_ctx, val}; } + case var_string: + { + if (check_for_argument (args, "--")) + { + /* Treat e.g., "pipe -d --" as if there was no argument + after "-d". */ + error (_("-%s requires an argument"), match->name); + } + + const char *arg_start = *args; + *args = skip_to_space (*args); + + if (*args == arg_start) + error (_("-%s requires an argument"), match->name); + + option_value val; + val.string = savestring (arg_start, *args - arg_start); + return option_def_and_value {*match, match_ctx, val}; + } default: /* Not yet. */ @@ -531,6 +600,11 @@ save_option_value_in_ctx (gdb::optional<option_def_and_value> &ov) *ov->option.var_address.enumeration (ov->option, ov->ctx) = ov->value->enumeration; break; + case var_string: + *ov->option.var_address.string (ov->option, ov->ctx) + = ov->value->string; + ov->value->string = nullptr; + break; default: gdb_assert_not_reached ("unhandled option type"); } @@ -603,6 +677,8 @@ get_val_type_str (const option_def &opt, std::string &buffer) } return buffer.c_str (); } + case var_string: + return "STRING"; default: return nullptr; } @@ -730,6 +806,15 @@ add_setshow_cmds_for_options (command_class cmd_class, nullptr, option.show_cmd_cb, set_list, show_list); } + else if (option.type == var_string) + { + add_setshow_string_cmd (option.name, cmd_class, + option.var_address.string (option, data), + option.set_doc, option.show_doc, + option.help_doc, + nullptr, option.show_cmd_cb, + set_list, show_list); + } else gdb_assert_not_reached (_("option type not handled")); } diff --git a/gdb/cli/cli-option.h b/gdb/cli/cli-option.h index 1bfbfce..70a3ea5 100644 --- a/gdb/cli/cli-option.h +++ b/gdb/cli/cli-option.h @@ -86,6 +86,7 @@ public: unsigned int *(*uinteger) (const option_def &, void *ctx); int *(*integer) (const option_def &, void *ctx); const char **(*enumeration) (const option_def &, void *ctx); + char **(*string) (const option_def &, void *ctx); } var_address; @@ -261,6 +262,26 @@ struct enum_option_def : option_def } }; +/* An var_string command line option. */ + +template<typename Context> +struct string_option_def : option_def +{ + string_option_def (const char *long_option_, + char **(*get_var_address_cb_) (Context *), + show_value_ftype *show_cmd_cb_, + const char *set_doc_, + const char *show_doc_ = nullptr, + const char *help_doc_ = nullptr) + : option_def (long_option_, var_string, + (erased_get_var_address_ftype *) get_var_address_cb_, + show_cmd_cb_, + set_doc_, show_doc_, help_doc_) + { + var_address.enumeration = detail::get_var_address<const char *, Context>; + } +}; + /* A group of options that all share the same context pointer to pass to the options' get-current-value callbacks. */ struct option_def_group |