diff options
| author | Jim Ingham <jingham@apple.com> | 2010-06-15 19:49:27 +0000 |
|---|---|---|
| committer | Jim Ingham <jingham@apple.com> | 2010-06-15 19:49:27 +0000 |
| commit | 40af72e1063d72a00d47fe23816412029efc55ee (patch) | |
| tree | cb81a5f78ad9da77ad62611f1c4cd8a04986de1c /lldb/source/Interpreter | |
| parent | e22295e8a660b99674556e2eaceb0a02b37fdf79 (diff) | |
| download | bcm5719-llvm-40af72e1063d72a00d47fe23816412029efc55ee.tar.gz bcm5719-llvm-40af72e1063d72a00d47fe23816412029efc55ee.zip | |
Move Args.{cpp,h} and Options.{cpp,h} to Interpreter where they really belong.
llvm-svn: 106034
Diffstat (limited to 'lldb/source/Interpreter')
| -rw-r--r-- | lldb/source/Interpreter/Args.cpp | 1103 | ||||
| -rw-r--r-- | lldb/source/Interpreter/CommandInterpreter.cpp | 2 | ||||
| -rw-r--r-- | lldb/source/Interpreter/CommandObject.cpp | 2 | ||||
| -rw-r--r-- | lldb/source/Interpreter/CommandObjectScript.cpp | 2 | ||||
| -rw-r--r-- | lldb/source/Interpreter/Options.cpp | 734 |
5 files changed, 1840 insertions, 3 deletions
diff --git a/lldb/source/Interpreter/Args.cpp b/lldb/source/Interpreter/Args.cpp new file mode 100644 index 00000000000..bf1ff42f230 --- /dev/null +++ b/lldb/source/Interpreter/Args.cpp @@ -0,0 +1,1103 @@ +//===-- Args.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// C Includes +#include <getopt.h> +#include <cstdlib> +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Stream.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Interpreter/Options.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +using namespace lldb; +using namespace lldb_private; + +static const char *k_space_characters = "\t\n\v\f\r "; +static const char *k_space_characters_with_slash = "\t\n\v\f\r \\"; + + +//---------------------------------------------------------------------- +// Args constructor +//---------------------------------------------------------------------- +Args::Args (const char *command) : + m_args(), + m_argv() +{ + SetCommandString (command); +} + + +Args::Args (const char *command, size_t len) : + m_args(), + m_argv() +{ + SetCommandString (command, len); +} + + + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +Args::~Args () +{ +} + +void +Args::Dump (Stream *s) +{ +// int argc = GetArgumentCount(); +// +// arg_sstr_collection::const_iterator pos, begin = m_args.begin(), end = m_args.end(); +// for (pos = m_args.begin(); pos != end; ++pos) +// { +// s->Indent(); +// s->Printf("args[%zu]=%s\n", std::distance(begin, pos), pos->c_str()); +// } +// s->EOL(); + const int argc = m_argv.size(); + for (int i=0; i<argc; ++i) + { + s->Indent(); + const char *arg_cstr = m_argv[i]; + if (arg_cstr) + s->Printf("argv[%i]=\"%s\"\n", i, arg_cstr); + else + s->Printf("argv[%i]=NULL\n", i); + } + s->EOL(); +} + +bool +Args::GetCommandString (std::string &command) +{ + command.clear(); + int argc = GetArgumentCount(); + for (int i=0; i<argc; ++i) + { + if (i > 0) + command += ' '; + command += m_argv[i]; + } + return argc > 0; +} + +void +Args::SetCommandString (const char *command, size_t len) +{ + // Use std::string to make sure we get a NULL terminated string we can use + // as "command" could point to a string within a large string.... + std::string null_terminated_command(command, len); + SetCommandString(null_terminated_command.c_str()); +} + +void +Args::SetCommandString (const char *command) +{ + m_args.clear(); + m_argv.clear(); + if (command && command[0]) + { + const char *arg_start; + const char *next_arg_start; + for (arg_start = command, next_arg_start = NULL; + arg_start && arg_start[0]; + arg_start = next_arg_start, next_arg_start = NULL) + { + // Skip any leading space characters + arg_start = ::strspn (arg_start, k_space_characters) + arg_start; + + // If there were only space characters to the end of the line, then + // we're done. + if (*arg_start == '\0') + break; + + std::string arg; + const char *arg_end = NULL; + + switch (*arg_start) + { + case '\'': + case '"': + case '`': + { + // Look for either a quote character, or the backslash + // character + const char quote_char = *arg_start; + char find_chars[3] = { quote_char, '\\' , '\0'}; + bool is_backtick = (quote_char == '`'); + if (quote_char == '"' || quote_char == '`') + m_args_quote_char.push_back(quote_char); + else + m_args_quote_char.push_back('\0'); + + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start + 1, find_chars) + arg_start + 1; + + if (*arg_end == '\0') + { + arg.append (arg_start); + break; + } + + // Watch out for quote characters prefixed with '\' + if (*arg_end == '\\') + { + if (arg_end[1] == quote_char) + { + // The character following the '\' is our quote + // character so strip the backslash character + arg.append (arg_start, arg_end); + } + else + { + // The character following the '\' is NOT our + // quote character, so include the backslash + // and continue + arg.append (arg_start, arg_end + 1); + } + arg_start = arg_end + 1; + continue; + } + else + { + arg.append (arg_start, arg_end + 1); + next_arg_start = arg_end + 1; + break; + } + } + + // Skip single and double quotes, but leave backtick quotes + if (!is_backtick) + { + char first_c = arg[0]; + arg.erase(0,1); + // Only erase the last character if it is the same as the first. + // Otherwise, we're parsing an incomplete command line, and we + // would be stripping off the last character of that string. + if (arg[arg.size() - 1] == first_c) + arg.erase(arg.size() - 1, 1); + } + } + break; + default: + { + m_args_quote_char.push_back('\0'); + // Look for the next non-escaped space character + while (*arg_start != '\0') + { + arg_end = ::strcspn (arg_start, k_space_characters_with_slash) + arg_start; + + if (arg_end == NULL) + { + arg.append(arg_start); + break; + } + + if (*arg_end == '\\') + { + // Append up to the '\' char + arg.append (arg_start, arg_end); + + if (arg_end[1] == '\0') + break; + + // Append the character following the '\' if it isn't + // the end of the string + arg.append (1, arg_end[1]); + arg_start = arg_end + 2; + continue; + } + else + { + arg.append (arg_start, arg_end); + next_arg_start = arg_end; + break; + } + } + } + break; + } + + m_args.push_back(arg); + } + } + UpdateArgvFromArgs(); +} + +void +Args::UpdateArgsAfterOptionParsing() +{ + // Now m_argv might be out of date with m_args, so we need to fix that + arg_cstr_collection::const_iterator argv_pos, argv_end = m_argv.end(); + arg_sstr_collection::iterator args_pos; + arg_quote_char_collection::iterator quotes_pos; + + for (argv_pos = m_argv.begin(), args_pos = m_args.begin(), quotes_pos = m_args_quote_char.begin(); + argv_pos != argv_end && args_pos != m_args.end(); + ++argv_pos) + { + const char *argv_cstr = *argv_pos; + if (argv_cstr == NULL) + break; + + while (args_pos != m_args.end()) + { + const char *args_cstr = args_pos->c_str(); + if (args_cstr == argv_cstr) + { + // We found the argument that matches the C string in the + // vector, so we can now look for the next one + ++args_pos; + ++quotes_pos; + break; + } + else + { + quotes_pos = m_args_quote_char.erase (quotes_pos); + args_pos = m_args.erase (args_pos); + } + } + } + + if (args_pos != m_args.end()) + m_args.erase (args_pos, m_args.end()); + + if (quotes_pos != m_args_quote_char.end()) + m_args_quote_char.erase (quotes_pos, m_args_quote_char.end()); +} + +void +Args::UpdateArgvFromArgs() +{ + m_argv.clear(); + arg_sstr_collection::const_iterator pos, end = m_args.end(); + for (pos = m_args.begin(); pos != end; ++pos) + m_argv.push_back(pos->c_str()); + m_argv.push_back(NULL); +} + +size_t +Args::GetArgumentCount() const +{ + if (m_argv.empty()) + return 0; + return m_argv.size() - 1; +} + +const char * +Args::GetArgumentAtIndex (size_t idx) const +{ + if (idx < m_argv.size()) + return m_argv[idx]; + return NULL; +} + +char +Args::GetArgumentQuoteCharAtIndex (size_t idx) const +{ + if (idx < m_args_quote_char.size()) + return m_args_quote_char[idx]; + return '\0'; +} + +char ** +Args::GetArgumentVector() +{ + if (!m_argv.empty()) + return (char **)&m_argv[0]; + return NULL; +} + +const char ** +Args::GetConstArgumentVector() const +{ + if (!m_argv.empty()) + return (const char **)&m_argv[0]; + return NULL; +} + +void +Args::Shift () +{ + // Don't pop the last NULL terminator from the argv array + if (m_argv.size() > 1) + { + m_argv.erase(m_argv.begin()); + m_args.pop_front(); + m_args_quote_char.erase(m_args_quote_char.begin()); + } +} + +const char * +Args::Unshift (const char *arg_cstr, char quote_char) +{ + m_args.push_front(arg_cstr); + m_argv.insert(m_argv.begin(), m_args.front().c_str()); + m_args_quote_char.insert(m_args_quote_char.begin(), quote_char); + return GetArgumentAtIndex (0); +} + +void +Args::AppendArguments (const Args &rhs) +{ + const size_t rhs_argc = rhs.GetArgumentCount(); + for (size_t i=0; i<rhs_argc; ++i) + AppendArgument(rhs.GetArgumentAtIndex(i)); +} + +const char * +Args::AppendArgument (const char *arg_cstr, char quote_char) +{ + return InsertArgumentAtIndex (GetArgumentCount(), arg_cstr, quote_char); +} + +const char * +Args::InsertArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + pos = m_args.insert(pos, arg_cstr); + + + m_args_quote_char.insert(m_args_quote_char.begin() + idx, quote_char); + + UpdateArgvFromArgs(); + return GetArgumentAtIndex(idx); +} + +const char * +Args::ReplaceArgumentAtIndex (size_t idx, const char *arg_cstr, char quote_char) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + pos->assign(arg_cstr); + assert(idx < m_argv.size() - 1); + m_argv[idx] = pos->c_str(); + m_args_quote_char[idx] = quote_char; + return GetArgumentAtIndex(idx); + } + return NULL; +} + +void +Args::DeleteArgumentAtIndex (size_t idx) +{ + // Since we are using a std::list to hold onto the copied C string and + // we don't have direct access to the elements, we have to iterate to + // find the value. + arg_sstr_collection::iterator pos, end = m_args.end(); + size_t i = idx; + for (pos = m_args.begin(); i > 0 && pos != end; ++pos) + --i; + + if (pos != end) + { + m_args.erase (pos); + assert(idx < m_argv.size() - 1); + m_argv.erase(m_argv.begin() + idx); + m_args_quote_char.erase(m_args_quote_char.begin() + idx); + } +} + +void +Args::SetArguments (int argc, const char **argv) +{ + // m_argv will be rebuilt in UpdateArgvFromArgs() below, so there is + // no need to clear it here. + m_args.clear(); + m_args_quote_char.clear(); + + // Make a copy of the arguments in our internal buffer + size_t i; + // First copy each string + for (i=0; i<argc; ++i) + { + m_args.push_back (argv[i]); + if ((argv[i][0] == '"') || (argv[i][0] == '`')) + m_args_quote_char.push_back (argv[i][0]); + else + m_args_quote_char.push_back ('\0'); + } + + UpdateArgvFromArgs(); +} + + +Error +Args::ParseOptions (Options &options) +{ + StreamString sstr; + int i; + Error error; + struct option *long_options = options.GetLongOptions(); + if (long_options == NULL) + { + error.SetErrorStringWithFormat("Invalid long options.\n"); + return error; + } + + for (i=0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char)long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: break; + case required_argument: sstr << ':'; break; + case optional_argument: sstr << "::"; break; + } + } + } +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long(GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, + &long_options_index); + if (val == -1) + break; + + // Did we get an error? + if (val == '?') + { + error.SetErrorStringWithFormat("Unknown or ambiguous option.\n"); + break; + } + // The option auto-set itself + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Lookup the long option index + if (long_options_index == -1) + { + for (int i=0; + long_options[i].name || long_options[i].has_arg || long_options[i].flag || long_options[i].val; + ++i) + { + if (long_options[i].val == val) + { + long_options_index = i; + break; + } + } + } + // Call the callback with the option + if (long_options_index >= 0) + { + error = options.SetOptionValue(long_options_index, + long_options[long_options_index].has_arg == no_argument ? NULL : optarg); + } + else + { + error.SetErrorStringWithFormat("Invalid option with value '%i'.\n", val); + } + if (error.Fail()) + break; + } + + // Update our ARGV now that get options has consumed all the options + m_argv.erase(m_argv.begin(), m_argv.begin() + optind); + UpdateArgsAfterOptionParsing (); + return error; +} + +void +Args::Clear () +{ + m_args.clear (); + m_argv.clear (); + m_args_quote_char.clear(); +} + +int32_t +Args::StringToSInt32 (const char *s, int32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + int32_t uval = ::strtol (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint32_t +Args::StringToUInt32 (const char *s, uint32_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint32_t uval = ::strtoul (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + + +int64_t +Args::StringToSInt64 (const char *s, int64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + int64_t uval = ::strtoll (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +uint64_t +Args::StringToUInt64 (const char *s, uint64_t fail_value, int base, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + uint64_t uval = ::strtoull (s, &end, base); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return uval; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +lldb::addr_t +Args::StringToAddress (const char *s, lldb::addr_t fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + char *end = NULL; + lldb::addr_t addr = ::strtoull (s, &end, 0); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + // Try base 16 with no prefix... + addr = ::strtoull (s, &end, 16); + if (*end == '\0') + { + if (success_ptr) *success_ptr = true; + return addr; // All characters were used, return the result + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +bool +Args::StringToBoolean (const char *s, bool fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if (::strcasecmp (s, "false") == 0 || + ::strcasecmp (s, "off") == 0 || + ::strcasecmp (s, "no") == 0 || + ::strcmp (s, "0") == 0) + { + if (success_ptr) + *success_ptr = true; + return false; + } + else + if (::strcasecmp (s, "true") == 0 || + ::strcasecmp (s, "on") == 0 || + ::strcasecmp (s, "yes") == 0 || + ::strcmp (s, "1") == 0) + { + if (success_ptr) *success_ptr = true; + return true; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +int32_t +Args::StringToOptionEnum (const char *s, lldb::OptionEnumValueElement *enum_values, int32_t fail_value, bool *success_ptr) +{ + if (enum_values && s && s[0]) + { + for (int i = 0; enum_values[i].string_value != NULL ; i++) + { + if (strstr(enum_values[i].string_value, s) == enum_values[i].string_value) + { + if (success_ptr) *success_ptr = true; + return enum_values[i].value; + } + } + } + if (success_ptr) *success_ptr = false; + + return fail_value; +} + +ScriptLanguage +Args::StringToScriptLanguage (const char *s, ScriptLanguage fail_value, bool *success_ptr) +{ + if (s && s[0]) + { + if ((::strcasecmp (s, "python") == 0) || + (::strcasecmp (s, "default") == 0 && eScriptLanguagePython == eScriptLanguageDefault)) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguagePython; + } + if (::strcasecmp (s, "none")) + { + if (success_ptr) *success_ptr = true; + return eScriptLanguageNone; + } + } + if (success_ptr) *success_ptr = false; + return fail_value; +} + +Error +Args::StringToFormat +( + const char *s, + lldb::Format &format +) +{ + format = eFormatInvalid; + Error error; + + if (s && s[0]) + { + switch (s[0]) + { + case 'y': format = eFormatBytes; break; + case 'Y': format = eFormatBytesWithASCII; break; + case 'b': format = eFormatBinary; break; + case 'B': format = eFormatBoolean; break; + case 'c': format = eFormatChar; break; + case 'C': format = eFormatCharPrintable; break; + case 'o': format = eFormatOctal; break; + case 'i': + case 'd': format = eFormatDecimal; break; + case 'u': format = eFormatUnsigned; break; + case 'x': format = eFormatHex; break; + case 'f': + case 'e': + case 'g': format = eFormatFloat; break; + case 'p': format = eFormatPointer; break; + case 's': format = eFormatCString; break; + default: + error.SetErrorStringWithFormat("Invalid format character '%c'. Valid values are:\n" + " b - binary\n" + " B - boolean\n" + " c - char\n" + " C - printable char\n" + " d - signed decimal\n" + " e - float\n" + " f - float\n" + " g - float\n" + " i - signed decimal\n" + " o - octal\n" + " s - c-string\n" + " u - unsigned decimal\n" + " x - hex\n" + " y - bytes\n" + " Y - bytes with ASCII\n", s[0]); + break; + } + + if (error.Fail()) + return error; + } + else + { + error.SetErrorStringWithFormat("%s option string.\n", s ? "empty" : "invalid"); + } + return error; +} + +void +Args::LongestCommonPrefix (std::string &common_prefix) +{ + arg_sstr_collection::iterator pos, end = m_args.end(); + pos = m_args.begin(); + if (pos == end) + common_prefix.clear(); + else + common_prefix = (*pos); + + for (++pos; pos != end; ++pos) + { + int new_size = (*pos).size(); + + // First trim common_prefix if it is longer than the current element: + if (common_prefix.size() > new_size) + common_prefix.erase (new_size); + + // Then trim it at the first disparity: + + for (int i = 0; i < common_prefix.size(); i++) + { + if ((*pos)[i] != common_prefix[i]) + { + common_prefix.erase(i); + break; + } + } + + // If we've emptied the common prefix, we're done. + if (common_prefix.empty()) + break; + } +} + +void +Args::ParseAliasOptions +( + Options &options, + CommandReturnObject &result, + OptionArgVector *option_arg_vector +) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + + if (long_options == NULL) + { + result.AppendError ("invalid long options"); + result.SetStatus (eReturnStatusFailed); + return; + } + + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + int val; + while (1) + { + int long_options_index = -1; + val = ::getopt_long (GetArgumentCount(), GetArgumentVector(), sstr.GetData(), long_options, + &long_options_index); + + if (val == -1) + break; + + if (val == '?') + { + result.AppendError ("unknown or ambiguous option"); + result.SetStatus (eReturnStatusFailed); + break; + } + + if (val == 0) + continue; + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + StreamString option_str; + option_str.Printf ("-%c", (char) val); + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), "<no-argument>")); + break; + case required_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + std::string (optarg))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + result.AppendErrorWithFormat ("Option '%s' is missing argument specifier.\n", + option_str.GetData()); + result.SetStatus (eReturnStatusFailed); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + std::string (optarg))); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + else + { + option_arg_vector->push_back (OptionArgPair (std::string (option_str.GetData()), + "<no-argument>")); + result.SetStatus (eReturnStatusSuccessFinishNoResult); + } + break; + default: + result.AppendErrorWithFormat + ("error with options table; invalid value in has_arg field for option '%c'.\n", + (char) val); + result.SetStatus (eReturnStatusFailed); + break; + } + } + else + { + result.AppendErrorWithFormat ("Invalid option with value '%c'.\n", (char) val); + result.SetStatus (eReturnStatusFailed); + } + if (!result.Succeeded()) + break; + } +} + +void +Args::ParseArgsForCompletion +( + Options &options, + OptionElementVector &option_element_vector +) +{ + StreamString sstr; + int i; + struct option *long_options = options.GetLongOptions(); + option_element_vector.clear(); + + if (long_options == NULL) + { + return; + } + + // Leading : tells getopt to return a : for a missing option argument AND + // to suppress error messages. + + sstr << ":"; + for (i = 0; long_options[i].name != NULL; ++i) + { + if (long_options[i].flag == NULL) + { + sstr << (char) long_options[i].val; + switch (long_options[i].has_arg) + { + default: + case no_argument: + break; + case required_argument: + sstr << ":"; + break; + case optional_argument: + sstr << "::"; + break; + } + } + } + +#ifdef __GLIBC__ + optind = 0; +#else + optreset = 1; + optind = 1; +#endif + opterr = 0; + + int val; + const OptionDefinition *opt_defs = options.GetDefinitions(); + + // Fooey... getopt_long permutes the GetArgumentVector for no apparent reason. + // So we have to build another Arg and pass that to getopt_long so it doesn't + // screw up the one we have. + + std::vector<const char *> dummy_vec(GetArgumentVector(), GetArgumentVector() + GetArgumentCount() + 1); + + while (1) + { + bool missing_argument = false; + int parse_start = optind; + int long_options_index = -1; + val = ::getopt_long (dummy_vec.size() - 1,(char *const *) dummy_vec.data(), sstr.GetData(), long_options, + &long_options_index); + + if (val == -1) + break; + + else if (val == '?') + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + continue; + } + else if (val == 0) + { + continue; + } + else if (val == ':') + { + // This is a missing argument. + val = optopt; + missing_argument = true; + } + + ((Options *) &options)->OptionSeen (val); + + // Look up the long option index + if (long_options_index == -1) + { + for (int j = 0; + long_options[j].name || long_options[j].has_arg || long_options[j].flag || long_options[j].val; + ++j) + { + if (long_options[j].val == val) + { + long_options_index = j; + break; + } + } + } + + // See if the option takes an argument, and see if one was supplied. + if (long_options_index >= 0) + { + int opt_defs_index = -1; + for (int i = 0; ; i++) + { + if (opt_defs[i].short_option == 0) + break; + else if (opt_defs[i].short_option == val) + { + opt_defs_index = i; + break; + } + } + + switch (long_options[long_options_index].has_arg) + { + case no_argument: + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); + break; + case required_argument: + if (optarg != NULL) + { + int arg_index; + if (missing_argument) + arg_index = -1; + else + arg_index = parse_start + 1; + + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, arg_index)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, -1)); + } + break; + case optional_argument: + if (optarg != NULL) + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, 0)); + } + else + { + option_element_vector.push_back (OptionArgElement (opt_defs_index, parse_start, parse_start + 1)); + } + break; + default: + // The options table is messed up. Here we'll just continue + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + break; + } + } + else + { + option_element_vector.push_back (OptionArgElement (-1, parse_start, -1)); + } + } +} diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index a10050c9b26..7a0a1167c88 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -49,7 +49,7 @@ #include "../Commands/CommandObjectUnalias.h" #include "../Commands/CommandObjectVariable.h" -#include "lldb/Core/Args.h" +#include "lldb/Interpreter/Args.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Stream.h" #include "lldb/Core/Timer.h" diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 080b5b057bf..eb8cdd2260d 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -17,7 +17,7 @@ #include <ctype.h> #include "lldb/Core/Address.h" -#include "lldb/Core/Options.h" +#include "lldb/Interpreter/Options.h" // These are for the Sourcename completers. // FIXME: Make a separate file for the completers. diff --git a/lldb/source/Interpreter/CommandObjectScript.cpp b/lldb/source/Interpreter/CommandObjectScript.cpp index 64864be8d09..2cbca117bfc 100644 --- a/lldb/source/Interpreter/CommandObjectScript.cpp +++ b/lldb/source/Interpreter/CommandObjectScript.cpp @@ -13,7 +13,7 @@ // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/Core/Args.h" +#include "lldb/Interpreter/Args.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Interpreter/ScriptInterpreter.h" diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp new file mode 100644 index 00000000000..1f42e2a417b --- /dev/null +++ b/lldb/source/Interpreter/Options.cpp @@ -0,0 +1,734 @@ +//===-- 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/Interpreter/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 () +{ + BuildValidOptionSets(); +} + +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; +} + +// This is called in the Options constructor, though we could call it lazily if that ends up being +// a performance problem. + +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(); + m_required_options.resize(1); + m_optional_options.resize(1); + + // First count the number of option sets we've got. Ignore LLDB_ALL_OPTION_SETS... + + uint32_t num_option_sets = 0; + + for (int i = 0; i < num_options; i++) + { + uint32_t this_usage_mask = full_options_table[i].usage_mask; + if (this_usage_mask == LLDB_OPT_SET_ALL) + { + if (num_option_sets == 0) + num_option_sets = 1; + } + else + { + for (int j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++) + { + if (this_usage_mask & 1 << j) + { + if (num_option_sets <= j) + num_option_sets = j + 1; + } + } + } + } + + if (num_option_sets > 0) + { + m_required_options.resize(num_option_sets); + m_optional_options.resize(num_option_sets); + + for (int i = 0; i < num_options; ++i) + { + for (int j = 0; j < num_option_sets; j++) + { + if (full_options_table[i].usage_mask & 1 << j) + { + if (full_options_table[i].required) + m_required_options[j].insert(full_options_table[i].short_option); + else + m_optional_options[j].insert(full_options_table[i].short_option); + } + } + } + } +} + +uint32_t +Options::NumCommandOptions () +{ + const lldb::OptionDefinition *full_options_table = GetDefinitions (); + if (full_options_table == NULL) + return 0; + + 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. + + const uint32_t num_options = NumCommandOptions(); + if (num_options == 0) + return; + + BuildValidOptionSets (); + int num_option_sets = m_required_options.size(); + + uint32_t i; + + for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set) + { + uint32_t opt_set_mask; + + opt_set_mask = 1 << opt_set; + if (opt_set > 0) + strm.Printf ("\n"); + strm.Indent (name); + + for (i = 0; i < num_options; ++i) + { + if (full_options_table[i].usage_mask & opt_set_mask) + { + // 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); + +} |

