aboutsummaryrefslogtreecommitdiff
path: root/lldb/source
diff options
context:
space:
mode:
authorJim Ingham <jingham@apple.com>2022-08-31 10:13:05 -0700
committerJim Ingham <jingham@apple.com>2022-09-13 11:02:47 -0700
commit6c089b2af5d8d98f66b27b67f70958f520820a76 (patch)
tree575e84076bd5fa53e04744d3c54b1be35a9e8e13 /lldb/source
parentbfc550a361f0c93fb81bfd8e37b07515696e9ee0 (diff)
downloadllvm-6c089b2af5d8d98f66b27b67f70958f520820a76.zip
llvm-6c089b2af5d8d98f66b27b67f70958f520820a76.tar.gz
llvm-6c089b2af5d8d98f66b27b67f70958f520820a76.tar.bz2
Be more careful to maintain quoting information when parsing commands.
This is particularly a problem for alias construction, where you might want to have a backtick surrounded option in the alias. Before this patch: command alias expression -Z \`argc\` -- argv for instance would be rendered as: expression -Z argc -- argv and would fail to work. Differential Revision: https://reviews.llvm.org/D133045
Diffstat (limited to 'lldb/source')
-rw-r--r--lldb/source/Interpreter/CommandAlias.cpp14
-rw-r--r--lldb/source/Interpreter/CommandInterpreter.cpp62
-rw-r--r--lldb/source/Interpreter/CommandObject.cpp2
-rw-r--r--lldb/source/Interpreter/Options.cpp38
-rw-r--r--lldb/source/Utility/Args.cpp5
5 files changed, 87 insertions, 34 deletions
diff --git a/lldb/source/Interpreter/CommandAlias.cpp b/lldb/source/Interpreter/CommandAlias.cpp
index d55b3fd..e36edcac 100644
--- a/lldb/source/Interpreter/CommandAlias.cpp
+++ b/lldb/source/Interpreter/CommandAlias.cpp
@@ -60,11 +60,12 @@ static bool ProcessAliasOptionsArgs(lldb::CommandObjectSP &cmd_obj_sp,
if (!options_string.empty()) {
if (cmd_obj_sp->WantsRawCommandString())
- option_arg_vector->emplace_back("<argument>", -1, options_string);
+ option_arg_vector->emplace_back(CommandInterpreter::g_argument,
+ -1, options_string);
else {
for (auto &entry : args.entries()) {
if (!entry.ref().empty())
- option_arg_vector->emplace_back(std::string("<argument>"), -1,
+ option_arg_vector->emplace_back(std::string(CommandInterpreter::g_argument), -1,
std::string(entry.ref()));
}
}
@@ -153,11 +154,12 @@ void CommandAlias::GetAliasExpansion(StreamString &help_string) const {
for (const auto &opt_entry : *options) {
std::tie(opt, std::ignore, value) = opt_entry;
- if (opt == "<argument>") {
+ if (opt == CommandInterpreter::g_argument) {
help_string.Printf(" %s", value.c_str());
} else {
help_string.Printf(" %s", opt.c_str());
- if ((value != "<no-argument>") && (value != "<need-argument")) {
+ if ((value != CommandInterpreter::g_no_argument)
+ && (value != CommandInterpreter::g_need_argument)) {
help_string.Printf(" %s", value.c_str());
}
}
@@ -178,7 +180,7 @@ bool CommandAlias::IsDashDashCommand() {
for (const auto &opt_entry : *GetOptionArguments()) {
std::tie(opt, std::ignore, value) = opt_entry;
- if (opt == "<argument>" && !value.empty() &&
+ if (opt == CommandInterpreter::g_argument && !value.empty() &&
llvm::StringRef(value).endswith("--")) {
m_is_dashdash_alias = eLazyBoolYes;
break;
@@ -206,6 +208,8 @@ std::pair<lldb::CommandObjectSP, OptionArgVectorSP> CommandAlias::Desugar() {
return {nullptr, nullptr};
if (underlying->IsAlias()) {
+ // FIXME: This doesn't work if the original alias fills a slot in the
+ // underlying alias, since this just appends the two lists.
auto desugared = ((CommandAlias *)underlying.get())->Desugar();
auto options = GetOptionArguments();
options->insert(options->begin(), desugared.second->begin(),
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index 0596d85..2218d54 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -105,6 +105,11 @@ static constexpr const char *InitFileWarning =
"and\n"
"accept the security risk.";
+constexpr const char *CommandInterpreter::g_no_argument = "<no-argument>";
+constexpr const char *CommandInterpreter::g_need_argument = "<need-argument>";
+constexpr const char *CommandInterpreter::g_argument = "<argument>";
+
+
#define LLDB_PROPERTIES_interpreter
#include "InterpreterProperties.inc"
@@ -1634,7 +1639,7 @@ CommandObject *CommandInterpreter::BuildAliasResult(
std::string value;
for (const auto &entry : *option_arg_vector) {
std::tie(option, value_type, value) = entry;
- if (option == "<argument>") {
+ if (option == g_argument) {
result_str.Printf(" %s", value.c_str());
continue;
}
@@ -1656,11 +1661,33 @@ CommandObject *CommandInterpreter::BuildAliasResult(
index);
return nullptr;
} else {
- size_t strpos = raw_input_string.find(cmd_args.GetArgumentAtIndex(index));
- if (strpos != std::string::npos)
+ const Args::ArgEntry &entry = cmd_args[index];
+ size_t strpos = raw_input_string.find(entry.c_str());
+ const char quote_char = entry.GetQuoteChar();
+ if (strpos != std::string::npos) {
+ const size_t start_fudge = quote_char == '\0' ? 0 : 1;
+ const size_t len_fudge = quote_char == '\0' ? 0 : 2;
+
+ // Make sure we aren't going outside the bounds of the cmd string:
+ if (strpos < start_fudge) {
+ result.AppendError("Unmatched quote at command beginning.");
+ return nullptr;
+ }
+ llvm::StringRef arg_text = entry.ref();
+ if (strpos - start_fudge + arg_text.size() + len_fudge
+ > raw_input_string.size()) {
+ result.AppendError("Unmatched quote at command end.");
+ return nullptr;
+ }
raw_input_string = raw_input_string.erase(
- strpos, strlen(cmd_args.GetArgumentAtIndex(index)));
- result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index));
+ strpos - start_fudge,
+ strlen(cmd_args.GetArgumentAtIndex(index)) + len_fudge);
+ }
+ if (quote_char == '\0')
+ result_str.Printf("%s", cmd_args.GetArgumentAtIndex(index));
+ else
+ result_str.Printf("%c%s%c", quote_char,
+ entry.c_str(), quote_char);
}
}
@@ -1911,13 +1938,6 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
return true;
}
- Status error(PreprocessCommand(command_string));
-
- if (error.Fail()) {
- result.AppendError(error.AsCString());
- return false;
- }
-
// Phase 1.
// Before we do ANY kind of argument processing, we need to figure out what
@@ -1935,6 +1955,20 @@ bool CommandInterpreter::HandleCommand(const char *command_line,
CommandObject *cmd_obj = ResolveCommandImpl(command_string, result);
+ // We have to preprocess the whole command string for Raw commands, since we
+ // don't know the structure of the command. For parsed commands, we only
+ // treat backticks as quote characters specially.
+ // FIXME: We probably want to have raw commands do their own preprocessing.
+ // For instance, I don't think people expect substitution in expr expressions.
+ if (cmd_obj && cmd_obj->WantsRawCommandString()) {
+ Status error(PreprocessCommand(command_string));
+
+ if (error.Fail()) {
+ result.AppendError(error.AsCString());
+ return false;
+ }
+ }
+
// Although the user may have abbreviated the command, the command_string now
// has the command expanded to the full name. For example, if the input was
// "br s -n main", command_string is now "breakpoint set -n main".
@@ -2163,7 +2197,7 @@ void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,
std::string value;
for (const auto &option_entry : *option_arg_vector) {
std::tie(option, value_type, value) = option_entry;
- if (option == "<argument>") {
+ if (option == g_argument) {
if (!wants_raw_input || (value != "--")) {
// Since we inserted this above, make sure we don't insert it twice
new_args.AppendArgument(value);
@@ -2174,7 +2208,7 @@ void CommandInterpreter::BuildAliasCommandArgs(CommandObject *alias_cmd_obj,
if (value_type != OptionParser::eOptionalArgument)
new_args.AppendArgument(option);
- if (value == "<no-argument>")
+ if (value == g_no_argument)
continue;
int index = GetOptionArgumentPosition(value.c_str());
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 5ab3392..4500378 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -727,7 +727,7 @@ bool CommandObjectParsed::Execute(const char *args_string,
}
if (!handled) {
for (auto entry : llvm::enumerate(cmd_args.entries())) {
- if (!entry.value().ref().empty() && entry.value().ref().front() == '`') {
+ if (!entry.value().ref().empty() && entry.value().GetQuoteChar() == '`') {
cmd_args.ReplaceArgumentAtIndex(
entry.index(),
m_interpreter.ProcessEmbeddedScriptCommands(entry.value().c_str()));
diff --git a/lldb/source/Interpreter/Options.cpp b/lldb/source/Interpreter/Options.cpp
index 09af51a..ae36250 100644
--- a/lldb/source/Interpreter/Options.cpp
+++ b/lldb/source/Interpreter/Options.cpp
@@ -1002,37 +1002,47 @@ llvm::Expected<Args> Options::ParseAlias(const Args &args,
.str(),
llvm::inconvertibleErrorCode());
}
- if (!option_arg)
- option_arg = "<no-argument>";
- option_arg_vector->emplace_back(std::string(option_str.GetString()),
- has_arg, std::string(option_arg));
-
// Find option in the argument list; also see if it was supposed to take an
// argument and if one was supplied. Remove option (and argument, if
// given) from the argument list. Also remove them from the
// raw_input_string, if one was passed in.
+ // Note: We also need to preserve any option argument values that were
+ // surrounded by backticks, as we lose track of them in the
+ // option_args_vector.
size_t idx =
FindArgumentIndexForOption(args_copy, long_options[long_options_index]);
+ std::string option_to_insert;
+ if (option_arg) {
+ if (idx != size_t(-1) && has_arg) {
+ bool arg_has_backtick = args_copy[idx + 1].GetQuoteChar() == '`';
+ if (arg_has_backtick)
+ option_to_insert = "`";
+ option_to_insert += option_arg;
+ if (arg_has_backtick)
+ option_to_insert += "`";
+ } else
+ option_to_insert = option_arg;
+ } else
+ option_to_insert = CommandInterpreter::g_no_argument;
+
+ option_arg_vector->emplace_back(std::string(option_str.GetString()),
+ has_arg, option_to_insert);
+
if (idx == size_t(-1))
continue;
if (!input_line.empty()) {
- auto tmp_arg = args_copy[idx].ref();
+ llvm::StringRef tmp_arg = args_copy[idx].ref();
size_t pos = input_line.find(std::string(tmp_arg));
if (pos != std::string::npos)
input_line.erase(pos, tmp_arg.size());
}
args_copy.DeleteArgumentAtIndex(idx);
- if ((long_options[long_options_index].definition->option_has_arg !=
- OptionParser::eNoArgument) &&
- (OptionParser::GetOptionArgument() != nullptr) &&
- (idx < args_copy.GetArgumentCount()) &&
- (args_copy[idx].ref() == OptionParser::GetOptionArgument())) {
+ if (option_to_insert != CommandInterpreter::g_no_argument) {
if (input_line.size() > 0) {
- auto tmp_arg = args_copy[idx].ref();
- size_t pos = input_line.find(std::string(tmp_arg));
+ size_t pos = input_line.find(option_to_insert);
if (pos != std::string::npos)
- input_line.erase(pos, tmp_arg.size());
+ input_line.erase(pos, option_to_insert.size());
}
args_copy.DeleteArgumentAtIndex(idx);
}
diff --git a/lldb/source/Utility/Args.cpp b/lldb/source/Utility/Args.cpp
index daccb91..42c84d7 100644
--- a/lldb/source/Utility/Args.cpp
+++ b/lldb/source/Utility/Args.cpp
@@ -215,7 +215,12 @@ bool Args::GetCommandString(std::string &command) const {
for (size_t i = 0; i < m_entries.size(); ++i) {
if (i > 0)
command += ' ';
+ char quote = m_entries[i].quote;
+ if (quote != '\0')
+ command += quote;
command += m_entries[i].ref();
+ if (quote != '\0')
+ command += quote;
}
return !m_entries.empty();