Modify HandleCommand to not do any argument processing until it has determined whether or

not the command should take raw input, then handle & dispatch the arguments appropriately.

Also change the 'alias' command to be a command that takes raw input.  This is necessary to
allow aliases to be created for other commands that take raw input and might want to include
raw input in the alias itself.

Fix a bug in the aliasing mechanism when creating aliases for commands with 3-or-more words.

Raw input should now be properly handled by all the command and alias mechanisms.

llvm-svn: 121423
This commit is contained in:
Caroline Tice 2010-12-09 22:52:49 +00:00
parent 2bac54fe0c
commit 844d230332
5 changed files with 489 additions and 163 deletions

View File

@ -313,7 +313,8 @@ public:
// and it builds up the option_arg_vector as it parses the options.
void
ParseAliasOptions (Options &options, CommandReturnObject &result, OptionArgVector *option_arg_vector);
ParseAliasOptions (Options &options, CommandReturnObject &result, OptionArgVector *option_arg_vector,
std::string &raw_input_line);
void
ParseArgsForCompletion (Options &options, OptionElementVector &option_element_vector, uint32_t cursor_index);

View File

@ -84,12 +84,22 @@ public:
void
AddOrReplaceAliasOptions (const char *alias_name, OptionArgVectorSP &option_arg_vector_sp);
bool
StripFirstWord (std::string &command_string, std::string &next_word);
void
BuildAliasResult (const char *alias_name, std::string &raw_input_string, std::string &alias_result,
CommandObject *&alias_cmd_obj, CommandReturnObject &result);
bool
HandleCommand (const char *command_line,
bool add_to_history,
CommandReturnObject &result,
ExecutionContext *override_context = NULL);
CommandObject *
GetCommandObjectForCommand (std::string &command_line);
// This handles command line completion. You are given a pointer to the command string buffer, to the current cursor,
// and to the end of the string (in case it is not NULL terminated).
// You also passed in an Args object to fill with the returns.

View File

@ -239,6 +239,133 @@ public:
{
}
bool
WantsRawCommandString ()
{
return true;
}
bool
ExecuteRawCommandString (const char *raw_command_line, CommandReturnObject &result)
{
Args args (raw_command_line);
std::string raw_command_string (raw_command_line);
size_t argc = args.GetArgumentCount();
if (argc < 2)
{
result.AppendError ("'alias' requires at least two arguments");
result.SetStatus (eReturnStatusFailed);
return false;
}
// Get the alias command.
const std::string alias_command = args.GetArgumentAtIndex (0);
// Strip the new alias name off 'raw_command_string' (leave it on args, which gets passed to 'Execute', which
// does the stripping itself.
size_t pos = raw_command_string.find (alias_command);
if (pos == 0)
{
raw_command_string = raw_command_string.substr (alias_command.size());
pos = raw_command_string.find_first_not_of (' ');
if ((pos != std::string::npos) && (pos > 0))
raw_command_string = raw_command_string.substr (pos);
}
else
{
result.AppendError ("Error parsing command string. No alias created.");
result.SetStatus (eReturnStatusFailed);
return false;
}
// Verify that the command is alias-able.
if (m_interpreter.CommandExists (alias_command.c_str()))
{
result.AppendErrorWithFormat ("'%s' is a permanent debugger command and cannot be redefined.\n",
alias_command.c_str());
result.SetStatus (eReturnStatusFailed);
return false;
}
// Get CommandObject that is being aliased. The command name is read from the front of raw_command_string.
// raw_command_string is returned with the name of the command object stripped off the front.
CommandObject *cmd_obj = m_interpreter.GetCommandObjectForCommand (raw_command_string);
if (!cmd_obj)
{
result.AppendErrorWithFormat ("Invalid command given to 'alias'. '%s' does not begin with a valid command."
" No alias created.", raw_command_string.c_str());
result.SetStatus (eReturnStatusFailed);
return false;
}
else if (!cmd_obj->WantsRawCommandString ())
{
// Note that args was initialized with the original command, and has not been updated to this point.
// Therefore can we pass it to the version of Execute that does not need/expect raw input in the alias.
return Execute (args, result);
}
else
{
// Verify & handle any options/arguments passed to the alias command
OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector);
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
// Check to see if there's anything left in the input command string.
if (raw_command_string.size() > 0)
{
// Check to see if the command being aliased can take any command options.
Options *options = cmd_obj->GetOptions();
if (options)
{
// See if any options were specified as part of the alias; if so, handle them appropriately
options->ResetOptionValues ();
Args tmp_args (raw_command_string.c_str());
args.Unshift ("dummy_arg");
args.ParseAliasOptions (*options, result, option_arg_vector, raw_command_string);
args.Shift ();
if (result.Succeeded())
options->VerifyPartialOptions (result);
if (!result.Succeeded() && result.GetStatus() != lldb::eReturnStatusStarted)
{
result.AppendError ("Unable to create requested alias.\n");
return false;
}
}
// Anything remaining must be plain raw input. Push it in as a single raw input argument.
if (raw_command_string.size() > 0)
option_arg_vector->push_back (OptionArgPair ("<argument>",
OptionArgValue (-1,
raw_command_string)));
}
// Create the alias
if (m_interpreter.AliasExists (alias_command.c_str())
|| m_interpreter.UserCommandExists (alias_command.c_str()))
{
OptionArgVectorSP temp_option_arg_sp (m_interpreter.GetAliasOptions (alias_command.c_str()));
if (temp_option_arg_sp.get())
{
if (option_arg_vector->size() == 0)
m_interpreter.RemoveAliasOptions (alias_command.c_str());
}
result.AppendWarningWithFormat ("Overwriting existing definition for '%s'.\n",
alias_command.c_str());
}
CommandObjectSP cmd_obj_sp = m_interpreter.GetCommandSPExact (cmd_obj->GetCommandName(), false);
m_interpreter.AddAlias (alias_command.c_str(), cmd_obj_sp);
if (option_arg_vector->size() > 0)
m_interpreter.AddOrReplaceAliasOptions (alias_command.c_str(), option_arg_vector_sp);
result.SetStatus (eReturnStatusSuccessFinishNoResult);
}
return result.Succeeded();
}
bool
Execute
@ -282,7 +409,7 @@ public:
OptionArgVectorSP option_arg_vector_sp = OptionArgVectorSP (new OptionArgVector);
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
if (cmd_obj->IsMultiwordObject())
while (cmd_obj->IsMultiwordObject() && args.GetArgumentCount() > 0)
{
if (argc >= 3)
{
@ -295,6 +422,7 @@ public:
sub_cmd_obj = subcommand_obj_sp.get();
use_subcommand = true;
args.Shift(); // Shift the sub_command word off the argument vector.
cmd_obj = sub_cmd_obj;
}
else
{
@ -320,8 +448,9 @@ public:
else
options = cmd_obj->GetOptions();
options->ResetOptionValues ();
std::string empty_string;
args.Unshift ("dummy_arg");
args.ParseAliasOptions (*options, result, option_arg_vector);
args.ParseAliasOptions (*options, result, option_arg_vector, empty_string);
args.Shift ();
if (result.Succeeded())
options->VerifyPartialOptions (result);

View File

@ -849,7 +849,8 @@ Args::IsPositionalArgument (const char *arg)
void
Args::ParseAliasOptions (Options &options,
CommandReturnObject &result,
OptionArgVector *option_arg_vector)
OptionArgVector *option_arg_vector,
std::string &raw_input_string)
{
StreamString sstr;
int i;
@ -985,16 +986,33 @@ Args::ParseAliasOptions (Options &options,
if (long_options_index >= 0)
{
// 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.
// supplied. Remove option (and argument, if given) from the argument list. Also remove them from
// the raw_input_string, if one was passed in.
size_t idx = FindArgumentIndexForOption (long_options, long_options_index);
if (idx < GetArgumentCount())
{
if (raw_input_string.size() > 0)
{
const char *tmp_arg = GetArgumentAtIndex (idx);
size_t pos = raw_input_string.find (tmp_arg);
if (pos != std::string::npos)
raw_input_string.erase (pos, strlen (tmp_arg));
}
ReplaceArgumentAtIndex (idx, "");
if ((long_options[long_options_index].has_arg != no_argument)
&& (optarg != NULL)
&& (idx+1 < GetArgumentCount())
&& (strcmp (optarg, GetArgumentAtIndex(idx+1)) == 0))
{
if (raw_input_string.size() > 0)
{
const char *tmp_arg = GetArgumentAtIndex (idx+1);
size_t pos = raw_input_string.find (tmp_arg);
if (pos != std::string::npos)
raw_input_string.erase (pos, strlen (tmp_arg));
}
ReplaceArgumentAtIndex (idx+1, "");
}
}
}

View File

@ -494,30 +494,184 @@ CommandInterpreter::GetHelp (CommandReturnObject &result)
result.AppendMessage("For more information on any particular command, try 'help <command-name>'.");
}
bool
CommandInterpreter::HandleCommand
(
const char *command_line,
bool add_to_history,
CommandReturnObject &result,
ExecutionContext *override_context
)
CommandObject *
CommandInterpreter::GetCommandObjectForCommand (std::string &command_string)
{
LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMANDS));
// FIXME: there should probably be a mutex to make sure only one thread can
// run the interpreter at a time.
// This function finds the final, lowest-level, alias-resolved command object whose 'Execute' function will
// eventually be invoked by the given command line.
CommandObject *cmd_obj = NULL;
std::string white_space (" \t\v");
size_t start = command_string.find_first_not_of (white_space);
size_t end = 0;
bool done = false;
while (!done)
{
if (start != std::string::npos)
{
// Get the next word from command_string.
end = command_string.find_first_of (white_space, start);
if (end == std::string::npos)
end = command_string.size();
std::string cmd_word = command_string.substr (start, end - start);
if (cmd_obj == NULL)
// Since cmd_obj is NULL we are on our first time through this loop. Check to see if cmd_word is a valid
// command or alias.
cmd_obj = GetCommandObject (cmd_word.c_str());
else if (cmd_obj->IsMultiwordObject ())
{
// Our current object is a multi-word object; see if the cmd_word is a valid sub-command for our object.
CommandObject *sub_cmd_obj =
((CommandObjectMultiword *) cmd_obj)->GetSubcommandObject (cmd_word.c_str());
if (sub_cmd_obj)
cmd_obj = sub_cmd_obj;
else // cmd_word was not a valid sub-command word, so we are donee
done = true;
}
else
// We have a cmd_obj and it is not a multi-word object, so we are done.
done = true;
// If we didn't find a valid command object, or our command object is not a multi-word object, or
// we are at the end of the command_string, then we are done. Otherwise, find the start of the
// next word.
if (!cmd_obj || !cmd_obj->IsMultiwordObject() || end >= command_string.size())
done = true;
else
start = command_string.find_first_not_of (white_space, end);
}
else
// Unable to find any more words.
done = true;
}
if (end == command_string.size())
command_string.clear();
else
command_string = command_string.substr(end);
return cmd_obj;
}
bool
CommandInterpreter::StripFirstWord (std::string &command_string, std::string &word)
{
std::string white_space (" \t\v");
size_t start;
size_t end;
start = command_string.find_first_not_of (white_space);
if (start != std::string::npos)
{
end = command_string.find_first_of (white_space, start);
if (end != std::string::npos)
{
word = command_string.substr (start, end - start);
command_string = command_string.substr (end);
size_t pos = command_string.find_first_not_of (white_space);
if ((pos != 0) && (pos != std::string::npos))
command_string = command_string.substr (pos);
}
else
{
word = command_string.substr (start);
command_string.erase();
}
}
return true;
}
void
CommandInterpreter::BuildAliasResult (const char *alias_name, std::string &raw_input_string, std::string &alias_result,
CommandObject *&alias_cmd_obj, CommandReturnObject &result)
{
Args cmd_args (raw_input_string.c_str());
alias_cmd_obj = GetCommandObject (alias_name);
StreamString result_str;
if (alias_cmd_obj)
{
std::string alias_name_str = alias_name;
if ((cmd_args.GetArgumentCount() == 0)
|| (alias_name_str.compare (cmd_args.GetArgumentAtIndex(0)) != 0))
cmd_args.Unshift (alias_name);
result_str.Printf ("%s", alias_cmd_obj->GetCommandName ());
OptionArgVectorSP option_arg_vector_sp = GetAliasOptions (alias_name);
if (option_arg_vector_sp.get())
{
OptionArgVector *option_arg_vector = option_arg_vector_sp.get();
for (int i = 0; i < option_arg_vector->size(); ++i)
{
OptionArgPair option_pair = (*option_arg_vector)[i];
OptionArgValue value_pair = option_pair.second;
int value_type = value_pair.first;
std::string option = option_pair.first;
std::string value = value_pair.second;
if (option.compare ("<argument>") == 0)
result_str.Printf (" %s", value.c_str());
else
{
result_str.Printf (" %s", option.c_str());
if (value_type != optional_argument)
result_str.Printf (" ");
if (value.compare ("<no_argument>") != 0)
{
int index = GetOptionArgumentPosition (value.c_str());
if (index == 0)
result_str.Printf ("%s", value.c_str());
else if (index >= cmd_args.GetArgumentCount())
{
result.AppendErrorWithFormat
("Not enough arguments provided; you need at least %d arguments to use this alias.\n",
index);
result.SetStatus (eReturnStatusFailed);
return;
}
else
{
size_t strpos = raw_input_string.find (cmd_args.GetArgumentAtIndex (index));
if (strpos != std::string::npos)
raw_input_string = raw_input_string.erase (strpos,
strlen (cmd_args.GetArgumentAtIndex (index)));
result_str.Printf ("%s", cmd_args.GetArgumentAtIndex (index));
}
}
}
}
}
alias_result = result_str.GetData();
}
}
bool
CommandInterpreter::HandleCommand (const char *command_line,
bool add_to_history,
CommandReturnObject &result,
ExecutionContext *override_context)
{
bool done = false;
CommandObject *cmd_obj = NULL;
std::string next_word;
bool wants_raw_input = false;
std::string command_string (command_line);
LogSP log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_COMMANDS));
Host::SetCrashDescriptionWithFormat ("HandleCommand(command = \"%s\")", command_line);
// Make a scoped cleanup object that will clear the crash description string
// on exit of this function.
lldb_utility::CleanUp <const char *, void> crash_description_cleanup(NULL, Host::SetCrashDescription);
// TODO: this should be a logging channel in lldb.
// if (DebugSelf())
// {
// result.AppendMessageWithFormat ("Processing command: %s\n", command_line);
// }
if (log)
log->Printf ("Processing command: %s", command_line);
Timer scoped_timer (__PRETTY_FUNCTION__, "Handling command: %s.", command_line);
@ -534,6 +688,7 @@ CommandInterpreter::HandleCommand
else
{
command_line = m_repeat_command.c_str();
command_string = command_line;
if (m_repeat_command.empty())
{
result.AppendErrorWithFormat("No auto repeat.\n");
@ -544,158 +699,171 @@ CommandInterpreter::HandleCommand
add_to_history = false;
}
if (log)
log->Printf ("CommandInterpreter::HandleCommand, command_line = '%s'", command_line);
// Phase 1.
// Before we do ANY kind of argument processing, etc. we need to figure out what the real/final command object
// is for the specified command, and whether or not it wants raw input. This gets complicated by the fact that
// the user could have specified an alias, and in translating the alias there may also be command options and/or
// even data (including raw text strings) that need to be found and inserted into the command line as part of
// the translation. So this first step is plain look-up & replacement, resulting in three things: 1). the command
// object whose Execute method will actually be called; 2). a revised command string, with all substituitions &
// replacements taken care of; 3). whether or not the Execute function wants raw input or not.
Args command_args(command_line);
if (command_args.GetArgumentCount() > 0)
StreamString revised_command_line;
while (!done)
{
std::string raw_command_string (command_line); // For commands that take raw input but may be aliased or
// have command options.
const char *command_cstr = command_args.GetArgumentAtIndex(0);
if (command_cstr)
StripFirstWord (command_string, next_word);
if (!cmd_obj && AliasExists (next_word.c_str()))
{
// We're looking up the command object here. So first find an exact match to the
// command in the commands.
CommandObject *command_obj = GetCommandObject(command_cstr);
if (command_obj != NULL)
std::string alias_result;
BuildAliasResult (next_word.c_str(), command_string, alias_result, cmd_obj, result);
revised_command_line.Printf ("%s", alias_result.c_str());
if (cmd_obj)
wants_raw_input = cmd_obj->WantsRawCommandString ();
}
else if (!cmd_obj)
{
cmd_obj = GetCommandObject (next_word.c_str());
if (cmd_obj)
{
// Strip command name from the raw_command_string.
if (raw_command_string.find (command_cstr) == 0)
{
// The command name is at the front of the string (where it should be) so strip it off.
raw_command_string = raw_command_string.substr (strlen (command_cstr));
}
std::string aliased_cmd_str;
if (command_obj->IsAlias())
{
if (log)
log->Printf ("Command '%s' is an alias. Building alias command args.", command_cstr);
BuildAliasCommandArgs (command_obj, command_cstr, command_args, raw_command_string, result);
if (!result.Succeeded())
{
if (log)
log->Printf ("Building alias command args failed.");
return false;
}
else
{
// We need to transfer the newly constructed args back into the command_line, in case
// this happens to be an alias for a command that takes raw input.
if (command_args.GetCommandString (aliased_cmd_str))
{
if (log)
log->Printf ("Result string from BuildAliasCommandArgs is '%s'", aliased_cmd_str.c_str());
if (command_obj->WantsRawCommandString())
{
if (log)
{
log->Printf ("This command takes raw input.");
}
aliased_cmd_str.append (" ");
aliased_cmd_str.append (raw_command_string);
}
else
{
if (log)
log->Printf ("This command does NOT take raw input.");
}
command_line = aliased_cmd_str.c_str();
command_cstr = command_args.GetArgumentAtIndex (0);
if (log)
{
log->Printf ("Final command line, after resolving aliases is '%s'", command_line);
}
}
}
}
if (add_to_history)
{
const char *repeat_command = command_obj->GetRepeatCommand(command_args, 0);
if (repeat_command != NULL)
m_repeat_command.assign(repeat_command);
else
m_repeat_command.assign(command_line);
m_command_history.push_back (command_line);
}
if (command_obj->WantsRawCommandString())
{
const char *stripped_command = ::strstr (command_line, command_cstr);
if (stripped_command)
{
stripped_command += strlen(command_cstr);
while (isspace(*stripped_command))
++stripped_command;
if (log)
log->Printf ("Input string being passed to ExecuteRawCommandString for '%s' command is '%s'",
command_obj->GetCommandName(), stripped_command);
command_obj->ExecuteRawCommandString (stripped_command, result);
}
}
else
{
// Remove the command from the args.
command_args.Shift();
if (log)
{
size_t count = command_args.GetArgumentCount();
log->Printf ("ExecuteWithOptions for '%s' command is being called with %d args:",
command_obj->GetCommandName(), count);
for (size_t k = 0; k < count; ++k)
log->Printf (" arg[%d]='%s'", k, command_args.GetArgumentAtIndex (k));
}
command_obj->ExecuteWithOptions (command_args, result);
}
revised_command_line.Printf ("%s", next_word.c_str());
wants_raw_input = cmd_obj->WantsRawCommandString ();
}
else
{
// We didn't find the first command object, so complete the first argument.
StringList matches;
int num_matches;
int cursor_index = 0;
int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0));
bool word_complete;
num_matches = HandleCompletionMatches (command_args,
cursor_index,
cursor_char_position,
0,
-1,
word_complete,
matches);
if (num_matches > 0)
{
std::string error_msg;
error_msg.assign ("ambiguous command '");
error_msg.append(command_cstr);
error_msg.append ("'.");
error_msg.append (" Possible completions:");
for (int i = 0; i < num_matches; i++)
{
error_msg.append ("\n\t");
error_msg.append (matches.GetStringAtIndex (i));
}
error_msg.append ("\n");
result.AppendRawError (error_msg.c_str(), error_msg.size());
}
else
result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_cstr);
result.SetStatus (eReturnStatusFailed);
revised_command_line.Printf ("%s", next_word.c_str());
}
}
else if (cmd_obj->IsMultiwordObject ())
{
CommandObject *sub_cmd_obj = ((CommandObjectMultiword *) cmd_obj)->GetSubcommandObject (next_word.c_str());
if (sub_cmd_obj)
{
revised_command_line.Printf (" %s", next_word.c_str());
cmd_obj = sub_cmd_obj;
wants_raw_input = cmd_obj->WantsRawCommandString ();
}
else
{
revised_command_line.Printf (" %s", next_word.c_str());
done = true;
}
}
else
{
revised_command_line.Printf (" %s", next_word.c_str());
done = true;
}
if (cmd_obj == NULL)
{
result.AppendErrorWithFormat ("'%s' is not a valid command.\n", next_word.c_str());
result.SetStatus (eReturnStatusFailed);
return false;
}
next_word.erase ();
if (command_string.length() == 0)
done = true;
}
if (command_string.size() > 0)
revised_command_line.Printf (" %s", command_string.c_str());
// End of Phase 1.
// At this point cmd_obj should contain the CommandObject whose Execute method will be called, if the command
// specified was valid; revised_command_line contains the complete command line (including command name(s)),
// fully translated with all substitutions & translations taken care of (still in raw text format); and
// wants_raw_input specifies whether the Execute method expects raw input or not.
if (log)
{
log->Printf ("HandleCommand, cmd_obj : '%s'", cmd_obj ? cmd_obj->GetCommandName() : "<not found>");
log->Printf ("HandleCommand, revised_command_line: '%s'", revised_command_line.GetData());
log->Printf ("HandleCommand, wants_raw_input:'%s'", wants_raw_input ? "True" : "False");
}
// Phase 2.
// Take care of things like setting up the history command & calling the appropriate Execute method on the
// CommandObject, with the appropriate arguments.
if (cmd_obj != NULL)
{
if (add_to_history)
{
Args command_args (revised_command_line.GetData());
const char *repeat_command = cmd_obj->GetRepeatCommand(command_args, 0);
if (repeat_command != NULL)
m_repeat_command.assign(repeat_command);
else
m_repeat_command.assign(command_line);
m_command_history.push_back (command_line);
}
command_string = revised_command_line.GetData();
std::string command_name (cmd_obj->GetCommandName());
std::string remainder (command_string.substr (command_name.size()));
// Remove any initial spaces
std::string white_space (" \t\v");
size_t pos = remainder.find_first_not_of (white_space);
if (pos != 0 && pos != std::string::npos)
remainder = remainder.substr (pos);
if (log)
log->Printf ("HandleCommand, command line after removing command name(s): '%s'\n", remainder.c_str());
if (wants_raw_input)
cmd_obj->ExecuteRawCommandString (remainder.c_str(), result);
else
{
Args cmd_args (remainder.c_str());
cmd_obj->ExecuteWithOptions (cmd_args, result);
}
}
else
{
// We didn't find the first command object, so complete the first argument.
Args command_args (revised_command_line.GetData());
StringList matches;
int num_matches;
int cursor_index = 0;
int cursor_char_position = strlen (command_args.GetArgumentAtIndex(0));
bool word_complete;
num_matches = HandleCompletionMatches (command_args,
cursor_index,
cursor_char_position,
0,
-1,
word_complete,
matches);
if (num_matches > 0)
{
std::string error_msg;
error_msg.assign ("ambiguous command '");
error_msg.append(command_args.GetArgumentAtIndex(0));
error_msg.append ("'.");
error_msg.append (" Possible completions:");
for (int i = 0; i < num_matches; i++)
{
error_msg.append ("\n\t");
error_msg.append (matches.GetStringAtIndex (i));
}
error_msg.append ("\n");
result.AppendRawError (error_msg.c_str(), error_msg.size());
}
else
result.AppendErrorWithFormat ("Unrecognized command '%s'.\n", command_args.GetArgumentAtIndex (0));
result.SetStatus (eReturnStatusFailed);
}
return result.Succeeded();
}