brintos

brintos / llvm-project-archived public Read only

0
0
Text · 48.7 KiB · 73b817e Raw
1420 lines · cpp
1//===-- Options.cpp -------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Interpreter/Options.h"10 11#include <algorithm>12#include <bitset>13#include <map>14#include <set>15 16#include "lldb/Host/OptionParser.h"17#include "lldb/Host/common/DiagnosticsRendering.h"18#include "lldb/Interpreter/CommandCompletions.h"19#include "lldb/Interpreter/CommandInterpreter.h"20#include "lldb/Interpreter/CommandObject.h"21#include "lldb/Interpreter/CommandReturnObject.h"22#include "lldb/Target/Target.h"23#include "lldb/Utility/AnsiTerminal.h"24#include "lldb/Utility/StreamString.h"25#include "llvm/ADT/STLExtras.h"26 27using namespace lldb;28using namespace lldb_private;29 30namespace lldb_private {31 32/// An llvm::Error that represents an option parsing diagnostic.33class OptionParseError34    : public llvm::ErrorInfo<OptionParseError, DiagnosticError> {35  std::vector<DiagnosticDetail> m_details;36 37public:38  using llvm::ErrorInfo<OptionParseError, DiagnosticError>::ErrorInfo;39  OptionParseError(DiagnosticDetail detail)40      : ErrorInfo(std::error_code(EINVAL, std::generic_category())),41        m_details({detail}) {}42  OptionParseError(const Args::ArgEntry &arg, std::string msg)43      : ErrorInfo(std::error_code(EINVAL, std::generic_category())) {44    DiagnosticDetail::SourceLocation sloc;45    if (auto pos = arg.GetPos()) {46      uint16_t len = arg.GetLength();47      sloc = {FileSpec{}, 1, *pos, len, false, true};48    }49    m_details.push_back(DiagnosticDetail{sloc, lldb::eSeverityError, msg, msg});50  }51  std::unique_ptr<CloneableError> Clone() const override {52    return std::make_unique<OptionParseError>(m_details[0]);53  }54  llvm::ArrayRef<DiagnosticDetail> GetDetails() const override {55    return m_details;56  }57  static char ID;58};59 60char OptionParseError::ID;61 62} // namespace lldb_private63 64// Options65Options::Options() { BuildValidOptionSets(); }66 67Options::~Options() = default;68 69void Options::NotifyOptionParsingStarting(ExecutionContext *execution_context) {70  m_seen_options.clear();71  // Let the subclass reset its option values72  OptionParsingStarting(execution_context);73}74 75Status76Options::NotifyOptionParsingFinished(ExecutionContext *execution_context) {77  return OptionParsingFinished(execution_context);78}79 80void Options::OptionSeen(int option_idx) { m_seen_options.insert(option_idx); }81 82// Returns true is set_a is a subset of set_b;  Otherwise returns false.83 84bool Options::IsASubset(const OptionSet &set_a, const OptionSet &set_b) {85  bool is_a_subset = true;86  OptionSet::const_iterator pos_a;87  OptionSet::const_iterator pos_b;88 89  // set_a is a subset of set_b if every member of set_a is also a member of90  // set_b91 92  for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) {93    pos_b = set_b.find(*pos_a);94    if (pos_b == set_b.end())95      is_a_subset = false;96  }97 98  return is_a_subset;99}100 101// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) &&102// !ElementOf (x, set_b) }103 104size_t Options::OptionsSetDiff(const OptionSet &set_a, const OptionSet &set_b,105                               OptionSet &diffs) {106  size_t num_diffs = 0;107  OptionSet::const_iterator pos_a;108  OptionSet::const_iterator pos_b;109 110  for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) {111    pos_b = set_b.find(*pos_a);112    if (pos_b == set_b.end()) {113      ++num_diffs;114      diffs.insert(*pos_a);115    }116  }117 118  return num_diffs;119}120 121// Returns the union of set_a and set_b.  Does not put duplicate members into122// the union.123 124void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b,125                              OptionSet &union_set) {126  OptionSet::const_iterator pos;127  OptionSet::iterator pos_union;128 129  // Put all the elements of set_a into the union.130 131  for (pos = set_a.begin(); pos != set_a.end(); ++pos)132    union_set.insert(*pos);133 134  // Put all the elements of set_b that are not already there into the union.135  for (pos = set_b.begin(); pos != set_b.end(); ++pos) {136    pos_union = union_set.find(*pos);137    if (pos_union == union_set.end())138      union_set.insert(*pos);139  }140}141 142// This is called in the Options constructor, though we could call it lazily if143// that ends up being a performance problem.144 145void Options::BuildValidOptionSets() {146  // Check to see if we already did this.147  if (m_required_options.size() != 0)148    return;149 150  // Check to see if there are any options.151  int num_options = NumCommandOptions();152  if (num_options == 0)153    return;154 155  auto opt_defs = GetDefinitions();156  m_required_options.resize(1);157  m_optional_options.resize(1);158 159  // First count the number of option sets we've got.  Ignore160  // LLDB_ALL_OPTION_SETS...161 162  uint32_t num_option_sets = 0;163 164  for (const auto &def : opt_defs) {165    uint32_t this_usage_mask = def.usage_mask;166    if (this_usage_mask == LLDB_OPT_SET_ALL) {167      if (num_option_sets == 0)168        num_option_sets = 1;169    } else {170      for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) {171        if (this_usage_mask & (1 << j)) {172          if (num_option_sets <= j)173            num_option_sets = j + 1;174        }175      }176    }177  }178 179  if (num_option_sets > 0) {180    m_required_options.resize(num_option_sets);181    m_optional_options.resize(num_option_sets);182 183    for (const auto &def : opt_defs) {184      for (uint32_t j = 0; j < num_option_sets; j++) {185        if (def.usage_mask & 1 << j) {186          if (def.required)187            m_required_options[j].insert(def.short_option);188          else189            m_optional_options[j].insert(def.short_option);190        }191      }192    }193  }194}195 196uint32_t Options::NumCommandOptions() { return GetDefinitions().size(); }197 198Option *Options::GetLongOptions() {199  // Check to see if this has already been done.200  if (m_getopt_table.empty()) {201    auto defs = GetDefinitions();202    if (defs.empty())203      return nullptr;204 205    std::map<int, uint32_t> option_seen;206 207    m_getopt_table.resize(defs.size() + 1);208    for (size_t i = 0; i < defs.size(); ++i) {209      const int short_opt = defs[i].short_option;210 211      m_getopt_table[i].definition = &defs[i];212      m_getopt_table[i].flag = nullptr;213      m_getopt_table[i].val = short_opt;214 215      auto [pos, inserted] = option_seen.try_emplace(short_opt, i);216      if (!inserted && short_opt) {217        m_getopt_table[i].val = 0;218        StreamString strm;219        if (defs[i].HasShortOption())220          Debugger::ReportError(221              llvm::formatv(222                  "option[{0}] --{1} has a short option -{2} that "223                  "conflicts with option[{3}] --{4}, short option won't "224                  "be used for --{5}",225                  i, defs[i].long_option, short_opt, pos->second,226                  m_getopt_table[pos->second].definition->long_option,227                  defs[i].long_option)228                  .str());229        else230          Debugger::ReportError(231              llvm::formatv(232                  "option[{0}] --{1} has a short option {2:x} that "233                  "conflicts with option[{3}] --{4}, short option won't "234                  "be used for --{5}",235                  (int)i, defs[i].long_option, short_opt, pos->second,236                  m_getopt_table[pos->second].definition->long_option,237                  defs[i].long_option)238                  .str());239      }240    }241 242    // getopt_long_only requires a NULL final entry in the table:243 244    m_getopt_table.back().definition = nullptr;245    m_getopt_table.back().flag = nullptr;246    m_getopt_table.back().val = 0;247  }248 249  if (m_getopt_table.empty())250    return nullptr;251 252  return &m_getopt_table.front();253}254 255// This function takes INDENT, which tells how many spaces to output at the256// front of each line; SPACES, which is a string containing 80 spaces; and257// TEXT, which is the text that is to be output.   It outputs the text, on258// multiple lines if necessary, to RESULT, with INDENT spaces at the front of259// each line.  It breaks lines on spaces, tabs or newlines, shortening the line260// if necessary to not break in the middle of a word.  It assumes that each261// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.262 263void Options::OutputFormattedUsageText(Stream &strm,264                                       const OptionDefinition &option_def,265                                       uint32_t output_max_columns,266                                       bool use_color) {267  std::string actual_text;268  if (option_def.validator) {269    const char *condition = option_def.validator->ShortConditionString();270    if (condition) {271      actual_text = "[";272      actual_text.append(condition);273      actual_text.append("] ");274    }275  }276  actual_text.append(277      ansi::FormatAnsiTerminalCodes(option_def.usage_text, use_color));278  const size_t visible_length = ansi::ColumnWidth(actual_text);279 280  // Will it all fit on one line?281 282  if (static_cast<uint32_t>(visible_length + strm.GetIndentLevel()) <283      output_max_columns) {284    // Output it as a single line.285    strm.Indent(ansi::FormatAnsiTerminalCodes(actual_text, use_color));286    strm.EOL();287  } else {288    // We need to break it up into multiple lines.289 290    int text_width = output_max_columns - strm.GetIndentLevel() - 1;291    int start = 0;292    int end = start;293    int final_end = visible_length;294    int sub_len;295 296    while (end < final_end) {297      // Don't start the 'text' on a space, since we're already outputting the298      // indentation.299      while ((start < final_end) && (actual_text[start] == ' '))300        start++;301 302      end = start + text_width;303      if (end > final_end)304        end = final_end;305      else {306        // If we're not at the end of the text, make sure we break the line on307        // white space.308        while (end > start && actual_text[end] != ' ' &&309               actual_text[end] != '\t' && actual_text[end] != '\n')310          end--;311      }312 313      sub_len = end - start;314      if (start != 0)315        strm.EOL();316      strm.Indent();317      assert(start < final_end);318      assert(start + sub_len <= final_end);319      strm.PutCString(ansi::FormatAnsiTerminalCodes(320          llvm::StringRef(actual_text.c_str() + start, sub_len), use_color));321      start = end + 1;322    }323    strm.EOL();324  }325}326 327bool Options::SupportsLongOption(const char *long_option) {328  if (!long_option || !long_option[0])329    return false;330 331  auto opt_defs = GetDefinitions();332  if (opt_defs.empty())333    return false;334 335  const char *long_option_name = long_option;336  if (long_option[0] == '-' && long_option[1] == '-')337    long_option_name += 2;338 339  for (auto &def : opt_defs) {340    if (!def.long_option)341      continue;342 343    if (strcmp(def.long_option, long_option_name) == 0)344      return true;345  }346 347  return false;348}349 350enum OptionDisplayType {351  eDisplayBestOption,352  eDisplayShortOption,353  eDisplayLongOption354};355 356static bool PrintOption(const OptionDefinition &opt_def,357                        OptionDisplayType display_type, const char *header,358                        const char *footer, bool show_optional, Stream &strm) {359  if (display_type == eDisplayShortOption && !opt_def.HasShortOption())360    return false;361 362  if (header && header[0])363    strm.PutCString(header);364 365  if (show_optional && !opt_def.required)366    strm.PutChar('[');367  const bool show_short_option =368      opt_def.HasShortOption() && display_type != eDisplayLongOption;369  if (show_short_option)370    strm.Printf("-%c", opt_def.short_option);371  else372    strm.Printf("--%s", opt_def.long_option);373  switch (opt_def.option_has_arg) {374  case OptionParser::eNoArgument:375    break;376  case OptionParser::eRequiredArgument:377    strm.Printf(" <%s>", CommandObject::GetArgumentName(opt_def.argument_type));378    break;379 380  case OptionParser::eOptionalArgument:381    strm.Printf("%s[<%s>]", show_short_option ? "" : "=",382                CommandObject::GetArgumentName(opt_def.argument_type));383    break;384  }385  if (show_optional && !opt_def.required)386    strm.PutChar(']');387  if (footer && footer[0])388    strm.PutCString(footer);389  return true;390}391 392void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd,393                                  uint32_t screen_width, bool use_color) {394  auto opt_defs = GetDefinitions();395  const uint32_t save_indent_level = strm.GetIndentLevel();396  llvm::StringRef name = cmd.GetCommandName();397  StreamString arguments_str;398  cmd.GetFormattedCommandArguments(arguments_str);399 400  const uint32_t num_options = NumCommandOptions();401  if (num_options == 0)402    return;403 404  const bool only_print_args = cmd.IsDashDashCommand();405  if (!only_print_args)406    strm.PutCString("\nCommand Options Usage:\n");407 408  strm.IndentMore(2);409 410  // First, show each usage level set of options, e.g. <cmd> [options-for-411  // level-0]412  //                                                   <cmd>413  //                                                   [options-for-level-1]414  //                                                   etc.415 416  if (!only_print_args) {417    uint32_t num_option_sets = GetRequiredOptions().size();418    for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) {419      if (opt_set > 0)420        strm.Printf("\n");421      strm.Indent(name);422 423      // Different option sets may require different args.424      StreamString args_str;425      uint32_t opt_set_mask = 1 << opt_set;426      cmd.GetFormattedCommandArguments(args_str, opt_set_mask);427 428      // First go through and print all options that take no arguments as a429      // single string. If a command has "-a" "-b" and "-c", this will show up430      // as [-abc]431 432      // We use a set here so that they will be sorted.433      std::set<int> required_options;434      std::set<int> optional_options;435 436      for (auto &def : opt_defs) {437        if (def.usage_mask & opt_set_mask && def.HasShortOption() &&438            def.option_has_arg == OptionParser::eNoArgument) {439          if (def.required) {440            required_options.insert(def.short_option);441          } else {442            optional_options.insert(def.short_option);443          }444        }445      }446 447      if (!required_options.empty()) {448        strm.PutCString(" -");449        for (int short_option : required_options)450          strm.PutChar(short_option);451      }452 453      if (!optional_options.empty()) {454        strm.PutCString(" [-");455        for (int short_option : optional_options)456          strm.PutChar(short_option);457        strm.PutChar(']');458      }459 460      // First go through and print the required options (list them up front).461      for (auto &def : opt_defs) {462        if (def.usage_mask & opt_set_mask && def.HasShortOption() &&463            def.required && def.option_has_arg != OptionParser::eNoArgument)464          PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);465      }466 467      // Now go through again, and this time only print the optional options.468      for (auto &def : opt_defs) {469        if (def.usage_mask & opt_set_mask && !def.required &&470            def.option_has_arg != OptionParser::eNoArgument)471          PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);472      }473 474      if (args_str.GetSize() > 0) {475        if (cmd.WantsRawCommandString())476          strm.Printf(" --");477        strm << " " << args_str.GetString();478      }479    }480  }481 482  if ((only_print_args || cmd.WantsRawCommandString()) &&483      arguments_str.GetSize() > 0) {484    if (!only_print_args)485      strm.PutChar('\n');486    strm.Indent(name);487    strm << " " << arguments_str.GetString();488  }489 490  if (!only_print_args) {491    strm.Printf("\n\n");492 493    // Now print out all the detailed information about the various options:494    // long form, short form and help text:495    //   -short <argument> ( --long_name <argument> )496    //   help text497 498    strm.IndentMore(5);499 500    // Put the command options in a sorted container, so we can output501    // them alphabetically by short_option.502    std::multimap<int, uint32_t> options_ordered;503    for (auto def : llvm::enumerate(opt_defs))504      options_ordered.insert(505          std::make_pair(def.value().short_option, def.index()));506 507    // Go through each option, find the table entry and write out the detailed508    // help information for that option.509 510    bool first_option_printed = false;511 512    for (auto pos : options_ordered) {513      // Put a newline separation between arguments514      if (first_option_printed)515        strm.EOL();516      else517        first_option_printed = true;518 519      OptionDefinition opt_def = opt_defs[pos.second];520 521      strm.Indent();522      if (opt_def.short_option && opt_def.HasShortOption()) {523        PrintOption(opt_def, eDisplayShortOption, nullptr, nullptr, false,524                    strm);525        PrintOption(opt_def, eDisplayLongOption, " ( ", " )", false, strm);526      } else {527        // Short option is not printable, just print long option528        PrintOption(opt_def, eDisplayLongOption, nullptr, nullptr, false, strm);529      }530      strm.EOL();531 532      strm.IndentMore(5);533 534      if (opt_def.usage_text)535        OutputFormattedUsageText(strm, opt_def, screen_width, use_color);536      if (!opt_def.enum_values.empty()) {537        strm.Indent();538        strm.Printf("Values: ");539        bool is_first = true;540        for (const auto &enum_value : opt_def.enum_values) {541          if (is_first) {542            strm.Printf("%s", enum_value.string_value);543            is_first = false;544          }545          else546            strm.Printf(" | %s", enum_value.string_value);547        }548        strm.EOL();549      }550      strm.IndentLess(5);551    }552  }553 554  // Restore the indent level555  strm.SetIndentLevel(save_indent_level);556}557 558llvm::Error Options::VerifyOptions() {559  bool options_are_valid = false;560 561  int num_levels = GetRequiredOptions().size();562  if (num_levels) {563    for (int i = 0; i < num_levels && !options_are_valid; ++i) {564      // This is the correct set of options if:  1). m_seen_options contains565      // all of m_required_options[i] (i.e. all the required options at this566      // level are a subset of m_seen_options); AND 2). { m_seen_options -567      // m_required_options[i] is a subset of m_options_options[i] (i.e. all568      // the rest of m_seen_options are in the set of optional options at this569      // level.570 571      // Check to see if all of m_required_options[i] are a subset of572      // m_seen_options573      if (IsASubset(GetRequiredOptions()[i], m_seen_options)) {574        // Construct the set difference: remaining_options = {m_seen_options} -575        // {m_required_options[i]}576        OptionSet remaining_options;577        OptionsSetDiff(m_seen_options, GetRequiredOptions()[i],578                       remaining_options);579        // Check to see if remaining_options is a subset of580        // m_optional_options[i]581        if (IsASubset(remaining_options, GetOptionalOptions()[i]))582          options_are_valid = true;583      }584    }585  } else {586    options_are_valid = true;587  }588 589  if (!options_are_valid)590    return llvm::createStringError(591        "invalid combination of options for the given command");592 593  return llvm::Error::success();594}595 596// This function is called when we have been given a potentially incomplete set597// of options, such as when an alias has been defined (more options might be598// added at at the time the alias is invoked).  We need to verify that the599// options in the set m_seen_options are all part of a set that may be used600// together, but m_seen_options may be missing some of the "required" options.601llvm::Error Options::VerifyPartialOptions() {602  bool options_are_valid = false;603 604  int num_levels = GetRequiredOptions().size();605  if (num_levels) {606    for (int i = 0; i < num_levels && !options_are_valid; ++i) {607      // In this case we are treating all options as optional rather than608      // required. Therefore a set of options is correct if m_seen_options is a609      // subset of the union of m_required_options and m_optional_options.610      OptionSet union_set;611      OptionsSetUnion(GetRequiredOptions()[i], GetOptionalOptions()[i],612                      union_set);613      if (IsASubset(m_seen_options, union_set))614        options_are_valid = true;615    }616  }617 618  if (!options_are_valid)619    return llvm::createStringError(620        "invalid combination of options for the given command");621 622  return llvm::Error::success();623}624 625bool Options::HandleOptionCompletion(CompletionRequest &request,626                                     OptionElementVector &opt_element_vector,627                                     CommandInterpreter &interpreter) {628  // For now we just scan the completions to see if the cursor position is in629  // an option or its argument.  Otherwise we'll call HandleArgumentCompletion.630  // In the future we can use completion to validate options as well if we631  // want.632 633  auto opt_defs = GetDefinitions();634 635  llvm::StringRef cur_opt_str = request.GetCursorArgumentPrefix();636  const bool use_color = interpreter.GetDebugger().GetUseColor();637 638  for (size_t i = 0; i < opt_element_vector.size(); i++) {639    size_t opt_pos = static_cast<size_t>(opt_element_vector[i].opt_pos);640    size_t opt_arg_pos = static_cast<size_t>(opt_element_vector[i].opt_arg_pos);641    int opt_defs_index = opt_element_vector[i].opt_defs_index;642    if (opt_pos == request.GetCursorIndex()) {643      // We're completing the option itself.644 645      if (opt_defs_index == OptionArgElement::eBareDash) {646        // We're completing a bare dash.  That means all options are open.647        // FIXME: We should scan the other options provided and only complete648        // options649        // within the option group they belong to.650        std::string opt_str = "-a";651 652        for (auto &def : opt_defs) {653          if (!def.short_option)654            continue;655          opt_str[1] = def.short_option;656          request.AddCompletion(opt_str, ansi::FormatAnsiTerminalCodes(657                                             def.usage_text, use_color));658        }659 660        return true;661      } else if (opt_defs_index == OptionArgElement::eBareDoubleDash) {662        std::string full_name("--");663        for (auto &def : opt_defs) {664          if (!def.short_option)665            continue;666 667          full_name.erase(full_name.begin() + 2, full_name.end());668          full_name.append(def.long_option);669          request.AddCompletion(full_name, ansi::FormatAnsiTerminalCodes(670                                               def.usage_text, use_color));671        }672        return true;673      } else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) {674        // We recognized it, if it an incomplete long option, complete it675        // anyway (getopt_long_only is happy with shortest unique string, but676        // it's still a nice thing to do.)  Otherwise return The string so the677        // upper level code will know this is a full match and add the " ".678        const OptionDefinition &opt = opt_defs[opt_defs_index];679        llvm::StringRef long_option = opt.long_option;680        if (cur_opt_str.starts_with("--") && cur_opt_str != long_option) {681          request.AddCompletion(682              "--" + long_option.str(),683              ansi::FormatAnsiTerminalCodes(opt.usage_text, use_color));684          return true;685        } else686          request.AddCompletion(request.GetCursorArgumentPrefix());687        return true;688      } else {689        // FIXME - not handling wrong options yet:690        // Check to see if they are writing a long option & complete it.691        // I think we will only get in here if the long option table has two692        // elements693        // that are not unique up to this point.  getopt_long_only does694        // shortest unique match for long options already.695        if (cur_opt_str.consume_front("--")) {696          for (auto &def : opt_defs) {697            llvm::StringRef long_option(def.long_option);698            if (long_option.starts_with(cur_opt_str))699              request.AddCompletion(700                  "--" + long_option.str(),701                  ansi::FormatAnsiTerminalCodes(def.usage_text, use_color));702          }703        }704        return true;705      }706 707    } else if (opt_arg_pos == request.GetCursorIndex()) {708      // Okay the cursor is on the completion of an argument. See if it has a709      // completion, otherwise return no matches.  Note, opt_defs_index == -1710      // means we're after an option, but that option doesn't exist.  We'll711      // end up treating that as an argument.  Not sure we can do much better.712      if (opt_defs_index != -1) {713        HandleOptionArgumentCompletion(request, opt_element_vector, i,714                                       interpreter);715        return true;716      } else {717        // No completion callback means no completions...718        return true;719      }720 721    } else {722      // Not the last element, keep going.723      continue;724    }725  }726  return false;727}728 729void Options::HandleOptionArgumentCompletion(730    CompletionRequest &request, OptionElementVector &opt_element_vector,731    int opt_element_index, CommandInterpreter &interpreter) {732  auto opt_defs = GetDefinitions();733  std::unique_ptr<SearchFilter> filter_up;734 735  int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;736 737  // See if this is an enumeration type option, and if so complete it here:738  const auto &enum_values = opt_defs[opt_defs_index].enum_values;739  if (!enum_values.empty())740    for (const auto &enum_value : enum_values)741      request.TryCompleteCurrentArg(enum_value.string_value);742 743  // If this is a source file or symbol type completion, and  there is a -shlib744  // option somewhere in the supplied arguments, then make a search filter for745  // that shared library.746  // FIXME: Do we want to also have an "OptionType" so we don't have to match747  // string names?748 749  uint32_t completion_mask = opt_defs[opt_defs_index].completion_type;750 751  if (completion_mask == 0) {752    lldb::CommandArgumentType option_arg_type =753        opt_defs[opt_defs_index].argument_type;754    if (option_arg_type != eArgTypeNone) {755      const CommandObject::ArgumentTableEntry *arg_entry =756          CommandObject::FindArgumentDataByType(757              opt_defs[opt_defs_index].argument_type);758      if (arg_entry)759        completion_mask = arg_entry->completion_type;760    }761  }762 763  if (completion_mask & lldb::eSourceFileCompletion ||764      completion_mask & lldb::eSymbolCompletion) {765    for (size_t i = 0; i < opt_element_vector.size(); i++) {766      int cur_defs_index = opt_element_vector[i].opt_defs_index;767 768      // trying to use <0 indices will definitely cause problems769      if (cur_defs_index == OptionArgElement::eUnrecognizedArg ||770          cur_defs_index == OptionArgElement::eBareDash ||771          cur_defs_index == OptionArgElement::eBareDoubleDash)772        continue;773 774      int cur_arg_pos = opt_element_vector[i].opt_arg_pos;775      const char *cur_opt_name = opt_defs[cur_defs_index].long_option;776 777      // If this is the "shlib" option and there was an argument provided,778      // restrict it to that shared library.779      if (cur_opt_name && strcmp(cur_opt_name, "shlib") == 0 &&780          cur_arg_pos != -1) {781        const char *module_name =782            request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos);783        if (module_name) {784          FileSpec module_spec(module_name);785          lldb::TargetSP target_sp =786              interpreter.GetDebugger().GetSelectedTarget();787          // Search filters require a target...788          if (target_sp)789            filter_up =790                std::make_unique<SearchFilterByModule>(target_sp, module_spec);791        }792        break;793      }794    }795  }796 797  lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(798      interpreter, completion_mask, request, filter_up.get());799}800 801void OptionGroupOptions::Append(OptionGroup *group) {802  auto group_option_defs = group->GetDefinitions();803  for (uint32_t i = 0; i < group_option_defs.size(); ++i) {804    m_option_infos.push_back(OptionInfo(group, i));805    m_option_defs.push_back(group_option_defs[i]);806  }807}808 809const OptionGroup *OptionGroupOptions::GetGroupWithOption(char short_opt) {810  for (uint32_t i = 0; i < m_option_defs.size(); i++) {811    OptionDefinition opt_def = m_option_defs[i];812    if (opt_def.short_option == short_opt)813      return m_option_infos[i].option_group;814  }815  return nullptr;816}817 818void OptionGroupOptions::Append(OptionGroup *group, uint32_t src_mask,819                                uint32_t dst_mask) {820  auto group_option_defs = group->GetDefinitions();821  for (uint32_t i = 0; i < group_option_defs.size(); ++i) {822    if (group_option_defs[i].usage_mask & src_mask) {823      m_option_infos.push_back(OptionInfo(group, i));824      m_option_defs.push_back(group_option_defs[i]);825      m_option_defs.back().usage_mask = dst_mask;826    }827  }828}829 830void OptionGroupOptions::Append(831    OptionGroup *group, llvm::ArrayRef<llvm::StringRef> exclude_long_options) {832  auto group_option_defs = group->GetDefinitions();833  for (uint32_t i = 0; i < group_option_defs.size(); ++i) {834    const auto &definition = group_option_defs[i];835    if (llvm::is_contained(exclude_long_options, definition.long_option))836      continue;837 838    m_option_infos.push_back(OptionInfo(group, i));839    m_option_defs.push_back(definition);840  }841}842 843void OptionGroupOptions::Finalize() {844  m_did_finalize = true;845}846 847Status OptionGroupOptions::SetOptionValue(uint32_t option_idx,848                                          llvm::StringRef option_value,849                                          ExecutionContext *execution_context) {850  // After calling OptionGroupOptions::Append(...), you must finalize the851  // groups by calling OptionGroupOptions::Finlize()852  assert(m_did_finalize);853  Status error;854  if (option_idx < m_option_infos.size()) {855    error = m_option_infos[option_idx].option_group->SetOptionValue(856        m_option_infos[option_idx].option_index, option_value,857        execution_context);858 859  } else {860    error =861        Status::FromErrorString("invalid option index"); // Shouldn't happen...862  }863  return error;864}865 866void OptionGroupOptions::OptionParsingStarting(867    ExecutionContext *execution_context) {868  std::set<OptionGroup *> group_set;869  OptionInfos::iterator pos, end = m_option_infos.end();870  for (pos = m_option_infos.begin(); pos != end; ++pos) {871    OptionGroup *group = pos->option_group;872    if (group_set.find(group) == group_set.end()) {873      group->OptionParsingStarting(execution_context);874      group_set.insert(group);875    }876  }877}878Status879OptionGroupOptions::OptionParsingFinished(ExecutionContext *execution_context) {880  std::set<OptionGroup *> group_set;881  Status error;882  OptionInfos::iterator pos, end = m_option_infos.end();883  for (pos = m_option_infos.begin(); pos != end; ++pos) {884    OptionGroup *group = pos->option_group;885    if (group_set.find(group) == group_set.end()) {886      error = group->OptionParsingFinished(execution_context);887      group_set.insert(group);888      if (error.Fail())889        return error;890    }891  }892  return error;893}894 895// OptionParser permutes the arguments while processing them, so we create a896// temporary array holding to avoid modification of the input arguments. The897// options themselves are never modified, but the API expects a char * anyway,898// hence the const_cast.899static std::vector<char *> GetArgvForParsing(const Args &args) {900  std::vector<char *> result;901  // OptionParser always skips the first argument as it is based on getopt().902  result.push_back(const_cast<char *>("<FAKE-ARG0>"));903  for (const Args::ArgEntry &entry : args)904    result.push_back(const_cast<char *>(entry.c_str()));905  result.push_back(nullptr);906  return result;907}908 909// Given a permuted argument, find it's position in the original Args vector.910static Args::const_iterator FindOriginalIter(const char *arg,911                                             const Args &original) {912  return llvm::find_if(913      original, [arg](const Args::ArgEntry &D) { return D.c_str() == arg; });914}915 916// Given a permuted argument, find it's index in the original Args vector.917static size_t FindOriginalIndex(const char *arg, const Args &original) {918  return std::distance(original.begin(), FindOriginalIter(arg, original));919}920 921// Construct a new Args object, consisting of the entries from the original922// arguments, but in the permuted order.923static Args ReconstituteArgsAfterParsing(llvm::ArrayRef<char *> parsed,924                                         const Args &original) {925  Args result;926  for (const char *arg : parsed) {927    auto pos = FindOriginalIter(arg, original);928    assert(pos != original.end());929    result.AppendArgument(pos->ref(), pos->GetQuoteChar());930  }931  return result;932}933 934static size_t FindArgumentIndexForOption(const Args &args,935                                         const Option &long_option) {936  std::string short_opt = llvm::formatv("-{0}", char(long_option.val)).str();937  std::string long_opt =938      std::string(llvm::formatv("--{0}", long_option.definition->long_option));939  for (const auto &entry : llvm::enumerate(args)) {940    if (entry.value().ref().starts_with(short_opt) ||941        entry.value().ref().starts_with(long_opt))942      return entry.index();943  }944 945  return size_t(-1);946}947 948static std::string BuildShortOptions(const Option *long_options) {949  std::string storage;950  llvm::raw_string_ostream sstr(storage);951 952  // Leading : tells getopt to return a : for a missing option argument AND to953  // suppress error messages.954  sstr << ":";955 956  for (size_t i = 0; long_options[i].definition != nullptr; ++i) {957    if (long_options[i].flag == nullptr) {958      sstr << (char)long_options[i].val;959      switch (long_options[i].definition->option_has_arg) {960      default:961      case OptionParser::eNoArgument:962        break;963      case OptionParser::eRequiredArgument:964        sstr << ":";965        break;966      case OptionParser::eOptionalArgument:967        sstr << "::";968        break;969      }970    }971  }972  return storage;973}974 975llvm::Expected<Args> Options::ParseAlias(const Args &args,976                                         OptionArgVector *option_arg_vector,977                                         std::string &input_line) {978  Option *long_options = GetLongOptions();979 980  if (long_options == nullptr) {981    return llvm::createStringError("Invalid long options");982  }983 984  std::string short_options = BuildShortOptions(long_options);985 986  Args args_copy = args;987  std::vector<char *> argv = GetArgvForParsing(args);988 989  std::unique_lock<std::mutex> lock;990  OptionParser::Prepare(lock);991  int val;992  while (true) {993    int long_options_index = -1;994    val = OptionParser::Parse(argv, short_options, long_options,995                              &long_options_index);996 997    if (val == ':') {998      return llvm::createStringError(llvm::inconvertibleErrorCode(),999                                     "last option requires an argument");1000    }1001 1002    if (val == -1)1003      break;1004 1005    if (val == '?') {1006      return llvm::createStringError("Unknown or ambiguous option");1007    }1008 1009    if (val == 0)1010      continue;1011 1012    OptionSeen(val);1013 1014    // Look up the long option index1015    if (long_options_index == -1) {1016      for (int j = 0; long_options[j].definition || long_options[j].flag ||1017                      long_options[j].val;1018           ++j) {1019        if (long_options[j].val == val) {1020          long_options_index = j;1021          break;1022        }1023      }1024    }1025 1026    // See if the option takes an argument, and see if one was supplied.1027    if (long_options_index == -1) {1028      return llvm::createStringError(1029          llvm::formatv("Invalid option with value '{0}'.", char(val)).str());1030    }1031 1032    StreamString option_str;1033    option_str.Printf("-%c", val);1034    const OptionDefinition *def = long_options[long_options_index].definition;1035    int has_arg =1036        (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;1037 1038    const char *option_arg = nullptr;1039    switch (has_arg) {1040    case OptionParser::eRequiredArgument:1041      if (OptionParser::GetOptionArgument() == nullptr) {1042        return llvm::createStringError(1043            llvm::formatv("Option '{0}' is missing argument specifier.",1044                          option_str.GetString())1045                .str());1046      }1047      [[fallthrough]];1048    case OptionParser::eOptionalArgument:1049      option_arg = OptionParser::GetOptionArgument();1050      [[fallthrough]];1051    case OptionParser::eNoArgument:1052      break;1053    default:1054      return llvm::createStringError(1055          llvm::formatv("error with options table; invalid value in has_arg "1056                        "field for option '{0}'.",1057                        char(val))1058              .str());1059    }1060    // Find option in the argument list; also see if it was supposed to take an1061    // argument and if one was supplied.  Remove option (and argument, if1062    // given) from the argument list.  Also remove them from the1063    // raw_input_string, if one was passed in.1064    // Note: We also need to preserve any option argument values that were1065    // surrounded by backticks, as we lose track of them in the1066    // option_args_vector.1067    size_t idx =1068        FindArgumentIndexForOption(args_copy, long_options[long_options_index]);1069    std::string option_to_insert;1070    if (option_arg) {1071      if (idx != size_t(-1) && has_arg) {1072        bool arg_has_backtick = args_copy[idx + 1].GetQuoteChar() == '`';1073        if (arg_has_backtick)1074          option_to_insert = "`";1075        option_to_insert += option_arg;1076        if (arg_has_backtick)1077          option_to_insert += "`";1078      } else1079        option_to_insert = option_arg;1080    } else1081      option_to_insert = CommandInterpreter::g_no_argument;1082 1083    option_arg_vector->emplace_back(std::string(option_str.GetString()),1084                                    has_arg, option_to_insert);1085 1086    if (idx == size_t(-1))1087      continue;1088 1089    if (!input_line.empty()) {1090      llvm::StringRef tmp_arg = args_copy[idx].ref();1091      size_t pos = input_line.find(tmp_arg);1092      if (pos != std::string::npos)1093        input_line.erase(pos, tmp_arg.size());1094    }1095    args_copy.DeleteArgumentAtIndex(idx);1096    if ((option_to_insert != CommandInterpreter::g_no_argument) &&1097        (OptionParser::GetOptionArgument() != nullptr) &&1098        (idx < args_copy.GetArgumentCount()) &&1099        (args_copy[idx].ref() == OptionParser::GetOptionArgument())) {1100      if (input_line.size() > 0) {1101        size_t pos = input_line.find(option_to_insert);1102        if (pos != std::string::npos)1103          input_line.erase(pos, option_to_insert.size());1104      }1105      args_copy.DeleteArgumentAtIndex(idx);1106    }1107  }1108 1109  return std::move(args_copy);1110}1111 1112OptionElementVector Options::ParseForCompletion(const Args &args,1113                                                uint32_t cursor_index) {1114  OptionElementVector option_element_vector;1115  Option *long_options = GetLongOptions();1116  option_element_vector.clear();1117 1118  if (long_options == nullptr)1119    return option_element_vector;1120 1121  std::string short_options = BuildShortOptions(long_options);1122 1123  std::unique_lock<std::mutex> lock;1124  OptionParser::Prepare(lock);1125  OptionParser::EnableError(false);1126 1127  int val;1128  auto opt_defs = GetDefinitions();1129 1130  std::vector<char *> dummy_vec = GetArgvForParsing(args);1131 1132  bool failed_once = false;1133  uint32_t dash_dash_pos = -1;1134 1135  while (true) {1136    bool missing_argument = false;1137    int long_options_index = -1;1138 1139    val = OptionParser::Parse(dummy_vec, short_options, long_options,1140                              &long_options_index);1141 1142    if (val == -1) {1143      // When we're completing a "--" which is the last option on line,1144      if (failed_once)1145        break;1146 1147      failed_once = true;1148 1149      // If this is a bare  "--" we mark it as such so we can complete it1150      // successfully later.  Handling the "--" is a little tricky, since that1151      // may mean end of options or arguments, or the user might want to1152      // complete options by long name.  I make this work by checking whether1153      // the cursor is in the "--" argument, and if so I assume we're1154      // completing the long option, otherwise I let it pass to1155      // OptionParser::Parse which will terminate the option parsing.  Note, in1156      // either case we continue parsing the line so we can figure out what1157      // other options were passed.  This will be useful when we come to1158      // restricting completions based on what other options we've seen on the1159      // line.1160 1161      if (static_cast<size_t>(OptionParser::GetOptionIndex()) <1162              dummy_vec.size() &&1163          (strcmp(dummy_vec[OptionParser::GetOptionIndex() - 1], "--") == 0)) {1164        dash_dash_pos = FindOriginalIndex(1165            dummy_vec[OptionParser::GetOptionIndex() - 1], args);1166        if (dash_dash_pos == cursor_index) {1167          option_element_vector.push_back(1168              OptionArgElement(OptionArgElement::eBareDoubleDash, dash_dash_pos,1169                               OptionArgElement::eBareDoubleDash));1170          continue;1171        } else1172          break;1173      } else1174        break;1175    } else if (val == '?') {1176      option_element_vector.push_back(OptionArgElement(1177          OptionArgElement::eUnrecognizedArg,1178          FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1179                            args),1180          OptionArgElement::eUnrecognizedArg));1181      continue;1182    } else if (val == 0) {1183      continue;1184    } else if (val == ':') {1185      // This is a missing argument.1186      val = OptionParser::GetOptionErrorCause();1187      missing_argument = true;1188    }1189 1190    OptionSeen(val);1191 1192    // Look up the long option index1193    if (long_options_index == -1) {1194      for (int j = 0; long_options[j].definition || long_options[j].flag ||1195                      long_options[j].val;1196           ++j) {1197        if (long_options[j].val == val) {1198          long_options_index = j;1199          break;1200        }1201      }1202    }1203 1204    // See if the option takes an argument, and see if one was supplied.1205    if (long_options_index >= 0) {1206      int opt_defs_index = -1;1207      for (size_t i = 0; i < opt_defs.size(); i++) {1208        if (opt_defs[i].short_option != val)1209          continue;1210        opt_defs_index = i;1211        break;1212      }1213 1214      const OptionDefinition *def = long_options[long_options_index].definition;1215      int has_arg =1216          (def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;1217      switch (has_arg) {1218      case OptionParser::eNoArgument:1219        option_element_vector.push_back(OptionArgElement(1220            opt_defs_index,1221            FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1222                              args),1223            0));1224        break;1225      case OptionParser::eRequiredArgument:1226        if (OptionParser::GetOptionArgument() != nullptr) {1227          int arg_index;1228          if (missing_argument)1229            arg_index = -1;1230          else1231            arg_index = OptionParser::GetOptionIndex() - 2;1232 1233          option_element_vector.push_back(OptionArgElement(1234              opt_defs_index,1235              FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],1236                                args),1237              arg_index));1238        } else {1239          option_element_vector.push_back(OptionArgElement(1240              opt_defs_index,1241              FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1242                                args),1243              -1));1244        }1245        break;1246      case OptionParser::eOptionalArgument:1247        option_element_vector.push_back(OptionArgElement(1248            opt_defs_index,1249            FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],1250                              args),1251            FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1252                              args)));1253        break;1254      default:1255        // The options table is messed up.  Here we'll just continue1256        option_element_vector.push_back(OptionArgElement(1257            OptionArgElement::eUnrecognizedArg,1258            FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1259                              args),1260            OptionArgElement::eUnrecognizedArg));1261        break;1262      }1263    } else {1264      option_element_vector.push_back(OptionArgElement(1265          OptionArgElement::eUnrecognizedArg,1266          FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1267                            args),1268          OptionArgElement::eUnrecognizedArg));1269    }1270  }1271 1272  // Finally we have to handle the case where the cursor index points at a1273  // single "-".  We want to mark that in the option_element_vector, but only1274  // if it is not after the "--".  But it turns out that OptionParser::Parse1275  // just ignores an isolated "-".  So we have to look it up by hand here.  We1276  // only care if it is AT the cursor position. Note, a single quoted dash is1277  // not the same as a single dash...1278 1279  const Args::ArgEntry &cursor = args[cursor_index];1280  if ((static_cast<int32_t>(dash_dash_pos) == -1 ||1281       cursor_index < dash_dash_pos) &&1282      !cursor.IsQuoted() && cursor.ref() == "-") {1283    option_element_vector.push_back(1284        OptionArgElement(OptionArgElement::eBareDash, cursor_index,1285                         OptionArgElement::eBareDash));1286  }1287  return option_element_vector;1288}1289 1290llvm::Expected<Args> Options::Parse(const Args &args,1291                                    ExecutionContext *execution_context,1292                                    lldb::PlatformSP platform_sp,1293                                    bool require_validation) {1294  Status error;1295  Option *long_options = GetLongOptions();1296  if (long_options == nullptr) {1297    return llvm::createStringError("invalid long options");1298  }1299 1300  std::string short_options = BuildShortOptions(long_options);1301  std::vector<char *> argv = GetArgvForParsing(args);1302 1303  std::unique_lock<std::mutex> lock;1304  OptionParser::Prepare(lock);1305  while (true) {1306    int long_options_index = -1;1307    int val = OptionParser::Parse(argv, short_options, long_options,1308                                  &long_options_index);1309 1310    if (val == ':') {1311      error = Status::FromErrorString("last option requires an argument");1312      break;1313    }1314 1315    if (val == -1)1316      break;1317 1318    // Did we get an error?1319    if (val == '?') {1320      // Account for "argv[0]" and that it points to the next option.1321      int idx = OptionParser::GetOptionIndex() - 2;1322      if (idx >= 0 && (size_t)idx < args.GetArgumentCount())1323        error = Status::FromError(llvm::make_error<OptionParseError>(1324            args[idx], "unknown or ambiguous option"));1325      else1326        error = Status("unknown or ambiguous option");1327 1328      break;1329    }1330    // The option auto-set itself1331    if (val == 0)1332      continue;1333 1334    OptionSeen(val);1335 1336    // Lookup the long option index1337    if (long_options_index == -1) {1338      for (int i = 0; long_options[i].definition || long_options[i].flag ||1339                      long_options[i].val;1340           ++i) {1341        if (long_options[i].val == val) {1342          long_options_index = i;1343          break;1344        }1345      }1346    }1347    // Call the callback with the option1348    if (long_options_index >= 0 &&1349        long_options[long_options_index].definition) {1350      const OptionDefinition *def = long_options[long_options_index].definition;1351 1352      if (!platform_sp) {1353        // User did not pass in an explicit platform.  Try to grab from the1354        // execution context.1355        TargetSP target_sp =1356            execution_context ? execution_context->GetTargetSP() : TargetSP();1357        platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP();1358      }1359      OptionValidator *validator = def->validator;1360 1361      if (!platform_sp && require_validation) {1362        // Caller requires validation but we cannot validate as we don't have1363        // the mandatory platform against which to validate.1364        return llvm::createStringError(1365            "cannot validate options: no platform available");1366      }1367 1368      bool validation_failed = false;1369      if (platform_sp) {1370        // Ensure we have an execution context, empty or not.1371        ExecutionContext dummy_context;1372        ExecutionContext *exe_ctx_p =1373            execution_context ? execution_context : &dummy_context;1374        if (validator && !validator->IsValid(*platform_sp, *exe_ctx_p)) {1375          validation_failed = true;1376          error = Status::FromErrorStringWithFormat(1377              "Option \"%s\" invalid.  %s", def->long_option,1378              def->validator->LongConditionString());1379        }1380      }1381 1382      // As long as validation didn't fail, we set the option value.1383      if (!validation_failed)1384        error =1385            SetOptionValue(long_options_index,1386                           (def->option_has_arg == OptionParser::eNoArgument)1387                               ? nullptr1388                               : OptionParser::GetOptionArgument(),1389                           execution_context);1390      // If the Option setting returned an error, we should stop parsing1391      // and return the error.1392      if (error.Fail())1393        break;1394    } else {1395      error = Status::FromErrorStringWithFormat(1396          "invalid option with value '%i'", val);1397    }1398  }1399 1400  if (error.Fail())1401    return error.ToError();1402 1403  argv.pop_back();1404  argv.erase(argv.begin(), argv.begin() + OptionParser::GetOptionIndex());1405  return ReconstituteArgsAfterParsing(argv, args);1406}1407 1408llvm::Error lldb_private::CreateOptionParsingError(1409    llvm::StringRef option_arg, const char short_option,1410    llvm::StringRef long_option, llvm::StringRef additional_context) {1411  std::string buffer;1412  llvm::raw_string_ostream stream(buffer);1413  stream << "Invalid value ('" << option_arg << "') for -" << short_option;1414  if (!long_option.empty())1415    stream << " (" << long_option << ")";1416  if (!additional_context.empty())1417    stream << ": " << additional_context;1418  return llvm::createStringError(llvm::inconvertibleErrorCode(), buffer);1419}1420