Move mkdir_recursive to common/filestuff.c
[deliverable/binutils-gdb.git] / gdb / completer.c
index 85e6d88b7423acecadff89d085fb3b54e5418495..1c285262de66168e166b327867da0da0d386de11 100644 (file)
@@ -1,5 +1,5 @@
 /* Line completion stuff for GDB, the GNU debugger.
-   Copyright (C) 2000-2017 Free Software Foundation, Inc.
+   Copyright (C) 2000-2018 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -29,6 +29,7 @@
 #include "arch-utils.h"
 #include "location.h"
 #include <algorithm>
+#include "linespec.h"
 #include "cli/cli-decode.h"
 
 /* FIXME: This is needed because of lookup_cmd_1 ().  We should be
@@ -63,8 +64,9 @@ struct gdb_completer_state
 /* The current completion state.  */
 static gdb_completer_state current_completion;
 
-/* An enumeration of the various things a user might
-   attempt to complete for a location.  */
+/* An enumeration of the various things a user might attempt to
+   complete for a location.  If you change this, remember to update
+   the explicit_options array below too.  */
 
 enum explicit_location_match_type
 {
@@ -74,6 +76,12 @@ enum explicit_location_match_type
     /* The name of a function or method.  */
     MATCH_FUNCTION,
 
+    /* The fully-qualified name of a function or method.  */
+    MATCH_QUALIFIED,
+
+    /* A line number.  */
+    MATCH_LINE,
+
     /* The name of a label.  */
     MATCH_LABEL
 };
@@ -146,15 +154,13 @@ filename_completer (struct cmd_list_element *ignore,
                    const char *text, const char *word)
 {
   int subsequent_name;
-  VEC (char_ptr) *return_val = NULL;
 
   subsequent_name = 0;
   while (1)
     {
-      char *p, *q;
-
-      p = rl_filename_completion_function (text, subsequent_name);
-      if (p == NULL)
+      gdb::unique_xmalloc_ptr<char> p_rl
+       (rl_filename_completion_function (text, subsequent_name));
+      if (p_rl == NULL)
        break;
       /* We need to set subsequent_name to a non-zero value before the
         continue line below, because otherwise, if the first file
@@ -163,32 +169,12 @@ filename_completer (struct cmd_list_element *ignore,
       subsequent_name = 1;
       /* Like emacs, don't complete on old versions.  Especially
          useful in the "source" command.  */
+      const char *p = p_rl.get ();
       if (p[strlen (p) - 1] == '~')
-       {
-         xfree (p);
-         continue;
-       }
+       continue;
 
-      if (word == text)
-       /* Return exactly p.  */
-       q = p;
-      else if (word > text)
-       {
-         /* Return some portion of p.  */
-         q = (char *) xmalloc (strlen (p) + 5);
-         strcpy (q, p + (word - text));
-         xfree (p);
-       }
-      else
-       {
-         /* Return some of TEXT plus p.  */
-         q = (char *) xmalloc (strlen (p) + (text - word) + 5);
-         strncpy (q, word, text - word);
-         q[text - word] = '\0';
-         strcat (q, p);
-         xfree (p);
-       }
-      tracker.add_completion (gdb::unique_xmalloc_ptr<char> (q));
+      tracker.add_completion
+       (make_completion_match_str (std::move (p_rl), text, word));
     }
 #if 0
   /* There is no way to do this just long enough to affect quote
@@ -212,6 +198,202 @@ filename_completer_handle_brkchars (struct cmd_list_element *ignore,
     (gdb_completer_file_name_break_characters);
 }
 
+/* Possible values for the found_quote flags word used by the completion
+   functions.  It says what kind of (shell-like) quoting we found anywhere
+   in the line. */
+#define RL_QF_SINGLE_QUOTE      0x01
+#define RL_QF_DOUBLE_QUOTE      0x02
+#define RL_QF_BACKSLASH         0x04
+#define RL_QF_OTHER_QUOTE       0x08
+
+/* Find the bounds of the current word for completion purposes, and
+   return a pointer to the end of the word.  This mimics (and is a
+   modified version of) readline's _rl_find_completion_word internal
+   function.
+
+   This function skips quoted substrings (characters between matched
+   pairs of characters in rl_completer_quote_characters).  We try to
+   find an unclosed quoted substring on which to do matching.  If one
+   is not found, we use the word break characters to find the
+   boundaries of the current word.  QC, if non-null, is set to the
+   opening quote character if we found an unclosed quoted substring,
+   '\0' otherwise.  DP, if non-null, is set to the value of the
+   delimiter character that caused a word break.  */
+
+struct gdb_rl_completion_word_info
+{
+  const char *word_break_characters;
+  const char *quote_characters;
+  const char *basic_quote_characters;
+};
+
+static const char *
+gdb_rl_find_completion_word (struct gdb_rl_completion_word_info *info,
+                            int *qc, int *dp,
+                            const char *line_buffer)
+{
+  int scan, end, found_quote, delimiter, pass_next, isbrk;
+  char quote_char;
+  const char *brkchars;
+  int point = strlen (line_buffer);
+
+  /* The algorithm below does '--point'.  Avoid buffer underflow with
+     the empty string.  */
+  if (point == 0)
+    {
+      if (qc != NULL)
+       *qc = '\0';
+      if (dp != NULL)
+       *dp = '\0';
+      return line_buffer;
+    }
+
+  end = point;
+  found_quote = delimiter = 0;
+  quote_char = '\0';
+
+  brkchars = info->word_break_characters;
+
+  if (info->quote_characters != NULL)
+    {
+      /* We have a list of characters which can be used in pairs to
+        quote substrings for the completer.  Try to find the start of
+        an unclosed quoted substring.  */
+      /* FOUND_QUOTE is set so we know what kind of quotes we
+        found.  */
+      for (scan = pass_next = 0;
+          scan < end;
+          scan++)
+       {
+         if (pass_next)
+           {
+             pass_next = 0;
+             continue;
+           }
+
+         /* Shell-like semantics for single quotes -- don't allow
+            backslash to quote anything in single quotes, especially
+            not the closing quote.  If you don't like this, take out
+            the check on the value of quote_char.  */
+         if (quote_char != '\'' && line_buffer[scan] == '\\')
+           {
+             pass_next = 1;
+             found_quote |= RL_QF_BACKSLASH;
+             continue;
+           }
+
+         if (quote_char != '\0')
+           {
+             /* Ignore everything until the matching close quote
+                char.  */
+             if (line_buffer[scan] == quote_char)
+               {
+                 /* Found matching close.  Abandon this
+                    substring.  */
+                 quote_char = '\0';
+                 point = end;
+               }
+           }
+         else if (strchr (info->quote_characters, line_buffer[scan]))
+           {
+             /* Found start of a quoted substring.  */
+             quote_char = line_buffer[scan];
+             point = scan + 1;
+             /* Shell-like quoting conventions.  */
+             if (quote_char == '\'')
+               found_quote |= RL_QF_SINGLE_QUOTE;
+             else if (quote_char == '"')
+               found_quote |= RL_QF_DOUBLE_QUOTE;
+             else
+               found_quote |= RL_QF_OTHER_QUOTE;
+           }
+       }
+    }
+
+  if (point == end && quote_char == '\0')
+    {
+      /* We didn't find an unclosed quoted substring upon which to do
+        completion, so use the word break characters to find the
+        substring on which to complete.  */
+      while (--point)
+       {
+         scan = line_buffer[point];
+
+         if (strchr (brkchars, scan) != 0)
+           break;
+       }
+    }
+
+  /* If we are at an unquoted word break, then advance past it.  */
+  scan = line_buffer[point];
+
+  if (scan)
+    {
+      isbrk = strchr (brkchars, scan) != 0;
+
+      if (isbrk)
+       {
+         /* If the character that caused the word break was a quoting
+            character, then remember it as the delimiter.  */
+         if (info->basic_quote_characters
+             && strchr (info->basic_quote_characters, scan)
+             && (end - point) > 1)
+           delimiter = scan;
+
+         point++;
+       }
+    }
+
+  if (qc != NULL)
+    *qc = quote_char;
+  if (dp != NULL)
+    *dp = delimiter;
+
+  return line_buffer + point;
+}
+
+/* See completer.h.  */
+
+const char *
+advance_to_expression_complete_word_point (completion_tracker &tracker,
+                                          const char *text)
+{
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters
+    = current_language->la_word_break_characters ();
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  const char *start
+    = gdb_rl_find_completion_word (&info, NULL, NULL, text);
+
+  tracker.advance_custom_word_point_by (start - text);
+
+  return start;
+}
+
+/* See completer.h.  */
+
+bool
+completion_tracker::completes_to_completion_word (const char *word)
+{
+  if (m_lowest_common_denominator_unique)
+    {
+      const char *lcd = m_lowest_common_denominator;
+
+      if (strncmp_iw (word, lcd, strlen (lcd)) == 0)
+       {
+         /* Maybe skip the function and complete on keywords.  */
+         size_t wordlen = strlen (word);
+         if (word[wordlen - 1] == ' ')
+           return true;
+       }
+    }
+
+  return false;
+}
+
 /* Complete on linespecs, which might be of two possible forms:
 
        file:line
@@ -225,7 +407,6 @@ static void
 complete_files_symbols (completion_tracker &tracker,
                        const char *text, const char *word)
 {
-  int ix;
   completion_list fn_list;
   const char *p;
   int quote_found = 0;
@@ -296,7 +477,10 @@ complete_files_symbols (completion_tracker &tracker,
      symbols as well as on files.  */
   if (colon)
     {
-      collect_file_symbol_completion_matches (tracker, symbol_start, word,
+      collect_file_symbol_completion_matches (tracker,
+                                             complete_symbol_mode::EXPRESSION,
+                                             symbol_name_match_type::EXPRESSION,
+                                             symbol_start, word,
                                              file_to_match);
       xfree (file_to_match);
     }
@@ -304,7 +488,10 @@ complete_files_symbols (completion_tracker &tracker,
     {
       size_t text_len = strlen (text);
 
-      collect_symbol_completion_matches (tracker, symbol_start, word);
+      collect_symbol_completion_matches (tracker,
+                                        complete_symbol_mode::EXPRESSION,
+                                        symbol_name_match_type::EXPRESSION,
+                                        symbol_start, word);
       /* If text includes characters which cannot appear in a file
         name, they cannot be asking for completion on files.  */
       if (strcspn (text,
@@ -314,8 +501,6 @@ complete_files_symbols (completion_tracker &tracker,
 
   if (!fn_list.empty () && !tracker.have_completions ())
     {
-      char *fn;
-
       /* If we only have file names as possible completion, we should
         bring them in sync with what rl_complete expects.  The
         problem is that if the user types "break /foo/b TAB", and the
@@ -345,10 +530,73 @@ complete_files_symbols (completion_tracker &tracker,
       /* No completions at all.  As the final resort, try completing
         on the entire text as a symbol.  */
       collect_symbol_completion_matches (tracker,
+                                        complete_symbol_mode::EXPRESSION,
+                                        symbol_name_match_type::EXPRESSION,
                                         orig_text, word);
     }
 }
 
+/* See completer.h.  */
+
+completion_list
+complete_source_filenames (const char *text)
+{
+  size_t text_len = strlen (text);
+
+  /* If text includes characters which cannot appear in a file name,
+     the user cannot be asking for completion on files.  */
+  if (strcspn (text,
+              gdb_completer_file_name_break_characters)
+      == text_len)
+    return make_source_files_completion_list (text, text);
+
+  return {};
+}
+
+/* Complete address and linespec locations.  */
+
+static void
+complete_address_and_linespec_locations (completion_tracker &tracker,
+                                        const char *text,
+                                        symbol_name_match_type match_type)
+{
+  if (*text == '*')
+    {
+      tracker.advance_custom_word_point_by (1);
+      text++;
+      const char *word
+       = advance_to_expression_complete_word_point (tracker, text);
+      complete_expression (tracker, text, word);
+    }
+  else
+    {
+      linespec_complete (tracker, text, match_type);
+    }
+}
+
+/* The explicit location options.  Note that indexes into this array
+   must match the explicit_location_match_type enumerators.  */
+
+static const char *const explicit_options[] =
+  {
+    "-source",
+    "-function",
+    "-qualified",
+    "-line",
+    "-label",
+    NULL
+  };
+
+/* The probe modifier options.  These can appear before a location in
+   breakpoint commands.  */
+static const char *const probe_options[] =
+  {
+    "-probe",
+    "-probe-stap",
+    "-probe-dtrace",
+    NULL
+  };
+
 /* Returns STRING if not NULL, the empty string otherwise.  */
 
 static const char *
@@ -364,18 +612,25 @@ static void
 collect_explicit_location_matches (completion_tracker &tracker,
                                   struct event_location *location,
                                   enum explicit_location_match_type what,
-                                  const char *word)
+                                  const char *word,
+                                  const struct language_defn *language)
 {
   const struct explicit_location *explicit_loc
     = get_explicit_location (location);
 
+  /* True if the option expects an argument.  */
+  bool needs_arg = true;
+
+  /* Note, in the various MATCH_* below, we complete on
+     explicit_loc->foo instead of WORD, because only the former will
+     have already skipped past any quote char.  */
   switch (what)
     {
     case MATCH_SOURCE:
       {
        const char *source = string_or_empty (explicit_loc->source_filename);
        completion_list matches
-         = make_source_files_completion_list (source, word);
+         = make_source_files_completion_list (source, source);
        tracker.add_completions (std::move (matches));
       }
       break;
@@ -383,120 +638,187 @@ collect_explicit_location_matches (completion_tracker &tracker,
     case MATCH_FUNCTION:
       {
        const char *function = string_or_empty (explicit_loc->function_name);
-       if (explicit_loc->source_filename != NULL)
-         {
-           const char *filename = explicit_loc->source_filename;
-
-           collect_file_symbol_completion_matches (tracker,
-                                                   function, word, filename);
-         }
-       else
-        collect_symbol_completion_matches (tracker, function, word);
+       linespec_complete_function (tracker, function,
+                                   explicit_loc->func_name_match_type,
+                                   explicit_loc->source_filename);
       }
       break;
 
+    case MATCH_QUALIFIED:
+      needs_arg = false;
+      break;
+    case MATCH_LINE:
+      /* Nothing to offer.  */
+      break;
+
     case MATCH_LABEL:
-      /* Not supported.  */
+      {
+       const char *label = string_or_empty (explicit_loc->label_name);
+       linespec_complete_label (tracker, language,
+                                explicit_loc->source_filename,
+                                explicit_loc->function_name,
+                                explicit_loc->func_name_match_type,
+                                label);
+      }
       break;
 
     default:
       gdb_assert_not_reached ("unhandled explicit_location_match_type");
     }
+
+  if (!needs_arg || tracker.completes_to_completion_word (word))
+    {
+      tracker.discard_completions ();
+      tracker.advance_custom_word_point_by (strlen (word));
+      complete_on_enum (tracker, explicit_options, "", "");
+      complete_on_enum (tracker, linespec_keywords, "", "");
+    }
+  else if (!tracker.have_completions ())
+    {
+      /* Maybe we have an unterminated linespec keyword at the tail of
+        the string.  Try completing on that.  */
+      size_t wordlen = strlen (word);
+      const char *keyword = word + wordlen;
+
+      if (wordlen > 0 && keyword[-1] != ' ')
+       {
+         while (keyword > word && *keyword != ' ')
+           keyword--;
+         /* Don't complete on keywords if we'd be completing on the
+            whole explicit linespec option.  E.g., "b -function
+            thr<tab>" should not complete to the "thread"
+            keyword.  */
+         if (keyword != word)
+           {
+             keyword = skip_spaces (keyword);
+
+             tracker.advance_custom_word_point_by (keyword - word);
+             complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+           }
+       }
+      else if (wordlen > 0 && keyword[-1] == ' ')
+       {
+         /* Assume that we're maybe past the explicit location
+            argument, and we didn't manage to find any match because
+            the user wants to create a pending breakpoint.  Offer the
+            keyword and explicit location options as possible
+            completions.  */
+         tracker.advance_custom_word_point_by (keyword - word);
+         complete_on_enum (tracker, linespec_keywords, keyword, keyword);
+         complete_on_enum (tracker, explicit_options, keyword, keyword);
+       }
+    }
 }
 
-/* A convenience macro to (safely) back up P to the previous word.  */
+/* If the next word in *TEXT_P is any of the keywords in KEYWORDS,
+   then advance both TEXT_P and the word point in the tracker past the
+   keyword and return the (0-based) index in the KEYWORDS array that
+   matched.  Otherwise, return -1.  */
 
-static const char *
-backup_text_ptr (const char *p, const char *text)
+static int
+skip_keyword (completion_tracker &tracker,
+             const char * const *keywords, const char **text_p)
 {
-  while (p > text && isspace (*p))
-    --p;
-  for (; p > text && !isspace (p[-1]); --p)
-    ;
+  const char *text = *text_p;
+  const char *after = skip_to_space (text);
+  size_t len = after - text;
+
+  if (text[len] != ' ')
+    return -1;
 
-  return p;
+  int found = -1;
+  for (int i = 0; keywords[i] != NULL; i++)
+    {
+      if (strncmp (keywords[i], text, len) == 0)
+       {
+         if (found == -1)
+           found = i;
+         else
+           return -1;
+       }
+    }
+
+  if (found != -1)
+    {
+      tracker.advance_custom_word_point_by (len + 1);
+      text += len + 1;
+      *text_p = text;
+      return found;
+    }
+
+  return -1;
 }
 
 /* A completer function for explicit locations.  This function
-   completes both options ("-source", "-line", etc) and values.  */
+   completes both options ("-source", "-line", etc) and values.  If
+   completing a quoted string, then QUOTED_ARG_START and
+   QUOTED_ARG_END point to the quote characters.  LANGUAGE is the
+   current language.  */
 
 static void
 complete_explicit_location (completion_tracker &tracker,
                            struct event_location *location,
-                           const char *text, const char *word)
+                           const char *text,
+                           const language_defn *language,
+                           const char *quoted_arg_start,
+                           const char *quoted_arg_end)
 {
-  const char *p;
-
-  /* Find the beginning of the word.  This is necessary because
-     we need to know if we are completing an option name or value.  We
-     don't get the leading '-' from the completer.  */
-  p = backup_text_ptr (word, text);
-
-  if (*p == '-')
-    {
-      /* Completing on option name.  */
-      static const char *const keywords[] =
-       {
-         "source",
-         "function",
-         "line",
-         "label",
-         NULL
-       };
+  if (*text != '-')
+    return;
 
-      /* Skip over the '-'.  */
-      ++p;
+  int keyword = skip_keyword (tracker, explicit_options, &text);
 
-      complete_on_enum (tracker, keywords, p, p);
-      return;
-    }
+  if (keyword == -1)
+    complete_on_enum (tracker, explicit_options, text, text);
   else
     {
-      /* Completing on value (or unknown).  Get the previous word to see what
-        the user is completing on.  */
-      size_t len, offset;
-      const char *new_word, *end;
-      enum explicit_location_match_type what;
-      struct explicit_location *explicit_loc
-       = get_explicit_location (location);
-
-      /* Backup P to the previous word, which should be the option
-        the user is attempting to complete.  */
-      offset = word - p;
-      end = --p;
-      p = backup_text_ptr (p, text);
-      len = end - p;
-
-      if (strncmp (p, "-source", len) == 0)
-       {
-         what = MATCH_SOURCE;
-         new_word = explicit_loc->source_filename + offset;
-       }
-      else if (strncmp (p, "-function", len) == 0)
-       {
-         what = MATCH_FUNCTION;
-         new_word = explicit_loc->function_name + offset;
-       }
-      else if (strncmp (p, "-label", len) == 0)
-       {
-         what = MATCH_LABEL;
-         new_word = explicit_loc->label_name + offset;
-       }
-      else
+      /* Completing on value.  */
+      enum explicit_location_match_type what
+       = (explicit_location_match_type) keyword;
+
+      if (quoted_arg_start != NULL && quoted_arg_end != NULL)
        {
-         /* The user isn't completing on any valid option name,
-            e.g., "break -source foo.c [tab]".  */
+         if (quoted_arg_end[1] == '\0')
+           {
+             /* If completing a quoted string with the cursor right
+                at the terminating quote char, complete the
+                completion word without interpretation, so that
+                readline advances the cursor one whitespace past the
+                quote, even if there's no match.  This makes these
+                cases behave the same:
+
+                  before: "b -function function()"
+                  after:  "b -function function() "
+
+                  before: "b -function 'function()'"
+                  after:  "b -function 'function()' "
+
+                and trusts the user in this case:
+
+                  before: "b -function 'not_loaded_function_yet()'"
+                  after:  "b -function 'not_loaded_function_yet()' "
+             */
+             gdb::unique_xmalloc_ptr<char> text_copy
+               (xstrdup (text));
+             tracker.add_completion (std::move (text_copy));
+           }
+         else if (quoted_arg_end[1] == ' ')
+           {
+             /* We're maybe past the explicit location argument.
+                Skip the argument without interpretion, assuming the
+                user may want to create pending breakpoint.  Offer
+                the keyword and explicit location options as possible
+                completions.  */
+             tracker.advance_custom_word_point_by (strlen (text));
+             complete_on_enum (tracker, linespec_keywords, "", "");
+             complete_on_enum (tracker, explicit_options, "", "");
+           }
          return;
        }
 
-      /* If the user hasn't entered a search expression, e.g.,
-        "break -function <TAB><TAB>", new_word will be NULL, but
-        search routines require non-NULL search words.  */
-      if (new_word == NULL)
-       new_word = "";
-
       /* Now gather matches  */
-      collect_explicit_location_matches (tracker, location, what, new_word);
+      collect_explicit_location_matches (tracker, location, what, text,
+                                        language);
     }
 }
 
@@ -505,32 +827,142 @@ complete_explicit_location (completion_tracker &tracker,
 void
 location_completer (struct cmd_list_element *ignore,
                    completion_tracker &tracker,
-                   const char *text, const char *word)
+                   const char *text, const char * /* word */)
 {
+  int found_probe_option = -1;
+
+  /* If we have a probe modifier, skip it.  This can only appear as
+     first argument.  Until we have a specific completer for probes,
+     falling back to the linespec completer for the remainder of the
+     line is better than nothing.  */
+  if (text[0] == '-' && text[1] == 'p')
+    found_probe_option = skip_keyword (tracker, probe_options, &text);
+
+  const char *option_text = text;
+  int saved_word_point = tracker.custom_word_point ();
+
   const char *copy = text;
 
-  event_location_up location = string_to_explicit_location (&copy,
-                                                           current_language,
-                                                           1);
-  if (location != NULL)
-    complete_explicit_location (tracker, location.get (),
-                               text, word);
+  explicit_completion_info completion_info;
+  event_location_up location
+    = string_to_explicit_location (&copy, current_language,
+                                  &completion_info);
+  if (completion_info.quoted_arg_start != NULL
+      && completion_info.quoted_arg_end == NULL)
+    {
+      /* Found an unbalanced quote.  */
+      tracker.set_quote_char (*completion_info.quoted_arg_start);
+      tracker.advance_custom_word_point_by (1);
+    }
+
+  if (completion_info.saw_explicit_location_option)
+    {
+      if (*copy != '\0')
+       {
+         tracker.advance_custom_word_point_by (copy - text);
+         text = copy;
+
+         /* We found a terminator at the tail end of the string,
+            which means we're past the explicit location options.  We
+            may have a keyword to complete on.  If we have a whole
+            keyword, then complete whatever comes after as an
+            expression.  This is mainly for the "if" keyword.  If the
+            "thread" and "task" keywords gain their own completers,
+            they should be used here.  */
+         int keyword = skip_keyword (tracker, linespec_keywords, &text);
+
+         if (keyword == -1)
+           {
+             complete_on_enum (tracker, linespec_keywords, text, text);
+           }
+         else
+           {
+             const char *word
+               = advance_to_expression_complete_word_point (tracker, text);
+             complete_expression (tracker, text, word);
+           }
+       }
+      else
+       {
+         tracker.advance_custom_word_point_by (completion_info.last_option
+                                               - text);
+         text = completion_info.last_option;
+
+         complete_explicit_location (tracker, location.get (), text,
+                                     current_language,
+                                     completion_info.quoted_arg_start,
+                                     completion_info.quoted_arg_end);
+
+       }
+    }
+  /* This is an address or linespec location.  */
+  else if (location != NULL)
+    {
+      /* Handle non-explicit location options.  */
+
+      int keyword = skip_keyword (tracker, explicit_options, &text);
+      if (keyword == -1)
+       complete_on_enum (tracker, explicit_options, text, text);
+      else
+       {
+         tracker.advance_custom_word_point_by (copy - text);
+         text = copy;
+
+         symbol_name_match_type match_type
+           = get_explicit_location (location.get ())->func_name_match_type;
+         complete_address_and_linespec_locations (tracker, text, match_type);
+       }
+    }
   else
     {
-      /* This is an address or linespec location.
-        Right now both of these are handled by the (old) linespec
-        completer.  */
-      complete_files_symbols (tracker, text, word);
+      /* No options.  */
+      complete_address_and_linespec_locations (tracker, text,
+                                              symbol_name_match_type::WILD);
+    }
+
+  /* Add matches for option names, if either:
+
+     - Some completer above found some matches, but the word point did
+       not advance (e.g., "b <tab>" finds all functions, or "b -<tab>"
+       matches all objc selectors), or;
+
+     - Some completer above advanced the word point, but found no
+       matches.
+  */
+  if ((text[0] == '-' || text[0] == '\0')
+      && (!tracker.have_completions ()
+         || tracker.custom_word_point () == saved_word_point))
+    {
+      tracker.set_custom_word_point (saved_word_point);
+      text = option_text;
+
+      if (found_probe_option == -1)
+       complete_on_enum (tracker, probe_options, text, text);
+      complete_on_enum (tracker, explicit_options, text, text);
     }
 }
 
+/* The corresponding completer_handle_brkchars
+   implementation.  */
+
+static void
+location_completer_handle_brkchars (struct cmd_list_element *ignore,
+                                   completion_tracker &tracker,
+                                   const char *text,
+                                   const char *word_ignored)
+{
+  tracker.set_use_custom_word_point (true);
+
+  location_completer (ignore, tracker, text, NULL);
+}
+
 /* Helper for expression_completer which recursively adds field and
    method names from TYPE, a struct or union type, to the OUTPUT
    list.  */
 
 static void
 add_struct_fields (struct type *type, completion_list &output,
-                  char *fieldname, int namelen)
+                  const char *fieldname, int namelen)
 {
   int i;
   int computed_type_name = 0;
@@ -567,7 +999,7 @@ add_struct_fields (struct type *type, completion_list &output,
        {
          if (!computed_type_name)
            {
-             type_name = type_name_no_tag (type);
+             type_name = TYPE_NAME (type);
              computed_type_name = 1;
            }
          /* Omit constructors from the completion list.  */
@@ -577,21 +1009,18 @@ add_struct_fields (struct type *type, completion_list &output,
     }
 }
 
-/* Complete on expressions.  Often this means completing on symbol
-   names, but some language parsers also have support for completing
-   field names.  */
+/* See completer.h.  */
 
-static void
+void
 complete_expression (completion_tracker &tracker,
                     const char *text, const char *word)
 {
   struct type *type = NULL;
-  char *fieldname;
+  gdb::unique_xmalloc_ptr<char> fieldname;
   enum type_code code = TYPE_CODE_UNDEF;
 
   /* Perform a tentative parse of the expression, to see whether a
      field completion is required.  */
-  fieldname = NULL;
   TRY
     {
       type = parse_expression_for_completion (text, &fieldname, &code);
@@ -602,7 +1031,7 @@ complete_expression (completion_tracker &tracker,
     }
   END_CATCH
 
-  if (fieldname && type)
+  if (fieldname != nullptr && type)
     {
       for (;;)
        {
@@ -615,26 +1044,20 @@ complete_expression (completion_tracker &tracker,
       if (TYPE_CODE (type) == TYPE_CODE_UNION
          || TYPE_CODE (type) == TYPE_CODE_STRUCT)
        {
-         int flen = strlen (fieldname);
          completion_list result;
 
-         add_struct_fields (type, result, fieldname, flen);
-         xfree (fieldname);
+         add_struct_fields (type, result, fieldname.get (),
+                            strlen (fieldname.get ()));
          tracker.add_completions (std::move (result));
          return;
        }
     }
-  else if (fieldname && code != TYPE_CODE_UNDEF)
+  else if (fieldname != nullptr && code != TYPE_CODE_UNDEF)
     {
-      VEC (char_ptr) *result;
-      struct cleanup *cleanup = make_cleanup (xfree, fieldname);
-
-      collect_symbol_completion_matches_type (tracker, fieldname, fieldname,
-                                             code);
-      do_cleanups (cleanup);
+      collect_symbol_completion_matches_type (tracker, fieldname.get (),
+                                             fieldname.get (), code);
       return;
     }
-  xfree (fieldname);
 
   complete_files_symbols (tracker, text, word);
 }
@@ -683,7 +1106,9 @@ symbol_completer (struct cmd_list_element *ignore,
                  completion_tracker &tracker,
                  const char *text, const char *word)
 {
-  collect_symbol_completion_matches (tracker, text, word);
+  collect_symbol_completion_matches (tracker, complete_symbol_mode::EXPRESSION,
+                                    symbol_name_match_type::EXPRESSION,
+                                    text, word);
 }
 
 /* Here are some useful test cases for completion.  FIXME: These
@@ -712,10 +1137,24 @@ symbol_completer (struct cmd_list_element *ignore,
 enum complete_line_internal_reason
 {
   /* Preliminary phase, called by gdb_completion_word_break_characters
-     function, is used to determine the correct set of chars that are
-     word delimiters depending on the current command in line_buffer.
-     No completion list should be generated; the return value should
-     be NULL.  This is checked by an assertion.  */
+     function, is used to either:
+
+     #1 - Determine the set of chars that are word delimiters
+         depending on the current command in line_buffer.
+
+     #2 - Manually advance RL_POINT to the "word break" point instead
+         of letting readline do it (based on too-simple character
+         matching).
+
+     Simpler completers that just pass a brkchars array to readline
+     (#1 above) must defer generating the completions to the main
+     phase (below).  No completion list should be generated in this
+     phase.
+
+     OTOH, completers that manually advance the word point(#2 above)
+     must set "use_custom_word_point" in the tracker and generate
+     their completion in this phase.  Note that this is the convenient
+     thing to do since they'll be parsing the input line anyway.  */
   handle_brkchars,
 
   /* Main phase, called by complete_line function, is used to get the
@@ -831,10 +1270,13 @@ complete_line_internal_1 (completion_tracker &tracker,
       word = tmp_command + point - strlen (text);
     }
 
-  if (point == 0)
+  /* Move P up to the start of the command.  */
+  p = skip_spaces (p);
+
+  if (*p == '\0')
     {
-      /* An empty line we want to consider ambiguous; that is, it
-        could be any command.  */
+      /* An empty line is ambiguous; that is, it could be any
+        command.  */
       c = CMD_LIST_AMBIGUOUS;
       result_list = 0;
     }
@@ -849,6 +1291,8 @@ complete_line_internal_1 (completion_tracker &tracker,
       p++;
     }
 
+  tracker.advance_custom_word_point_by (p - tmp_command);
+
   if (!c)
     {
       /* It is an unrecognized command.  So there are no
@@ -1010,6 +1454,7 @@ complete_line_internal (completion_tracker &tracker,
       if (except.error != MAX_COMPLETIONS_REACHED_ERROR)
        throw_exception (except);
     }
+  END_CATCH
 }
 
 /* See completer.h.  */
@@ -1024,7 +1469,25 @@ int max_completions = 200;
 completion_tracker::completion_tracker ()
 {
   m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
-                                     htab_hash_string, (htab_eq) streq,
+                                     htab_hash_string, streq_hash,
+                                     NULL, xcalloc, xfree);
+}
+
+/* See completer.h.  */
+
+void
+completion_tracker::discard_completions ()
+{
+  xfree (m_lowest_common_denominator);
+  m_lowest_common_denominator = NULL;
+
+  m_lowest_common_denominator_unique = false;
+
+  m_entries_vec.clear ();
+
+  htab_delete (m_entries_hash);
+  m_entries_hash = htab_create_alloc (INITIAL_COMPLETION_HTAB_SIZE,
+                                     htab_hash_string, streq_hash,
                                      NULL, xcalloc, xfree);
 }
 
@@ -1039,7 +1502,10 @@ completion_tracker::~completion_tracker ()
 /* See completer.h.  */
 
 bool
-completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::maybe_add_completion
+  (gdb::unique_xmalloc_ptr<char> name,
+   completion_match_for_lcd *match_for_lcd,
+   const char *text, const char *word)
 {
   void **slot;
 
@@ -1052,9 +1518,18 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
   slot = htab_find_slot (m_entries_hash, name.get (), INSERT);
   if (*slot == HTAB_EMPTY_ENTRY)
     {
-      const char *match_for_lcd_str = name.get ();
+      const char *match_for_lcd_str = NULL;
+
+      if (match_for_lcd != NULL)
+       match_for_lcd_str = match_for_lcd->finish ();
 
-      recompute_lowest_common_denominator (match_for_lcd_str);
+      if (match_for_lcd_str == NULL)
+       match_for_lcd_str = name.get ();
+
+      gdb::unique_xmalloc_ptr<char> lcd
+       = make_completion_match_str (match_for_lcd_str, text, word);
+
+      recompute_lowest_common_denominator (std::move (lcd));
 
       *slot = name.get ();
       m_entries_vec.push_back (std::move (name));
@@ -1066,9 +1541,11 @@ completion_tracker::maybe_add_completion (gdb::unique_xmalloc_ptr<char> name)
 /* See completer.h.  */
 
 void
-completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name)
+completion_tracker::add_completion (gdb::unique_xmalloc_ptr<char> name,
+                                   completion_match_for_lcd *match_for_lcd,
+                                   const char *text, const char *word)
 {
-  if (!maybe_add_completion (std::move (name)))
+  if (!maybe_add_completion (std::move (name), match_for_lcd, text, word))
     throw_error (MAX_COMPLETIONS_REACHED_ERROR, _("Max completions reached."));
 }
 
@@ -1081,6 +1558,63 @@ completion_tracker::add_completions (completion_list &&list)
     add_completion (std::move (candidate));
 }
 
+/* Helper for the make_completion_match_str overloads.  Returns NULL
+   as an indication that we want MATCH_NAME exactly.  It is up to the
+   caller to xstrdup that string if desired.  */
+
+static char *
+make_completion_match_str_1 (const char *match_name,
+                            const char *text, const char *word)
+{
+  char *newobj;
+
+  if (word == text)
+    {
+      /* Return NULL as an indication that we want MATCH_NAME
+        exactly.  */
+      return NULL;
+    }
+  else if (word > text)
+    {
+      /* Return some portion of MATCH_NAME.  */
+      newobj = xstrdup (match_name + (word - text));
+    }
+  else
+    {
+      /* Return some of WORD plus MATCH_NAME.  */
+      size_t len = strlen (match_name);
+      newobj = (char *) xmalloc (text - word + len + 1);
+      memcpy (newobj, word, text - word);
+      memcpy (newobj + (text - word), match_name, len + 1);
+    }
+
+  return newobj;
+}
+
+/* See completer.h.  */
+
+gdb::unique_xmalloc_ptr<char>
+make_completion_match_str (const char *match_name,
+                          const char *text, const char *word)
+{
+  char *newobj = make_completion_match_str_1 (match_name, text, word);
+  if (newobj == NULL)
+    newobj = xstrdup (match_name);
+  return gdb::unique_xmalloc_ptr<char> (newobj);
+}
+
+/* See completer.h.  */
+
+gdb::unique_xmalloc_ptr<char>
+make_completion_match_str (gdb::unique_xmalloc_ptr<char> &&match_name,
+                          const char *text, const char *word)
+{
+  char *newobj = make_completion_match_str_1 (match_name.get (), text, word);
+  if (newobj == NULL)
+    return std::move (match_name);
+  return gdb::unique_xmalloc_ptr<char> (newobj);
+}
+
 /* Generate completions all at once.  Does nothing if max_completions
    is 0.  If max_completions is non-negative, this will collect at
    most max_completions strings.
@@ -1258,12 +1792,27 @@ completer_handle_brkchars_func_for_completer (completer_ftype *fn)
   if (fn == filename_completer)
     return filename_completer_handle_brkchars;
 
+  if (fn == location_completer)
+    return location_completer_handle_brkchars;
+
   if (fn == command_completer)
     return command_completer_handle_brkchars;
 
   return default_completer_handle_brkchars;
 }
 
+/* Used as brkchars when we want to tell readline we have a custom
+   word point.  We do that by making our rl_completion_word_break_hook
+   set RL_POINT to the desired word point, and return the character at
+   the word break point as the break char.  This is two bytes in order
+   to fit one break character plus the terminating null.  */
+static char gdb_custom_word_point_brkchars[2];
+
+/* Since rl_basic_quote_characters is not completer-specific, we save
+   its original value here, in order to be able to restore it in
+   gdb_rl_attempted_completion_function.  */
+static const char *gdb_org_rl_basic_quote_characters = rl_basic_quote_characters;
+
 /* Get the list of chars that are considered as word breaks
    for the current command.  */
 
@@ -1280,6 +1829,27 @@ gdb_completion_word_break_characters_throw ()
   complete_line_internal (tracker, NULL, rl_line_buffer,
                          rl_point, handle_brkchars);
 
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      rl_point = tracker.custom_word_point () - 1;
+      gdb_custom_word_point_brkchars[0] = rl_line_buffer[rl_point];
+      rl_completer_word_break_characters = gdb_custom_word_point_brkchars;
+      rl_completer_quote_characters = NULL;
+
+      /* Clear this too, so that if we're completing a quoted string,
+        readline doesn't consider the quote character a delimiter.
+        If we didn't do this, readline would auto-complete {b
+        'fun<tab>} to {'b 'function()'}, i.e., add the terminating
+        \', but, it wouldn't append the separator space either, which
+        is not desirable.  So instead we take care of appending the
+        quote character to the LCD ourselves, in
+        gdb_rl_attempted_completion_function.  Since this global is
+        not just completer-specific, we'll restore it back to the
+        default in gdb_rl_attempted_completion_function.  */
+      rl_basic_quote_characters = NULL;
+    }
+
   return rl_completer_word_break_characters;
 }
 
@@ -1306,22 +1876,50 @@ gdb_completion_word_break_characters ()
 
 /* See completer.h.  */
 
+const char *
+completion_find_completion_word (completion_tracker &tracker, const char *text,
+                                int *quote_char)
+{
+  size_t point = strlen (text);
+
+  complete_line_internal (tracker, NULL, text, point, handle_brkchars);
+
+  if (tracker.use_custom_word_point ())
+    {
+      gdb_assert (tracker.custom_word_point () > 0);
+      *quote_char = tracker.quote_char ();
+      return text + tracker.custom_word_point ();
+    }
+
+  gdb_rl_completion_word_info info;
+
+  info.word_break_characters = rl_completer_word_break_characters;
+  info.quote_characters = gdb_completer_quote_characters;
+  info.basic_quote_characters = rl_basic_quote_characters;
+
+  return gdb_rl_find_completion_word (&info, quote_char, NULL, text);
+}
+
+/* See completer.h.  */
+
 void
-completion_tracker::recompute_lowest_common_denominator (const char *new_match)
+completion_tracker::recompute_lowest_common_denominator
+  (gdb::unique_xmalloc_ptr<char> &&new_match_up)
 {
   if (m_lowest_common_denominator == NULL)
     {
       /* We don't have a lowest common denominator yet, so simply take
-        the whole NEW_MATCH as being it.  */
-      m_lowest_common_denominator = xstrdup (new_match);
+        the whole NEW_MATCH_UP as being it.  */
+      m_lowest_common_denominator = new_match_up.release ();
       m_lowest_common_denominator_unique = true;
     }
   else
     {
       /* Find the common denominator between the currently-known
-        lowest common denominator and NEW_MATCH.  That becomes the
+        lowest common denominator and NEW_MATCH_UP.  That becomes the
         new lowest common denominator.  */
       size_t i;
+      const char *new_match = new_match_up.get ();
 
       for (i = 0;
           (new_match[i] != '\0'
@@ -1336,6 +1934,14 @@ completion_tracker::recompute_lowest_common_denominator (const char *new_match)
     }
 }
 
+/* See completer.h.  */
+
+void
+completion_tracker::advance_custom_word_point_by (size_t len)
+{
+  m_custom_word_point += len;
+}
+
 /* Build a new C string that is a copy of LCD with the whitespace of
    ORIG/ORIG_LEN preserved.
 
@@ -1378,7 +1984,7 @@ expand_preserving_ws (const char *orig, size_t orig_len,
        {
          while (p_orig < orig_end && *p_orig == ' ')
            res += *p_orig++;
-         p_lcd = skip_spaces_const (p_lcd);
+         p_lcd = skip_spaces (p_lcd);
        }
       else
        {
@@ -1423,12 +2029,21 @@ completion_tracker::build_completion_result (const char *text,
 
   if (m_lowest_common_denominator_unique)
     {
+      /* We don't rely on readline appending the quote char as
+        delimiter as then readline wouldn't append the ' ' after the
+        completion.  */
+      char buf[2] = { (char) quote_char () };
+
+      match_list[0] = reconcat (match_list[0], match_list[0],
+                               buf, (char *) NULL);
       match_list[1] = NULL;
 
-      /* If we already have a space at the end of the match, tell
-        readline to skip appending another.  */
+      /* If the tracker wants to, or we already have a space at the
+        end of the match, tell readline to skip appending
+        another.  */
       bool completion_suppress_append
-       = (match_list[0][strlen (match_list[0]) - 1] == ' ');
+       = (suppress_append_ws ()
+          || match_list[0][strlen (match_list[0]) - 1] == ' ');
 
       return completion_result (match_list, 1, completion_suppress_append);
     }
@@ -1492,14 +2107,6 @@ completion_result::release_match_list ()
   return ret;
 }
 
-/* Compare C strings for std::sort.  */
-
-static bool
-compare_cstrings (const char *str1, const char *str2)
-{
-  return strcmp (str1, str2) < 0;
-}
-
 /* See completer.h  */
 
 void
@@ -1555,14 +2162,20 @@ completion_result::reset_match_list ()
 static char **
 gdb_rl_attempted_completion_function_throw (const char *text, int start, int end)
 {
-  /* Completers must be called twice.  If rl_point (i.e., END) is at
-     column 0, then readline skips the the handle_brkchars phase, and
-     so we create a tracker now in that case too.  */
-  delete current_completion.tracker;
-  current_completion.tracker = new completion_tracker ();
+  /* Completers that provide a custom word point in the
+     handle_brkchars phase also compute their completions then.
+     Completers that leave the completion word handling to readline
+     must be called twice.  If rl_point (i.e., END) is at column 0,
+     then readline skips the handle_brkchars phase, and so we create a
+     tracker now in that case too.  */
+  if (end == 0 || !current_completion.tracker->use_custom_word_point ())
+    {
+      delete current_completion.tracker;
+      current_completion.tracker = new completion_tracker ();
 
-  complete_line (*current_completion.tracker, text,
-                rl_line_buffer, rl_point);
+      complete_line (*current_completion.tracker, text,
+                    rl_line_buffer, rl_point);
+    }
 
   completion_tracker &tracker = *current_completion.tracker;
 
@@ -1580,6 +2193,10 @@ gdb_rl_attempted_completion_function_throw (const char *text, int start, int end
 char **
 gdb_rl_attempted_completion_function (const char *text, int start, int end)
 {
+  /* Restore globals that might have been tweaked in
+     gdb_completion_word_break_characters.  */
+  rl_basic_quote_characters = gdb_org_rl_basic_quote_characters;
+
   /* If we end up returning NULL, either on error, or simple because
      there are no matches, inhibit readline's default filename
      completer.  */
@@ -2239,8 +2856,6 @@ gdb_display_match_list (char **matches, int len, int max,
        }
     }
 }
-\f
-extern initialize_file_ftype _initialize_completer; /* -Wmissing-prototypes */
 
 void
 _initialize_completer (void)
This page took 0.037397 seconds and 4 git commands to generate.