]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
pipe completer & var_string options users/palves/cli-options
authorPedro Alves <palves@redhat.com>
Tue, 25 Jun 2019 17:18:50 +0000 (18:18 +0100)
committerPedro Alves <palves@redhat.com>
Tue, 25 Jun 2019 17:35:19 +0000 (18:35 +0100)
gdb/cli/cli-cmds.c
gdb/cli/cli-option.c
gdb/cli/cli-option.h

index bc32fbbaf887bbaf91be0a4571571ce60bd5aed3..59fd51991dfcde1af60b8a3956d1a5513e10ba00 100644 (file)
@@ -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, _("\
index eccabd220deda874c6e377b907190f845c2ed396..fed3cd3f20fc55bb4774f046fa82899b069ac29f 100644 (file)
@@ -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"));
     }
index 1bfbfce1ce512991f85f6afaa13274a99fa57df5..70a3ea558aeb40572a5d70b8a2f0a656106e1d73 100644 (file)
@@ -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