Path: blob/main/contrib/llvm-project/lldb/source/Interpreter/Options.cpp
39587 views
//===-- Options.cpp -------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Interpreter/Options.h"910#include <algorithm>11#include <bitset>12#include <map>13#include <set>1415#include "lldb/Host/OptionParser.h"16#include "lldb/Interpreter/CommandCompletions.h"17#include "lldb/Interpreter/CommandInterpreter.h"18#include "lldb/Interpreter/CommandObject.h"19#include "lldb/Interpreter/CommandReturnObject.h"20#include "lldb/Target/Target.h"21#include "lldb/Utility/StreamString.h"22#include "llvm/ADT/STLExtras.h"2324using namespace lldb;25using namespace lldb_private;2627// Options28Options::Options() { BuildValidOptionSets(); }2930Options::~Options() = default;3132void Options::NotifyOptionParsingStarting(ExecutionContext *execution_context) {33m_seen_options.clear();34// Let the subclass reset its option values35OptionParsingStarting(execution_context);36}3738Status39Options::NotifyOptionParsingFinished(ExecutionContext *execution_context) {40return OptionParsingFinished(execution_context);41}4243void Options::OptionSeen(int option_idx) { m_seen_options.insert(option_idx); }4445// Returns true is set_a is a subset of set_b; Otherwise returns false.4647bool Options::IsASubset(const OptionSet &set_a, const OptionSet &set_b) {48bool is_a_subset = true;49OptionSet::const_iterator pos_a;50OptionSet::const_iterator pos_b;5152// set_a is a subset of set_b if every member of set_a is also a member of53// set_b5455for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a) {56pos_b = set_b.find(*pos_a);57if (pos_b == set_b.end())58is_a_subset = false;59}6061return is_a_subset;62}6364// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) &&65// !ElementOf (x, set_b) }6667size_t Options::OptionsSetDiff(const OptionSet &set_a, const OptionSet &set_b,68OptionSet &diffs) {69size_t num_diffs = 0;70OptionSet::const_iterator pos_a;71OptionSet::const_iterator pos_b;7273for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a) {74pos_b = set_b.find(*pos_a);75if (pos_b == set_b.end()) {76++num_diffs;77diffs.insert(*pos_a);78}79}8081return num_diffs;82}8384// Returns the union of set_a and set_b. Does not put duplicate members into85// the union.8687void Options::OptionsSetUnion(const OptionSet &set_a, const OptionSet &set_b,88OptionSet &union_set) {89OptionSet::const_iterator pos;90OptionSet::iterator pos_union;9192// Put all the elements of set_a into the union.9394for (pos = set_a.begin(); pos != set_a.end(); ++pos)95union_set.insert(*pos);9697// Put all the elements of set_b that are not already there into the union.98for (pos = set_b.begin(); pos != set_b.end(); ++pos) {99pos_union = union_set.find(*pos);100if (pos_union == union_set.end())101union_set.insert(*pos);102}103}104105bool Options::VerifyOptions(CommandReturnObject &result) {106bool options_are_valid = false;107108int num_levels = GetRequiredOptions().size();109if (num_levels) {110for (int i = 0; i < num_levels && !options_are_valid; ++i) {111// This is the correct set of options if: 1). m_seen_options contains112// all of m_required_options[i] (i.e. all the required options at this113// level are a subset of m_seen_options); AND 2). { m_seen_options -114// m_required_options[i] is a subset of m_options_options[i] (i.e. all115// the rest of m_seen_options are in the set of optional options at this116// level.117118// Check to see if all of m_required_options[i] are a subset of119// m_seen_options120if (IsASubset(GetRequiredOptions()[i], m_seen_options)) {121// Construct the set difference: remaining_options = {m_seen_options} -122// {m_required_options[i]}123OptionSet remaining_options;124OptionsSetDiff(m_seen_options, GetRequiredOptions()[i],125remaining_options);126// Check to see if remaining_options is a subset of127// m_optional_options[i]128if (IsASubset(remaining_options, GetOptionalOptions()[i]))129options_are_valid = true;130}131}132} else {133options_are_valid = true;134}135136if (options_are_valid) {137result.SetStatus(eReturnStatusSuccessFinishNoResult);138} else {139result.AppendError("invalid combination of options for the given command");140}141142return options_are_valid;143}144145// This is called in the Options constructor, though we could call it lazily if146// that ends up being a performance problem.147148void Options::BuildValidOptionSets() {149// Check to see if we already did this.150if (m_required_options.size() != 0)151return;152153// Check to see if there are any options.154int num_options = NumCommandOptions();155if (num_options == 0)156return;157158auto opt_defs = GetDefinitions();159m_required_options.resize(1);160m_optional_options.resize(1);161162// First count the number of option sets we've got. Ignore163// LLDB_ALL_OPTION_SETS...164165uint32_t num_option_sets = 0;166167for (const auto &def : opt_defs) {168uint32_t this_usage_mask = def.usage_mask;169if (this_usage_mask == LLDB_OPT_SET_ALL) {170if (num_option_sets == 0)171num_option_sets = 1;172} else {173for (uint32_t j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) {174if (this_usage_mask & (1 << j)) {175if (num_option_sets <= j)176num_option_sets = j + 1;177}178}179}180}181182if (num_option_sets > 0) {183m_required_options.resize(num_option_sets);184m_optional_options.resize(num_option_sets);185186for (const auto &def : opt_defs) {187for (uint32_t j = 0; j < num_option_sets; j++) {188if (def.usage_mask & 1 << j) {189if (def.required)190m_required_options[j].insert(def.short_option);191else192m_optional_options[j].insert(def.short_option);193}194}195}196}197}198199uint32_t Options::NumCommandOptions() { return GetDefinitions().size(); }200201Option *Options::GetLongOptions() {202// Check to see if this has already been done.203if (m_getopt_table.empty()) {204auto defs = GetDefinitions();205if (defs.empty())206return nullptr;207208std::map<int, uint32_t> option_seen;209210m_getopt_table.resize(defs.size() + 1);211for (size_t i = 0; i < defs.size(); ++i) {212const int short_opt = defs[i].short_option;213214m_getopt_table[i].definition = &defs[i];215m_getopt_table[i].flag = nullptr;216m_getopt_table[i].val = short_opt;217218if (option_seen.find(short_opt) == option_seen.end()) {219option_seen[short_opt] = i;220} else if (short_opt) {221m_getopt_table[i].val = 0;222std::map<int, uint32_t>::const_iterator pos =223option_seen.find(short_opt);224StreamString strm;225if (defs[i].HasShortOption())226Debugger::ReportError(227llvm::formatv(228"option[{0}] --{1} has a short option -{2} that "229"conflicts with option[{3}] --{4}, short option won't "230"be used for --{5}",231i, defs[i].long_option, short_opt, pos->second,232m_getopt_table[pos->second].definition->long_option,233defs[i].long_option)234.str());235else236Debugger::ReportError(237llvm::formatv(238"option[{0}] --{1} has a short option {2:x} that "239"conflicts with option[{3}] --{4}, short option won't "240"be used for --{5}",241(int)i, defs[i].long_option, short_opt, pos->second,242m_getopt_table[pos->second].definition->long_option,243defs[i].long_option)244.str());245}246}247248// getopt_long_only requires a NULL final entry in the table:249250m_getopt_table.back().definition = nullptr;251m_getopt_table.back().flag = nullptr;252m_getopt_table.back().val = 0;253}254255if (m_getopt_table.empty())256return nullptr;257258return &m_getopt_table.front();259}260261// This function takes INDENT, which tells how many spaces to output at the262// front of each line; SPACES, which is a string containing 80 spaces; and263// TEXT, which is the text that is to be output. It outputs the text, on264// multiple lines if necessary, to RESULT, with INDENT spaces at the front of265// each line. It breaks lines on spaces, tabs or newlines, shortening the line266// if necessary to not break in the middle of a word. It assumes that each267// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.268269void Options::OutputFormattedUsageText(Stream &strm,270const OptionDefinition &option_def,271uint32_t output_max_columns) {272std::string actual_text;273if (option_def.validator) {274const char *condition = option_def.validator->ShortConditionString();275if (condition) {276actual_text = "[";277actual_text.append(condition);278actual_text.append("] ");279}280}281actual_text.append(option_def.usage_text);282283// Will it all fit on one line?284285if (static_cast<uint32_t>(actual_text.length() + strm.GetIndentLevel()) <286output_max_columns) {287// Output it as a single line.288strm.Indent(actual_text);289strm.EOL();290} else {291// We need to break it up into multiple lines.292293int text_width = output_max_columns - strm.GetIndentLevel() - 1;294int start = 0;295int end = start;296int final_end = actual_text.length();297int sub_len;298299while (end < final_end) {300// Don't start the 'text' on a space, since we're already outputting the301// indentation.302while ((start < final_end) && (actual_text[start] == ' '))303start++;304305end = start + text_width;306if (end > final_end)307end = final_end;308else {309// If we're not at the end of the text, make sure we break the line on310// white space.311while (end > start && actual_text[end] != ' ' &&312actual_text[end] != '\t' && actual_text[end] != '\n')313end--;314}315316sub_len = end - start;317if (start != 0)318strm.EOL();319strm.Indent();320assert(start < final_end);321assert(start + sub_len <= final_end);322strm.Write(actual_text.c_str() + start, sub_len);323start = end + 1;324}325strm.EOL();326}327}328329bool Options::SupportsLongOption(const char *long_option) {330if (!long_option || !long_option[0])331return false;332333auto opt_defs = GetDefinitions();334if (opt_defs.empty())335return false;336337const char *long_option_name = long_option;338if (long_option[0] == '-' && long_option[1] == '-')339long_option_name += 2;340341for (auto &def : opt_defs) {342if (!def.long_option)343continue;344345if (strcmp(def.long_option, long_option_name) == 0)346return true;347}348349return false;350}351352enum OptionDisplayType {353eDisplayBestOption,354eDisplayShortOption,355eDisplayLongOption356};357358static bool PrintOption(const OptionDefinition &opt_def,359OptionDisplayType display_type, const char *header,360const char *footer, bool show_optional, Stream &strm) {361if (display_type == eDisplayShortOption && !opt_def.HasShortOption())362return false;363364if (header && header[0])365strm.PutCString(header);366367if (show_optional && !opt_def.required)368strm.PutChar('[');369const bool show_short_option =370opt_def.HasShortOption() && display_type != eDisplayLongOption;371if (show_short_option)372strm.Printf("-%c", opt_def.short_option);373else374strm.Printf("--%s", opt_def.long_option);375switch (opt_def.option_has_arg) {376case OptionParser::eNoArgument:377break;378case OptionParser::eRequiredArgument:379strm.Printf(" <%s>", CommandObject::GetArgumentName(opt_def.argument_type));380break;381382case OptionParser::eOptionalArgument:383strm.Printf("%s[<%s>]", show_short_option ? "" : "=",384CommandObject::GetArgumentName(opt_def.argument_type));385break;386}387if (show_optional && !opt_def.required)388strm.PutChar(']');389if (footer && footer[0])390strm.PutCString(footer);391return true;392}393394void Options::GenerateOptionUsage(Stream &strm, CommandObject &cmd,395uint32_t screen_width) {396auto opt_defs = GetDefinitions();397const uint32_t save_indent_level = strm.GetIndentLevel();398llvm::StringRef name = cmd.GetCommandName();399StreamString arguments_str;400cmd.GetFormattedCommandArguments(arguments_str);401402const uint32_t num_options = NumCommandOptions();403if (num_options == 0)404return;405406const bool only_print_args = cmd.IsDashDashCommand();407if (!only_print_args)408strm.PutCString("\nCommand Options Usage:\n");409410strm.IndentMore(2);411412// First, show each usage level set of options, e.g. <cmd> [options-for-413// level-0]414// <cmd>415// [options-for-level-1]416// etc.417418if (!only_print_args) {419uint32_t num_option_sets = GetRequiredOptions().size();420for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) {421if (opt_set > 0)422strm.Printf("\n");423strm.Indent(name);424425// Different option sets may require different args.426StreamString args_str;427uint32_t opt_set_mask = 1 << opt_set;428cmd.GetFormattedCommandArguments(args_str, opt_set_mask);429430// First go through and print all options that take no arguments as a431// single string. If a command has "-a" "-b" and "-c", this will show up432// as [-abc]433434// We use a set here so that they will be sorted.435std::set<int> required_options;436std::set<int> optional_options;437438for (auto &def : opt_defs) {439if (def.usage_mask & opt_set_mask && def.HasShortOption() &&440def.option_has_arg == OptionParser::eNoArgument) {441if (def.required) {442required_options.insert(def.short_option);443} else {444optional_options.insert(def.short_option);445}446}447}448449if (!required_options.empty()) {450strm.PutCString(" -");451for (int short_option : required_options)452strm.PutChar(short_option);453}454455if (!optional_options.empty()) {456strm.PutCString(" [-");457for (int short_option : optional_options)458strm.PutChar(short_option);459strm.PutChar(']');460}461462// First go through and print the required options (list them up front).463for (auto &def : opt_defs) {464if (def.usage_mask & opt_set_mask && def.HasShortOption() &&465def.required && def.option_has_arg != OptionParser::eNoArgument)466PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);467}468469// Now go through again, and this time only print the optional options.470for (auto &def : opt_defs) {471if (def.usage_mask & opt_set_mask && !def.required &&472def.option_has_arg != OptionParser::eNoArgument)473PrintOption(def, eDisplayBestOption, " ", nullptr, true, strm);474}475476if (args_str.GetSize() > 0) {477if (cmd.WantsRawCommandString())478strm.Printf(" --");479strm << " " << args_str.GetString();480}481}482}483484if ((only_print_args || cmd.WantsRawCommandString()) &&485arguments_str.GetSize() > 0) {486if (!only_print_args)487strm.PutChar('\n');488strm.Indent(name);489strm << " " << arguments_str.GetString();490}491492if (!only_print_args) {493strm.Printf("\n\n");494495// Now print out all the detailed information about the various options:496// long form, short form and help text:497// -short <argument> ( --long_name <argument> )498// help text499500strm.IndentMore(5);501502// Put the command options in a sorted container, so we can output503// them alphabetically by short_option.504std::multimap<int, uint32_t> options_ordered;505for (auto def : llvm::enumerate(opt_defs))506options_ordered.insert(507std::make_pair(def.value().short_option, def.index()));508509// Go through each option, find the table entry and write out the detailed510// help information for that option.511512bool first_option_printed = false;513514for (auto pos : options_ordered) {515// Put a newline separation between arguments516if (first_option_printed)517strm.EOL();518else519first_option_printed = true;520521OptionDefinition opt_def = opt_defs[pos.second];522523strm.Indent();524if (opt_def.short_option && opt_def.HasShortOption()) {525PrintOption(opt_def, eDisplayShortOption, nullptr, nullptr, false,526strm);527PrintOption(opt_def, eDisplayLongOption, " ( ", " )", false, strm);528} else {529// Short option is not printable, just print long option530PrintOption(opt_def, eDisplayLongOption, nullptr, nullptr, false, strm);531}532strm.EOL();533534strm.IndentMore(5);535536if (opt_def.usage_text)537OutputFormattedUsageText(strm, opt_def, screen_width);538if (!opt_def.enum_values.empty()) {539strm.Indent();540strm.Printf("Values: ");541bool is_first = true;542for (const auto &enum_value : opt_def.enum_values) {543if (is_first) {544strm.Printf("%s", enum_value.string_value);545is_first = false;546}547else548strm.Printf(" | %s", enum_value.string_value);549}550strm.EOL();551}552strm.IndentLess(5);553}554}555556// Restore the indent level557strm.SetIndentLevel(save_indent_level);558}559560// This function is called when we have been given a potentially incomplete set561// of options, such as when an alias has been defined (more options might be562// added at at the time the alias is invoked). We need to verify that the563// options in the set m_seen_options are all part of a set that may be used564// together, but m_seen_options may be missing some of the "required" options.565566bool Options::VerifyPartialOptions(CommandReturnObject &result) {567bool options_are_valid = false;568569int num_levels = GetRequiredOptions().size();570if (num_levels) {571for (int i = 0; i < num_levels && !options_are_valid; ++i) {572// In this case we are treating all options as optional rather than573// required. Therefore a set of options is correct if m_seen_options is a574// subset of the union of m_required_options and m_optional_options.575OptionSet union_set;576OptionsSetUnion(GetRequiredOptions()[i], GetOptionalOptions()[i],577union_set);578if (IsASubset(m_seen_options, union_set))579options_are_valid = true;580}581}582583return options_are_valid;584}585586bool Options::HandleOptionCompletion(CompletionRequest &request,587OptionElementVector &opt_element_vector,588CommandInterpreter &interpreter) {589// For now we just scan the completions to see if the cursor position is in590// an option or its argument. Otherwise we'll call HandleArgumentCompletion.591// In the future we can use completion to validate options as well if we592// want.593594auto opt_defs = GetDefinitions();595596llvm::StringRef cur_opt_str = request.GetCursorArgumentPrefix();597598for (size_t i = 0; i < opt_element_vector.size(); i++) {599size_t opt_pos = static_cast<size_t>(opt_element_vector[i].opt_pos);600size_t opt_arg_pos = static_cast<size_t>(opt_element_vector[i].opt_arg_pos);601int opt_defs_index = opt_element_vector[i].opt_defs_index;602if (opt_pos == request.GetCursorIndex()) {603// We're completing the option itself.604605if (opt_defs_index == OptionArgElement::eBareDash) {606// We're completing a bare dash. That means all options are open.607// FIXME: We should scan the other options provided and only complete608// options609// within the option group they belong to.610std::string opt_str = "-a";611612for (auto &def : opt_defs) {613if (!def.short_option)614continue;615opt_str[1] = def.short_option;616request.AddCompletion(opt_str, def.usage_text);617}618619return true;620} else if (opt_defs_index == OptionArgElement::eBareDoubleDash) {621std::string full_name("--");622for (auto &def : opt_defs) {623if (!def.short_option)624continue;625626full_name.erase(full_name.begin() + 2, full_name.end());627full_name.append(def.long_option);628request.AddCompletion(full_name, def.usage_text);629}630return true;631} else if (opt_defs_index != OptionArgElement::eUnrecognizedArg) {632// We recognized it, if it an incomplete long option, complete it633// anyway (getopt_long_only is happy with shortest unique string, but634// it's still a nice thing to do.) Otherwise return The string so the635// upper level code will know this is a full match and add the " ".636const OptionDefinition &opt = opt_defs[opt_defs_index];637llvm::StringRef long_option = opt.long_option;638if (cur_opt_str.starts_with("--") && cur_opt_str != long_option) {639request.AddCompletion("--" + long_option.str(), opt.usage_text);640return true;641} else642request.AddCompletion(request.GetCursorArgumentPrefix());643return true;644} else {645// FIXME - not handling wrong options yet:646// Check to see if they are writing a long option & complete it.647// I think we will only get in here if the long option table has two648// elements649// that are not unique up to this point. getopt_long_only does650// shortest unique match for long options already.651if (cur_opt_str.consume_front("--")) {652for (auto &def : opt_defs) {653llvm::StringRef long_option(def.long_option);654if (long_option.starts_with(cur_opt_str))655request.AddCompletion("--" + long_option.str(), def.usage_text);656}657}658return true;659}660661} else if (opt_arg_pos == request.GetCursorIndex()) {662// Okay the cursor is on the completion of an argument. See if it has a663// completion, otherwise return no matches.664if (opt_defs_index != -1) {665HandleOptionArgumentCompletion(request, opt_element_vector, i,666interpreter);667return true;668} else {669// No completion callback means no completions...670return true;671}672673} else {674// Not the last element, keep going.675continue;676}677}678return false;679}680681void Options::HandleOptionArgumentCompletion(682CompletionRequest &request, OptionElementVector &opt_element_vector,683int opt_element_index, CommandInterpreter &interpreter) {684auto opt_defs = GetDefinitions();685std::unique_ptr<SearchFilter> filter_up;686687int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;688689// See if this is an enumeration type option, and if so complete it here:690691const auto &enum_values = opt_defs[opt_defs_index].enum_values;692if (!enum_values.empty())693for (const auto &enum_value : enum_values)694request.TryCompleteCurrentArg(enum_value.string_value);695696// If this is a source file or symbol type completion, and there is a -shlib697// option somewhere in the supplied arguments, then make a search filter for698// that shared library.699// FIXME: Do we want to also have an "OptionType" so we don't have to match700// string names?701702uint32_t completion_mask = opt_defs[opt_defs_index].completion_type;703704if (completion_mask == 0) {705lldb::CommandArgumentType option_arg_type =706opt_defs[opt_defs_index].argument_type;707if (option_arg_type != eArgTypeNone) {708const CommandObject::ArgumentTableEntry *arg_entry =709CommandObject::FindArgumentDataByType(710opt_defs[opt_defs_index].argument_type);711if (arg_entry)712completion_mask = arg_entry->completion_type;713}714}715716if (completion_mask & lldb::eSourceFileCompletion ||717completion_mask & lldb::eSymbolCompletion) {718for (size_t i = 0; i < opt_element_vector.size(); i++) {719int cur_defs_index = opt_element_vector[i].opt_defs_index;720721// trying to use <0 indices will definitely cause problems722if (cur_defs_index == OptionArgElement::eUnrecognizedArg ||723cur_defs_index == OptionArgElement::eBareDash ||724cur_defs_index == OptionArgElement::eBareDoubleDash)725continue;726727int cur_arg_pos = opt_element_vector[i].opt_arg_pos;728const char *cur_opt_name = opt_defs[cur_defs_index].long_option;729730// If this is the "shlib" option and there was an argument provided,731// restrict it to that shared library.732if (cur_opt_name && strcmp(cur_opt_name, "shlib") == 0 &&733cur_arg_pos != -1) {734const char *module_name =735request.GetParsedLine().GetArgumentAtIndex(cur_arg_pos);736if (module_name) {737FileSpec module_spec(module_name);738lldb::TargetSP target_sp =739interpreter.GetDebugger().GetSelectedTarget();740// Search filters require a target...741if (target_sp)742filter_up =743std::make_unique<SearchFilterByModule>(target_sp, module_spec);744}745break;746}747}748}749750lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(751interpreter, completion_mask, request, filter_up.get());752}753754void OptionGroupOptions::Append(OptionGroup *group) {755auto group_option_defs = group->GetDefinitions();756for (uint32_t i = 0; i < group_option_defs.size(); ++i) {757m_option_infos.push_back(OptionInfo(group, i));758m_option_defs.push_back(group_option_defs[i]);759}760}761762const OptionGroup *OptionGroupOptions::GetGroupWithOption(char short_opt) {763for (uint32_t i = 0; i < m_option_defs.size(); i++) {764OptionDefinition opt_def = m_option_defs[i];765if (opt_def.short_option == short_opt)766return m_option_infos[i].option_group;767}768return nullptr;769}770771void OptionGroupOptions::Append(OptionGroup *group, uint32_t src_mask,772uint32_t dst_mask) {773auto group_option_defs = group->GetDefinitions();774for (uint32_t i = 0; i < group_option_defs.size(); ++i) {775if (group_option_defs[i].usage_mask & src_mask) {776m_option_infos.push_back(OptionInfo(group, i));777m_option_defs.push_back(group_option_defs[i]);778m_option_defs.back().usage_mask = dst_mask;779}780}781}782783void OptionGroupOptions::Append(784OptionGroup *group, llvm::ArrayRef<llvm::StringRef> exclude_long_options) {785auto group_option_defs = group->GetDefinitions();786for (uint32_t i = 0; i < group_option_defs.size(); ++i) {787const auto &definition = group_option_defs[i];788if (llvm::is_contained(exclude_long_options, definition.long_option))789continue;790791m_option_infos.push_back(OptionInfo(group, i));792m_option_defs.push_back(definition);793}794}795796void OptionGroupOptions::Finalize() {797m_did_finalize = true;798}799800Status OptionGroupOptions::SetOptionValue(uint32_t option_idx,801llvm::StringRef option_value,802ExecutionContext *execution_context) {803// After calling OptionGroupOptions::Append(...), you must finalize the804// groups by calling OptionGroupOptions::Finlize()805assert(m_did_finalize);806Status error;807if (option_idx < m_option_infos.size()) {808error = m_option_infos[option_idx].option_group->SetOptionValue(809m_option_infos[option_idx].option_index, option_value,810execution_context);811812} else {813error.SetErrorString("invalid option index"); // Shouldn't happen...814}815return error;816}817818void OptionGroupOptions::OptionParsingStarting(819ExecutionContext *execution_context) {820std::set<OptionGroup *> group_set;821OptionInfos::iterator pos, end = m_option_infos.end();822for (pos = m_option_infos.begin(); pos != end; ++pos) {823OptionGroup *group = pos->option_group;824if (group_set.find(group) == group_set.end()) {825group->OptionParsingStarting(execution_context);826group_set.insert(group);827}828}829}830Status831OptionGroupOptions::OptionParsingFinished(ExecutionContext *execution_context) {832std::set<OptionGroup *> group_set;833Status error;834OptionInfos::iterator pos, end = m_option_infos.end();835for (pos = m_option_infos.begin(); pos != end; ++pos) {836OptionGroup *group = pos->option_group;837if (group_set.find(group) == group_set.end()) {838error = group->OptionParsingFinished(execution_context);839group_set.insert(group);840if (error.Fail())841return error;842}843}844return error;845}846847// OptionParser permutes the arguments while processing them, so we create a848// temporary array holding to avoid modification of the input arguments. The849// options themselves are never modified, but the API expects a char * anyway,850// hence the const_cast.851static std::vector<char *> GetArgvForParsing(const Args &args) {852std::vector<char *> result;853// OptionParser always skips the first argument as it is based on getopt().854result.push_back(const_cast<char *>("<FAKE-ARG0>"));855for (const Args::ArgEntry &entry : args)856result.push_back(const_cast<char *>(entry.c_str()));857result.push_back(nullptr);858return result;859}860861// Given a permuted argument, find it's position in the original Args vector.862static Args::const_iterator FindOriginalIter(const char *arg,863const Args &original) {864return llvm::find_if(865original, [arg](const Args::ArgEntry &D) { return D.c_str() == arg; });866}867868// Given a permuted argument, find it's index in the original Args vector.869static size_t FindOriginalIndex(const char *arg, const Args &original) {870return std::distance(original.begin(), FindOriginalIter(arg, original));871}872873// Construct a new Args object, consisting of the entries from the original874// arguments, but in the permuted order.875static Args ReconstituteArgsAfterParsing(llvm::ArrayRef<char *> parsed,876const Args &original) {877Args result;878for (const char *arg : parsed) {879auto pos = FindOriginalIter(arg, original);880assert(pos != original.end());881result.AppendArgument(pos->ref(), pos->GetQuoteChar());882}883return result;884}885886static size_t FindArgumentIndexForOption(const Args &args,887const Option &long_option) {888std::string short_opt = llvm::formatv("-{0}", char(long_option.val)).str();889std::string long_opt =890std::string(llvm::formatv("--{0}", long_option.definition->long_option));891for (const auto &entry : llvm::enumerate(args)) {892if (entry.value().ref().starts_with(short_opt) ||893entry.value().ref().starts_with(long_opt))894return entry.index();895}896897return size_t(-1);898}899900static std::string BuildShortOptions(const Option *long_options) {901std::string storage;902llvm::raw_string_ostream sstr(storage);903904// Leading : tells getopt to return a : for a missing option argument AND to905// suppress error messages.906sstr << ":";907908for (size_t i = 0; long_options[i].definition != nullptr; ++i) {909if (long_options[i].flag == nullptr) {910sstr << (char)long_options[i].val;911switch (long_options[i].definition->option_has_arg) {912default:913case OptionParser::eNoArgument:914break;915case OptionParser::eRequiredArgument:916sstr << ":";917break;918case OptionParser::eOptionalArgument:919sstr << "::";920break;921}922}923}924return std::move(sstr.str());925}926927llvm::Expected<Args> Options::ParseAlias(const Args &args,928OptionArgVector *option_arg_vector,929std::string &input_line) {930Option *long_options = GetLongOptions();931932if (long_options == nullptr) {933return llvm::createStringError("Invalid long options");934}935936std::string short_options = BuildShortOptions(long_options);937938Args args_copy = args;939std::vector<char *> argv = GetArgvForParsing(args);940941std::unique_lock<std::mutex> lock;942OptionParser::Prepare(lock);943int val;944while (true) {945int long_options_index = -1;946val = OptionParser::Parse(argv, short_options, long_options,947&long_options_index);948949if (val == ':') {950return llvm::createStringError(llvm::inconvertibleErrorCode(),951"last option requires an argument");952}953954if (val == -1)955break;956957if (val == '?') {958return llvm::createStringError("Unknown or ambiguous option");959}960961if (val == 0)962continue;963964OptionSeen(val);965966// Look up the long option index967if (long_options_index == -1) {968for (int j = 0; long_options[j].definition || long_options[j].flag ||969long_options[j].val;970++j) {971if (long_options[j].val == val) {972long_options_index = j;973break;974}975}976}977978// See if the option takes an argument, and see if one was supplied.979if (long_options_index == -1) {980return llvm::createStringError(981llvm::formatv("Invalid option with value '{0}'.", char(val)).str());982}983984StreamString option_str;985option_str.Printf("-%c", val);986const OptionDefinition *def = long_options[long_options_index].definition;987int has_arg =988(def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;989990const char *option_arg = nullptr;991switch (has_arg) {992case OptionParser::eRequiredArgument:993if (OptionParser::GetOptionArgument() == nullptr) {994return llvm::createStringError(995llvm::formatv("Option '{0}' is missing argument specifier.",996option_str.GetString())997.str());998}999[[fallthrough]];1000case OptionParser::eOptionalArgument:1001option_arg = OptionParser::GetOptionArgument();1002[[fallthrough]];1003case OptionParser::eNoArgument:1004break;1005default:1006return llvm::createStringError(1007llvm::formatv("error with options table; invalid value in has_arg "1008"field for option '{0}'.",1009char(val))1010.str());1011}1012// Find option in the argument list; also see if it was supposed to take an1013// argument and if one was supplied. Remove option (and argument, if1014// given) from the argument list. Also remove them from the1015// raw_input_string, if one was passed in.1016// Note: We also need to preserve any option argument values that were1017// surrounded by backticks, as we lose track of them in the1018// option_args_vector.1019size_t idx =1020FindArgumentIndexForOption(args_copy, long_options[long_options_index]);1021std::string option_to_insert;1022if (option_arg) {1023if (idx != size_t(-1) && has_arg) {1024bool arg_has_backtick = args_copy[idx + 1].GetQuoteChar() == '`';1025if (arg_has_backtick)1026option_to_insert = "`";1027option_to_insert += option_arg;1028if (arg_has_backtick)1029option_to_insert += "`";1030} else1031option_to_insert = option_arg;1032} else1033option_to_insert = CommandInterpreter::g_no_argument;10341035option_arg_vector->emplace_back(std::string(option_str.GetString()),1036has_arg, option_to_insert);10371038if (idx == size_t(-1))1039continue;10401041if (!input_line.empty()) {1042llvm::StringRef tmp_arg = args_copy[idx].ref();1043size_t pos = input_line.find(std::string(tmp_arg));1044if (pos != std::string::npos)1045input_line.erase(pos, tmp_arg.size());1046}1047args_copy.DeleteArgumentAtIndex(idx);1048if ((option_to_insert != CommandInterpreter::g_no_argument) &&1049(OptionParser::GetOptionArgument() != nullptr) &&1050(idx < args_copy.GetArgumentCount()) &&1051(args_copy[idx].ref() == OptionParser::GetOptionArgument())) {1052if (input_line.size() > 0) {1053size_t pos = input_line.find(option_to_insert);1054if (pos != std::string::npos)1055input_line.erase(pos, option_to_insert.size());1056}1057args_copy.DeleteArgumentAtIndex(idx);1058}1059}10601061return std::move(args_copy);1062}10631064OptionElementVector Options::ParseForCompletion(const Args &args,1065uint32_t cursor_index) {1066OptionElementVector option_element_vector;1067Option *long_options = GetLongOptions();1068option_element_vector.clear();10691070if (long_options == nullptr)1071return option_element_vector;10721073std::string short_options = BuildShortOptions(long_options);10741075std::unique_lock<std::mutex> lock;1076OptionParser::Prepare(lock);1077OptionParser::EnableError(false);10781079int val;1080auto opt_defs = GetDefinitions();10811082std::vector<char *> dummy_vec = GetArgvForParsing(args);10831084bool failed_once = false;1085uint32_t dash_dash_pos = -1;10861087while (true) {1088bool missing_argument = false;1089int long_options_index = -1;10901091val = OptionParser::Parse(dummy_vec, short_options, long_options,1092&long_options_index);10931094if (val == -1) {1095// When we're completing a "--" which is the last option on line,1096if (failed_once)1097break;10981099failed_once = true;11001101// If this is a bare "--" we mark it as such so we can complete it1102// successfully later. Handling the "--" is a little tricky, since that1103// may mean end of options or arguments, or the user might want to1104// complete options by long name. I make this work by checking whether1105// the cursor is in the "--" argument, and if so I assume we're1106// completing the long option, otherwise I let it pass to1107// OptionParser::Parse which will terminate the option parsing. Note, in1108// either case we continue parsing the line so we can figure out what1109// other options were passed. This will be useful when we come to1110// restricting completions based on what other options we've seen on the1111// line.11121113if (static_cast<size_t>(OptionParser::GetOptionIndex()) <1114dummy_vec.size() &&1115(strcmp(dummy_vec[OptionParser::GetOptionIndex() - 1], "--") == 0)) {1116dash_dash_pos = FindOriginalIndex(1117dummy_vec[OptionParser::GetOptionIndex() - 1], args);1118if (dash_dash_pos == cursor_index) {1119option_element_vector.push_back(1120OptionArgElement(OptionArgElement::eBareDoubleDash, dash_dash_pos,1121OptionArgElement::eBareDoubleDash));1122continue;1123} else1124break;1125} else1126break;1127} else if (val == '?') {1128option_element_vector.push_back(OptionArgElement(1129OptionArgElement::eUnrecognizedArg,1130FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1131args),1132OptionArgElement::eUnrecognizedArg));1133continue;1134} else if (val == 0) {1135continue;1136} else if (val == ':') {1137// This is a missing argument.1138val = OptionParser::GetOptionErrorCause();1139missing_argument = true;1140}11411142OptionSeen(val);11431144// Look up the long option index1145if (long_options_index == -1) {1146for (int j = 0; long_options[j].definition || long_options[j].flag ||1147long_options[j].val;1148++j) {1149if (long_options[j].val == val) {1150long_options_index = j;1151break;1152}1153}1154}11551156// See if the option takes an argument, and see if one was supplied.1157if (long_options_index >= 0) {1158int opt_defs_index = -1;1159for (size_t i = 0; i < opt_defs.size(); i++) {1160if (opt_defs[i].short_option != val)1161continue;1162opt_defs_index = i;1163break;1164}11651166const OptionDefinition *def = long_options[long_options_index].definition;1167int has_arg =1168(def == nullptr) ? OptionParser::eNoArgument : def->option_has_arg;1169switch (has_arg) {1170case OptionParser::eNoArgument:1171option_element_vector.push_back(OptionArgElement(1172opt_defs_index,1173FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1174args),11750));1176break;1177case OptionParser::eRequiredArgument:1178if (OptionParser::GetOptionArgument() != nullptr) {1179int arg_index;1180if (missing_argument)1181arg_index = -1;1182else1183arg_index = OptionParser::GetOptionIndex() - 2;11841185option_element_vector.push_back(OptionArgElement(1186opt_defs_index,1187FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],1188args),1189arg_index));1190} else {1191option_element_vector.push_back(OptionArgElement(1192opt_defs_index,1193FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1194args),1195-1));1196}1197break;1198case OptionParser::eOptionalArgument:1199option_element_vector.push_back(OptionArgElement(1200opt_defs_index,1201FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 2],1202args),1203FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1204args)));1205break;1206default:1207// The options table is messed up. Here we'll just continue1208option_element_vector.push_back(OptionArgElement(1209OptionArgElement::eUnrecognizedArg,1210FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1211args),1212OptionArgElement::eUnrecognizedArg));1213break;1214}1215} else {1216option_element_vector.push_back(OptionArgElement(1217OptionArgElement::eUnrecognizedArg,1218FindOriginalIndex(dummy_vec[OptionParser::GetOptionIndex() - 1],1219args),1220OptionArgElement::eUnrecognizedArg));1221}1222}12231224// Finally we have to handle the case where the cursor index points at a1225// single "-". We want to mark that in the option_element_vector, but only1226// if it is not after the "--". But it turns out that OptionParser::Parse1227// just ignores an isolated "-". So we have to look it up by hand here. We1228// only care if it is AT the cursor position. Note, a single quoted dash is1229// not the same as a single dash...12301231const Args::ArgEntry &cursor = args[cursor_index];1232if ((static_cast<int32_t>(dash_dash_pos) == -1 ||1233cursor_index < dash_dash_pos) &&1234!cursor.IsQuoted() && cursor.ref() == "-") {1235option_element_vector.push_back(1236OptionArgElement(OptionArgElement::eBareDash, cursor_index,1237OptionArgElement::eBareDash));1238}1239return option_element_vector;1240}12411242llvm::Expected<Args> Options::Parse(const Args &args,1243ExecutionContext *execution_context,1244lldb::PlatformSP platform_sp,1245bool require_validation) {1246Status error;1247Option *long_options = GetLongOptions();1248if (long_options == nullptr) {1249return llvm::createStringError("Invalid long options.");1250}12511252std::string short_options = BuildShortOptions(long_options);1253std::vector<char *> argv = GetArgvForParsing(args);1254std::unique_lock<std::mutex> lock;1255OptionParser::Prepare(lock);1256int val;1257while (true) {1258int long_options_index = -1;1259val = OptionParser::Parse(argv, short_options, long_options,1260&long_options_index);12611262if (val == ':') {1263error.SetErrorString("last option requires an argument");1264break;1265}12661267if (val == -1)1268break;12691270// Did we get an error?1271if (val == '?') {1272error.SetErrorString("unknown or ambiguous option");1273break;1274}1275// The option auto-set itself1276if (val == 0)1277continue;12781279OptionSeen(val);12801281// Lookup the long option index1282if (long_options_index == -1) {1283for (int i = 0; long_options[i].definition || long_options[i].flag ||1284long_options[i].val;1285++i) {1286if (long_options[i].val == val) {1287long_options_index = i;1288break;1289}1290}1291}1292// Call the callback with the option1293if (long_options_index >= 0 &&1294long_options[long_options_index].definition) {1295const OptionDefinition *def = long_options[long_options_index].definition;12961297if (!platform_sp) {1298// User did not pass in an explicit platform. Try to grab from the1299// execution context.1300TargetSP target_sp =1301execution_context ? execution_context->GetTargetSP() : TargetSP();1302platform_sp = target_sp ? target_sp->GetPlatform() : PlatformSP();1303}1304OptionValidator *validator = def->validator;13051306if (!platform_sp && require_validation) {1307// Caller requires validation but we cannot validate as we don't have1308// the mandatory platform against which to validate.1309return llvm::createStringError(1310"cannot validate options: no platform available");1311}13121313bool validation_failed = false;1314if (platform_sp) {1315// Ensure we have an execution context, empty or not.1316ExecutionContext dummy_context;1317ExecutionContext *exe_ctx_p =1318execution_context ? execution_context : &dummy_context;1319if (validator && !validator->IsValid(*platform_sp, *exe_ctx_p)) {1320validation_failed = true;1321error.SetErrorStringWithFormat("Option \"%s\" invalid. %s",1322def->long_option,1323def->validator->LongConditionString());1324}1325}13261327// As long as validation didn't fail, we set the option value.1328if (!validation_failed)1329error =1330SetOptionValue(long_options_index,1331(def->option_has_arg == OptionParser::eNoArgument)1332? nullptr1333: OptionParser::GetOptionArgument(),1334execution_context);1335// If the Option setting returned an error, we should stop parsing1336// and return the error.1337if (error.Fail())1338break;1339} else {1340error.SetErrorStringWithFormat("invalid option with value '%i'", val);1341}1342}13431344if (error.Fail())1345return error.ToError();13461347argv.pop_back();1348argv.erase(argv.begin(), argv.begin() + OptionParser::GetOptionIndex());1349return ReconstituteArgsAfterParsing(argv, args);1350}13511352llvm::Error lldb_private::CreateOptionParsingError(1353llvm::StringRef option_arg, const char short_option,1354llvm::StringRef long_option, llvm::StringRef additional_context) {1355std::string buffer;1356llvm::raw_string_ostream stream(buffer);1357stream << "Invalid value ('" << option_arg << "') for -" << short_option;1358if (!long_option.empty())1359stream << " (" << long_option << ")";1360if (!additional_context.empty())1361stream << ": " << additional_context;1362return llvm::createStringError(llvm::inconvertibleErrorCode(), buffer);1363}136413651366