summaryrefslogtreecommitdiffstats
path: root/lldb/source/Core/Options.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lldb/source/Core/Options.cpp')
-rw-r--r--lldb/source/Core/Options.cpp700
1 files changed, 700 insertions, 0 deletions
diff --git a/lldb/source/Core/Options.cpp b/lldb/source/Core/Options.cpp
new file mode 100644
index 00000000000..35a9fa40e1b
--- /dev/null
+++ b/lldb/source/Core/Options.cpp
@@ -0,0 +1,700 @@
+//===-- Options.cpp ---------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Core/Options.h"
+
+// C Includes
+// C++ Includes
+#include <bitset>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Interpreter/CommandObject.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/CommandCompletions.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//-------------------------------------------------------------------------
+// Options
+//-------------------------------------------------------------------------
+Options::Options () :
+ m_getopt_table ()
+{
+}
+
+Options::~Options ()
+{
+}
+
+
+void
+Options::ResetOptionValues ()
+{
+ m_seen_options.clear();
+}
+
+void
+Options::OptionSeen (int option_idx)
+{
+ m_seen_options.insert ((char) option_idx);
+}
+
+// Returns true is set_a is a subset of set_b; Otherwise returns false.
+
+bool
+Options::IsASubset (const OptionSet& set_a, const OptionSet& set_b)
+{
+ bool is_a_subset = true;
+ OptionSet::const_iterator pos_a;
+ OptionSet::const_iterator pos_b;
+
+ // set_a is a subset of set_b if every member of set_a is also a member of set_b
+
+ for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a)
+ {
+ pos_b = set_b.find(*pos_a);
+ if (pos_b == set_b.end())
+ is_a_subset = false;
+ }
+
+ return is_a_subset;
+}
+
+// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && !ElementOf (x, set_b) }
+
+size_t
+Options::OptionsSetDiff (const OptionSet& set_a, const OptionSet& set_b, OptionSet& diffs)
+{
+ size_t num_diffs = 0;
+ OptionSet::const_iterator pos_a;
+ OptionSet::const_iterator pos_b;
+
+ for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a)
+ {
+ pos_b = set_b.find(*pos_a);
+ if (pos_b == set_b.end())
+ {
+ ++num_diffs;
+ diffs.insert(*pos_a);
+ }
+ }
+
+ return num_diffs;
+}
+
+// Returns the union of set_a and set_b. Does not put duplicate members into the union.
+
+void
+Options::OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set)
+{
+ OptionSet::const_iterator pos;
+ OptionSet::iterator pos_union;
+
+ // Put all the elements of set_a into the union.
+
+ for (pos = set_a.begin(); pos != set_a.end(); ++pos)
+ union_set.insert(*pos);
+
+ // Put all the elements of set_b that are not already there into the union.
+ for (pos = set_b.begin(); pos != set_b.end(); ++pos)
+ {
+ pos_union = union_set.find(*pos);
+ if (pos_union == union_set.end())
+ union_set.insert(*pos);
+ }
+}
+
+bool
+Options::VerifyOptions (CommandReturnObject &result)
+{
+ bool options_are_valid = false;
+
+ int num_levels = m_required_options.size();
+ if (num_levels)
+ {
+ for (int i = 0; i < num_levels && !options_are_valid; ++i)
+ {
+ // This is the correct set of options if: 1). m_seen_options contains all of m_required_options[i]
+ // (i.e. all the required options at this level are a subset of m_seen_options); AND
+ // 2). { m_seen_options - m_required_options[i] is a subset of m_options_options[i] (i.e. all the rest of
+ // m_seen_options are in the set of optional options at this level.
+
+ // Check to see if all of m_required_options[i] are a subset of m_seen_options
+ if (IsASubset (m_required_options[i], m_seen_options))
+ {
+ // Construct the set difference: remaining_options = {m_seen_options} - {m_required_options[i]}
+ OptionSet remaining_options;
+ OptionsSetDiff (m_seen_options, m_required_options[i], remaining_options);
+ // Check to see if remaining_options is a subset of m_optional_options[i]
+ if (IsASubset (remaining_options, m_optional_options[i]))
+ options_are_valid = true;
+ }
+ }
+ }
+ else
+ {
+ options_are_valid = true;
+ }
+
+ if (options_are_valid)
+ {
+ result.SetStatus (eReturnStatusSuccessFinishNoResult);
+ }
+ else
+ {
+ result.AppendError ("invalid combination of options for the given command");
+ result.SetStatus (eReturnStatusFailed);
+ }
+
+ return options_are_valid;
+}
+
+void
+Options::BuildValidOptionSets ()
+{
+ // Check to see if we already did this.
+ if (m_required_options.size() != 0)
+ return;
+
+ // Check to see if there are any options.
+ int num_options = NumCommandOptions ();
+ if (num_options == 0)
+ return;
+
+ const lldb::OptionDefinition *full_options_table = GetDefinitions();
+ uint32_t usage_level = 0;
+ m_required_options.resize(1);
+ m_optional_options.resize(1);
+
+ for (int i = 0; i < num_options; ++i)
+ {
+ // NOTE: Assumption: The full options table is ordered with usage level growing monotonically.
+ assert (full_options_table[i].usage_level >= usage_level);
+
+ if (full_options_table[i].usage_level > usage_level)
+ {
+ // start a new level
+ usage_level = full_options_table[i].usage_level;
+ m_required_options.resize(m_required_options.size()+1);
+ m_optional_options.resize(m_optional_options.size()+1);
+ }
+ else
+ {
+ assert (m_required_options.empty() == false);
+ assert (m_optional_options.empty() == false);
+ }
+
+ if (full_options_table[i].required)
+ m_required_options.back().insert(full_options_table[i].short_option);
+ else
+ m_optional_options.back().insert(full_options_table[i].short_option);
+ }
+}
+
+uint32_t
+Options::NumCommandOptions ()
+{
+ const lldb::OptionDefinition *full_options_table = GetDefinitions ();
+ int i = 0;
+
+ if (full_options_table != NULL)
+ {
+ while (full_options_table[i].long_option != NULL)
+ ++i;
+ }
+
+ return i;
+}
+
+struct option *
+Options::GetLongOptions ()
+{
+ // Check to see if this has already been done.
+ if (m_getopt_table.empty())
+ {
+ // Check to see if there are any options.
+ const uint32_t num_options = NumCommandOptions();
+ if (num_options == 0)
+ return NULL;
+
+ uint32_t i;
+ uint32_t j;
+ const lldb::OptionDefinition *full_options_table = GetDefinitions();
+
+ std::bitset<256> option_seen;
+
+ m_getopt_table.resize(num_options + 1);
+ for (i = 0, j = 0; i < num_options; ++i)
+ {
+ char short_opt = full_options_table[i].short_option;
+
+ if (option_seen.test(short_opt) == false)
+ {
+ m_getopt_table[j].name = full_options_table[i].long_option;
+ m_getopt_table[j].has_arg = full_options_table[i].option_has_arg;
+ m_getopt_table[j].flag = NULL;
+ m_getopt_table[j].val = full_options_table[i].short_option;
+ option_seen.set(short_opt);
+ ++j;
+ }
+ }
+
+ //getopt_long requires a NULL final entry in the table:
+
+ m_getopt_table[j].name = NULL;
+ m_getopt_table[j].has_arg = 0;
+ m_getopt_table[j].flag = NULL;
+ m_getopt_table[j].val = 0;
+ }
+
+ return m_getopt_table.data();
+}
+
+
+// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is
+// a string containing 80 spaces; and TEXT, which is the text that is to be output. It outputs the text, on
+// multiple lines if necessary, to RESULT, with INDENT spaces at the front of each line. It breaks lines on spaces,
+// tabs or newlines, shortening the line if necessary to not break in the middle of a word. It assumes that each
+// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
+
+
+void
+Options::OutputFormattedUsageText
+(
+ Stream &strm,
+ const char *text,
+ uint32_t output_max_columns
+)
+{
+ int len = strlen (text);
+
+ // Will it all fit on one line?
+
+ if ((len + strm.GetIndentLevel()) < output_max_columns)
+ {
+ // Output it as a single line.
+ strm.Indent (text);
+ strm.EOL();
+ }
+ else
+ {
+ // We need to break it up into multiple lines.
+
+ int text_width = output_max_columns - strm.GetIndentLevel() - 1;
+ int start = 0;
+ int end = start;
+ int final_end = strlen (text);
+ int sub_len;
+
+ while (end < final_end)
+ {
+ // Don't start the 'text' on a space, since we're already outputting the indentation.
+ while ((start < final_end) && (text[start] == ' '))
+ start++;
+
+ end = start + text_width;
+ if (end > final_end)
+ end = final_end;
+ else
+ {
+ // If we're not at the end of the text, make sure we break the line on white space.
+ while (end > start
+ && text[end] != ' ' && text[end] != '\t' && text[end] != '\n')
+ end--;
+ }
+
+ sub_len = end - start;
+ if (start != 0)
+ strm.EOL();
+ strm.Indent();
+ assert (start < final_end);
+ assert (start + sub_len <= final_end);
+ strm.Write(text + start, sub_len);
+ start = end + 1;
+ }
+ strm.EOL();
+ }
+}
+
+void
+Options::GenerateOptionUsage
+(
+ Stream &strm,
+ CommandObject *cmd,
+ const char *program_name)
+{
+ uint32_t screen_width = 80;
+ const lldb::OptionDefinition *full_options_table = GetDefinitions();
+ const uint32_t save_indent_level = strm.GetIndentLevel();
+ const char *name;
+
+ if (cmd)
+ name = cmd->GetCommandName();
+ else
+ name = program_name;
+
+ strm.PutCString ("\nCommand Options Usage:\n");
+
+ strm.IndentMore(2);
+
+ // First, show each usage level set of options, e.g. <cmd> [options-for-level-0]
+ // <cmd> [options-for-level-1]
+ // etc.
+
+ uint32_t usage_level = 0;
+ const uint32_t num_options = NumCommandOptions();
+ uint32_t i;
+ for (i = 0; i < num_options; ++i)
+ {
+ if (i==0 || full_options_table[i].usage_level > usage_level)
+ {
+ // start a new level
+ usage_level = full_options_table[i].usage_level;
+ if (usage_level > 0)
+ {
+ strm.Printf ("\n");
+ }
+ strm.Indent (name);
+ }
+
+ // Add current option to the end of out_stream.
+
+ if (full_options_table[i].required)
+ {
+ if (full_options_table[i].option_has_arg == required_argument)
+ {
+ strm.Printf (" -%c %s",
+ full_options_table[i].short_option,
+ full_options_table[i].argument_name);
+ }
+ else if (full_options_table[i].option_has_arg == optional_argument)
+ {
+ strm.Printf (" -%c [%s]",
+ full_options_table[i].short_option,
+ full_options_table[i].argument_name);
+ }
+ else
+ strm.Printf (" -%c", full_options_table[i].short_option);
+ }
+ else
+ {
+ if (full_options_table[i].option_has_arg == required_argument)
+ strm.Printf (" [-%c %s]", full_options_table[i].short_option,
+ full_options_table[i].argument_name);
+ else if (full_options_table[i].option_has_arg == optional_argument)
+ strm.Printf (" [-%c [%s]]", full_options_table[i].short_option,
+ full_options_table[i].argument_name);
+ else
+ strm.Printf (" [-%c]", full_options_table[i].short_option);
+ }
+ }
+
+ strm.Printf ("\n\n");
+
+ // Now print out all the detailed information about the various options: long form, short form and help text:
+ // -- long_name <argument>
+ // - short <argument>
+ // help text
+
+ // This variable is used to keep track of which options' info we've printed out, because some options can be in
+ // more than one usage level, but we only want to print the long form of its information once.
+
+ OptionSet options_seen;
+ OptionSet::iterator pos;
+ strm.IndentMore (5);
+
+ int first_option_printed = 1;
+ for (i = 0; i < num_options; ++i)
+ {
+ // Only print out this option if we haven't already seen it.
+ pos = options_seen.find (full_options_table[i].short_option);
+ if (pos == options_seen.end())
+ {
+ // Put a newline separation between arguments
+ if (first_option_printed)
+ first_option_printed = 0;
+ else
+ strm.EOL();
+
+ options_seen.insert (full_options_table[i].short_option);
+ strm.Indent ();
+ strm.Printf ("-%c ", full_options_table[i].short_option);
+ if (full_options_table[i].argument_name != NULL)
+ strm.PutCString(full_options_table[i].argument_name);
+ strm.EOL();
+ strm.Indent ();
+ strm.Printf ("--%s ", full_options_table[i].long_option);
+ if (full_options_table[i].argument_name != NULL)
+ strm.PutCString(full_options_table[i].argument_name);
+ strm.EOL();
+
+ strm.IndentMore (5);
+
+ if (full_options_table[i].usage_text)
+ OutputFormattedUsageText (strm,
+ full_options_table[i].usage_text,
+ screen_width);
+ if (full_options_table[i].enum_values != NULL)
+ {
+ strm.Indent ();
+ strm.Printf("Values: ");
+ for (int j = 0; full_options_table[i].enum_values[j].string_value != NULL; j++)
+ {
+ if (j == 0)
+ strm.Printf("%s", full_options_table[i].enum_values[j].string_value);
+ else
+ strm.Printf(" | %s", full_options_table[i].enum_values[j].string_value);
+ }
+ strm.EOL();
+ }
+ strm.IndentLess (5);
+ }
+ }
+
+ // Restore the indent level
+ strm.SetIndentLevel (save_indent_level);
+}
+
+// This function is called when we have been given a potentially incomplete set of
+// options, such as when an alias has been defined (more options might be added at
+// at the time the alias is invoked). We need to verify that the options in the set
+// m_seen_options are all part of a set that may be used together, but m_seen_options
+// may be missing some of the "required" options.
+
+bool
+Options::VerifyPartialOptions (CommandReturnObject &result)
+{
+ bool options_are_valid = false;
+
+ int num_levels = m_required_options.size();
+ if (num_levels)
+ {
+ for (int i = 0; i < num_levels && !options_are_valid; ++i)
+ {
+ // In this case we are treating all options as optional rather than required.
+ // Therefore a set of options is correct if m_seen_options is a subset of the
+ // union of m_required_options and m_optional_options.
+ OptionSet union_set;
+ OptionsSetUnion (m_required_options[i], m_optional_options[i], union_set);
+ if (IsASubset (m_seen_options, union_set))
+ options_are_valid = true;
+ }
+ }
+
+ return options_are_valid;
+}
+
+bool
+Options::HandleOptionCompletion
+(
+ Args &input,
+ OptionElementVector &opt_element_vector,
+ int cursor_index,
+ int char_pos,
+ int match_start_point,
+ int max_return_elements,
+ lldb_private::CommandInterpreter *interpreter,
+ lldb_private::StringList &matches
+)
+{
+ // For now we just scan the completions to see if the cursor position is in
+ // an option or its argument. Otherwise we'll call HandleArgumentCompletion.
+ // In the future we can use completion to validate options as well if we want.
+
+ const OptionDefinition *opt_defs = GetDefinitions();
+
+ std::string cur_opt_std_str (input.GetArgumentAtIndex(cursor_index));
+ cur_opt_std_str.erase(char_pos);
+ const char *cur_opt_str = cur_opt_std_str.c_str();
+
+ for (int i = 0; i < opt_element_vector.size(); i++)
+ {
+ int opt_pos = opt_element_vector[i].opt_pos;
+ int opt_arg_pos = opt_element_vector[i].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[i].opt_defs_index;
+ if (opt_pos == cursor_index)
+ {
+ // We're completing the option itself.
+ if (opt_defs_index != -1)
+ {
+ // We recognized it, if it an incomplete long option, complete it anyway (getopt_long is
+ // happy with shortest unique string, but it's still a nice thing to do.) Otherwise return
+ // The string so the upper level code will know this is a full match and add the " ".
+ if (cur_opt_str && strlen (cur_opt_str) > 2
+ && cur_opt_str[0] == '-' && cur_opt_str[1] == '-'
+ && strcmp (opt_defs[opt_defs_index].long_option, cur_opt_str) != 0)
+ {
+ std::string full_name ("--");
+ full_name.append (opt_defs[opt_defs_index].long_option);
+ matches.AppendString(full_name.c_str());
+ return true;
+ }
+ else
+ {
+ matches.AppendString(input.GetArgumentAtIndex(cursor_index));
+ return true;
+ }
+ }
+ else
+ {
+ // FIXME - not handling wrong options yet:
+ // Check to see if they are writing a long option & complete it.
+ // I think we will only get in here if the long option table has two elements
+ // that are not unique up to this point. getopt_long does shortest unique match
+ // for long options already.
+
+ if (cur_opt_str && strlen (cur_opt_str) > 2
+ && cur_opt_str[0] == '-' && cur_opt_str[1] == '-')
+ {
+ for (int i = 0 ; opt_defs[i].short_option != 0 ; i++)
+ {
+ if (strstr(opt_defs[i].long_option, cur_opt_str + 2) == opt_defs[i].long_option)
+ {
+ std::string full_name ("--");
+ full_name.append (opt_defs[i].long_option);
+ // The options definitions table has duplicates because of the
+ // way the grouping information is stored, so only add once.
+ bool duplicate = false;
+ for (int j = 0; j < matches.GetSize(); j++)
+ {
+ if (matches.GetStringAtIndex(j) == full_name)
+ {
+ duplicate = true;
+ break;
+ }
+ }
+ if (!duplicate)
+ matches.AppendString(full_name.c_str());
+ }
+ }
+ }
+ return true;
+ }
+
+
+ }
+ else if (opt_arg_pos == cursor_index)
+ {
+ // Okay the cursor is on the completion of an argument.
+ // See if it has a completion, otherwise return no matches.
+
+ if (opt_defs_index != -1)
+ {
+ HandleOptionArgumentCompletion (input,
+ cursor_index,
+ strlen (input.GetArgumentAtIndex(cursor_index)),
+ opt_element_vector,
+ i,
+ match_start_point,
+ max_return_elements,
+ interpreter,
+ matches);
+ return true;
+ }
+ else
+ {
+ // No completion callback means no completions...
+ return true;
+ }
+
+ }
+ else
+ {
+ // Not the last element, keep going.
+ continue;
+ }
+ }
+ return false;
+}
+
+bool
+Options::HandleOptionArgumentCompletion
+(
+ Args &input,
+ int cursor_index,
+ int char_pos,
+ OptionElementVector &opt_element_vector,
+ int opt_element_index,
+ int match_start_point,
+ int max_return_elements,
+ lldb_private::CommandInterpreter *interpreter,
+ lldb_private::StringList &matches
+)
+{
+ const OptionDefinition *opt_defs = GetDefinitions();
+ std::auto_ptr<SearchFilter> filter_ap;
+
+ int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
+ int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
+
+ // See if this is an enumeration type option, and if so complete it here:
+
+ OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values;
+ if (enum_values != NULL)
+ {
+ bool return_value = false;
+ std::string match_string(input.GetArgumentAtIndex (opt_arg_pos), input.GetArgumentAtIndex (opt_arg_pos) + char_pos);
+ for (int i = 0; enum_values[i].string_value != NULL; i++)
+ {
+ if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value)
+ {
+ matches.AppendString (enum_values[i].string_value);
+ return_value = true;
+ }
+ }
+ return return_value;
+ }
+
+ // If this is a source file or symbol type completion, and there is a
+ // -shlib option somewhere in the supplied arguments, then make a search filter
+ // for that shared library.
+ // FIXME: Do we want to also have an "OptionType" so we don't have to match string names?
+
+ uint32_t completion_mask = opt_defs[opt_defs_index].completionType;
+ if (completion_mask & CommandCompletions::eSourceFileCompletion
+ || completion_mask & CommandCompletions::eSymbolCompletion)
+ {
+ for (int i = 0; i < opt_element_vector.size(); i++)
+ {
+ int cur_defs_index = opt_element_vector[i].opt_defs_index;
+ int cur_arg_pos = opt_element_vector[i].opt_arg_pos;
+ const char *cur_opt_name = opt_defs[cur_defs_index].long_option;
+
+ // If this is the "shlib" option and there was an argument provided,
+ // restrict it to that shared library.
+ if (strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1)
+ {
+ const char *module_name = input.GetArgumentAtIndex(cur_arg_pos);
+ if (module_name)
+ {
+ FileSpec module_spec(module_name);
+ lldb::TargetSP target_sp = interpreter->Context()->GetTarget()->GetSP();
+ // Search filters require a target...
+ if (target_sp != NULL)
+ filter_ap.reset (new SearchFilterByModule (target_sp, module_spec));
+ }
+ break;
+ }
+ }
+ }
+
+ return CommandCompletions::InvokeCommonCompletionCallbacks (completion_mask,
+ input.GetArgumentAtIndex (opt_arg_pos),
+ match_start_point,
+ max_return_elements,
+ interpreter,
+ filter_ap.get(),
+ matches);
+
+}
OpenPOWER on IntegriCloud