summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2019-06-25 18:18:50 +0100
committerPedro Alves <palves@redhat.com>2019-06-25 18:35:19 +0100
commitcc0a76bef5719c4357a1e91c7d23784ad5b6c634 (patch)
tree179b3694a3c6bb19f59f616456e15637f7a49ad7
parent2d3081ce815ec3f43e79ee922acca40d98dc4986 (diff)
downloadbinutils-gdb-users/palves/cli-options.tar.gz
pipe completer & var_string optionsusers/palves/cli-options
-rw-r--r--gdb/cli/cli-cmds.c109
-rw-r--r--gdb/cli/cli-option.c85
-rw-r--r--gdb/cli/cli-option.h21
3 files changed, 200 insertions, 15 deletions
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index bc32fbbaf88..59fd51991df 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 eccabd220de..fed3cd3f20f 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 1bfbfce1ce5..70a3ea558ae 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