Argument Parsing

- Obsoleted Command::exectute  'commandLine' argument.  It is worse
  than unnecessary, it is an uncategorized raw argument string, which
  is only really useful for the 'execute' command, which itself now
  calls Arguments::combine to reconstruct the command line string.
This commit is contained in:
Paul Beckingham
2011-06-04 12:28:50 -04:00
parent c2e1757fb6
commit 644d027a87
99 changed files with 397 additions and 289 deletions

View File

@@ -26,10 +26,13 @@
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <algorithm>
#include <stdlib.h>
#include <sys/select.h>
#include <Context.h>
#include <Nibbler.h>
#include <ViewText.h>
#include <text.h>
#include <util.h>
#include <Arguments.h>
@@ -47,17 +50,29 @@ Arguments::~Arguments ()
}
////////////////////////////////////////////////////////////////////////////////
// Add a pair for every argument, with a category of "".
void Arguments::capture (int argc, const char** argv)
{
for (int i = 0; i < argc; ++i)
if (i > 0)
this->push_back (argv[i]);
this->push_back (std::make_pair (argv[i], (i == 0 ? "program" : "")));
categorize ();
}
////////////////////////////////////////////////////////////////////////////////
// Add a pair with a category of "".
void Arguments::capture (const std::string& arg)
{
this->push_back (std::make_pair (arg, ""));
categorize ();
}
////////////////////////////////////////////////////////////////////////////////
// Add a pair for every word from std::cin, with a category of "".
void Arguments::append_stdin ()
{
// Capture any stdin args.
// Use 'select' to determine whether there is any std::cin content buffered
// before trying to read it, to prevent blocking.
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
@@ -70,12 +85,94 @@ void Arguments::append_stdin ()
std::string arg;
while (std::cin >> arg)
{
// It the terminator token is found, stop reading.
if (arg == "--")
break;
this->push_back (arg);
this->push_back (std::make_pair (arg, ""));
}
}
categorize ();
}
////////////////////////////////////////////////////////////////////////////////
// Scan all the arguments, and assign a category.
void Arguments::categorize ()
{
bool terminated = false;
// Generate a vector of command keywords against which autoComplete can run.
std::vector <std::string> keywords;
std::map <std::string, Command*>::iterator k;
for (k = context.commands.begin (); k != context.commands.end (); ++k)
keywords.push_back (k->first);
// First scan for a command.
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
if (arg->first == "--")
break;
std::vector <std::string> matches;
if (autoComplete (arg->first, keywords, matches) == 1)
{
if (arg->first != matches[0])
context.debug ("Arguments::categorize keyword '" + arg->first + "' --> '" + matches[0] + "'");
else
context.debug ("Arguments::categorize keyword '" + arg->first + "'");
// Not only categorize the command, but overwrite the original command
// the fule name.
arg->first = matches[0];
arg->second = "command";
// Only the first match is a command.
break;
}
}
// Now categorize every uncategorized argument.
for (arg = this->begin (); arg != this->end (); ++arg)
{
if (!terminated)
{
// Nothing after -- is to be interpreted in any way.
if (arg->first == "--")
{
terminated = true;
arg->second = "terminator";
}
// // Only categorize uncategorized args.
// else if (arg->second == "")
else
{
// rc:<file>
if (arg->first.substr (0, 3) == "rc:")
arg->second = "rc";
// rc.<name>[:=]<value>
else if (arg->first.substr (0, 3) == "rc.")
arg->second = "override";
// TODO Sequence
// TODO UUID
// TODO +tag
// TODO -tag
// TODO subst
// TODO attr
else if (arg->second == "")
arg->second = "word";
}
}
// All post-termination arguments are simply words.
else
arg->second = "word";
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -85,17 +182,13 @@ void Arguments::rc_override (
std::string& override)
{
// Is there an override for rc:<file>?
std::vector <std::string>::iterator arg;
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
// Nothing after -- is to be interpreted in any way.
if (*arg == "--")
break;
else if (arg->substr (0, 3) == "rc:")
if (arg->second == "rc")
{
override = *arg;
rc = File (arg->substr (3));
override = arg->first;
rc = File (arg->first.substr (3));
home = rc;
std::string::size_type last_slash = rc.data.rfind ("/");
@@ -104,9 +197,10 @@ void Arguments::rc_override (
else
home = ".";
this->erase (arg);
context.header ("Using alternate .taskrc file " + rc.data); // TODO i18n
break; // Must break - iterator is dead.
// Keep scanning, because if there are multiple rc:file arguments, we
// want the last one to dominate.
}
}
}
@@ -119,17 +213,21 @@ void Arguments::get_data_location (std::string& data)
data = location;
// Are there any overrides for data.location?
std::vector <std::string>::iterator arg;
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
if (*arg == "--")
break;
else if (arg->substr (0, 16) == "rc.data.location" &&
((*arg)[16] == ':' || (*arg)[16] == '='))
if (arg->second == "override")
{
data = arg->substr (17);
context.header ("Using alternate data.location " + data); // TODO i18n
if (arg->first.substr (0, 16) == "rc.data.location" &&
(arg->first[16] == ':' || arg->first[16] == '='))
{
data = arg->first.substr (17);
context.header ("Using alternate data.location " + data); // TODO i18n
}
}
// Keep scanning, because if there are multiple rc:file arguments, we
// want the last one to dominate.
}
}
@@ -138,22 +236,14 @@ void Arguments::get_data_location (std::string& data)
// leaving only the plain args.
void Arguments::apply_overrides (std::string& var_overrides)
{
std::vector <std::string> filtered;
bool foundTerminator = false;
std::vector <std::string>::iterator arg;
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
if (*arg == "--")
{
foundTerminator = true;
filtered.push_back (*arg);
}
else if (!foundTerminator && arg->substr (0, 3) == "rc.")
if (arg->second == "override")
{
std::string name;
std::string value;
Nibbler n (*arg);
Nibbler n (arg->first);
if (n.getLiteral ("rc.") && // rc.
n.getUntilOneOf (":=", name) && // xxx
n.skipN (1)) // :
@@ -161,22 +251,15 @@ void Arguments::apply_overrides (std::string& var_overrides)
n.getUntilEOS (value); // Don't care if it's blank.
context.config.set (name, value);
context.footnote ("Configuration override " + arg->substr (3));
context.footnote ("Configuration override rc." + name + "=" + value);
// Overrides are retained for potential use by the default command.
var_overrides += " " + *arg;
var_overrides += " " + arg->first;
}
else
context.footnote ("Problem with override: " + *arg);
context.footnote ("Problem with override: " + arg->first);
}
else
filtered.push_back (*arg);
}
// Overwrite args with the filtered subset.
this->clear ();
for (arg = filtered.begin (); arg != filtered.end (); ++arg)
this->push_back (*arg);
}
////////////////////////////////////////////////////////////////////////////////
@@ -187,22 +270,22 @@ void Arguments::resolve_aliases ()
std::vector <std::string> expanded;
bool something = false;
std::vector <std::string>::iterator arg;
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
std::map <std::string, std::string>::iterator match =
context.aliases.find (*arg);
context.aliases.find (arg->first);
if (match != context.aliases.end ())
{
context.debug (std::string ("Arguments::resolve_aliases '")
+ *arg
+ arg->first
+ "' --> '"
+ context.aliases[*arg]
+ context.aliases[arg->first]
+ "'");
std::vector <std::string> words;
splitq (words, context.aliases[*arg], ' ');
splitq (words, context.aliases[arg->first], ' ');
std::vector <std::string>::iterator word;
for (word = words.begin (); word != words.end (); ++word)
@@ -211,45 +294,59 @@ void Arguments::resolve_aliases ()
something = true;
}
else
expanded.push_back (*arg);
expanded.push_back (arg->first);
}
// Only overwrite if something happened.
if (something)
{
this->clear ();
for (arg = expanded.begin (); arg != expanded.end (); ++arg)
this->push_back (*arg);
std::vector <std::string>::iterator e;
for (e = expanded.begin (); e != expanded.end (); ++e)
this->push_back (std::make_pair (*e, ""));
// Must now re-categorize everything.
categorize ();
}
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Arguments::list ()
{
std::vector <std::string> all;
std::vector <std::pair <std::string, std::string> >::iterator i;
for (i = this->begin (); i != this->end (); ++i)
all.push_back (i->first);
return all;
}
////////////////////////////////////////////////////////////////////////////////
std::string Arguments::combine ()
{
std::string combined;
join (combined, " ", *(std::vector <std::string>*)this);
std::vector <std::pair <std::string, std::string> >::iterator i;
for (i = this->begin (); i != this->end (); ++i)
{
if (i != this->begin ())
combined += " ";
combined += i->first;
}
return combined;
}
////////////////////////////////////////////////////////////////////////////////
// Given a vector of command keywords, scan all arguments and locate the first
// argument that matches a keyword.
bool Arguments::extract_command (
const std::vector <std::string>& keywords,
std::string& command)
bool Arguments::find_command (std::string& command)
{
std::vector <std::string>::iterator arg;
std::vector <std::pair <std::string, std::string> >::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
std::vector <std::string> matches;
if (autoComplete (*arg, keywords, matches) == 1)
if (arg->second == "command")
{
if (*arg != matches[0])
context.debug ("Arguments::extract_command keyword '" + *arg + "' --> '" + matches[0] + "'");
else
context.debug ("Arguments::extract_command keyword '" + *arg + "'");
command = matches[0];
command = arg->first;
return true;
}
}
@@ -257,24 +354,6 @@ bool Arguments::extract_command (
return false;
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::remove_command (const std::string& command)
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_filter ()
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_modifications ()
{
}
////////////////////////////////////////////////////////////////////////////////
// A sequence can be:
//
@@ -300,6 +379,7 @@ void Arguments::extract_modifications ()
//
void Arguments::extract_sequence (std::vector <int>& sequence)
{
/*
sequence.clear ();
std::vector <int> kill;
@@ -378,44 +458,52 @@ void Arguments::extract_sequence (std::vector <int>& sequence)
// Now remove args in the kill list.
for (unsigned int k = 0; k < kill.size (); ++k)
this->erase (this->begin () + kill[k]);
*/
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_uuids (std::vector <std::string>& uuids)
void Arguments::dump (const std::string& label)
{
uuids.clear ();
// Set up a map of categories to colors.
std::map <std::string, Color> color_map;
color_map["program"] = Color ("white on blue");
color_map["command"] = Color ("black on cyan");
color_map["rc"] = Color ("bold white on red");
color_map["override"] = Color ("white on red");
color_map["none"] = Color ("white on gray3");
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_attrs ()
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_words ()
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_tags ()
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_pattern ()
{
}
////////////////////////////////////////////////////////////////////////////////
// TODO
void Arguments::extract_subst ()
{
Color color_debug (context.config.get ("color.debug"));
std::stringstream out;
out << color_debug.colorize (label)
<< "\n";
ViewText view;
view.width (context.getWidth ());
view.leftMargin (4);
for (unsigned int i = 0; i < this->size (); ++i)
view.add (Column::factory ("string", ""));
view.addRow ();
view.addRow ();
for (unsigned int i = 0; i < this->size (); ++i)
{
std::string arg = (*this)[i].first;
std::string category = (*this)[i].second;
Color c;
if (color_map[category].nontrivial ())
c = color_map[category];
else
c = color_map["none"];
view.set (0, i, arg, c);
view.set (1, i, category, c);
}
out << view.render ();
context.debug (out.str ());
std::cout << out.str (); // TODO Remove
}
////////////////////////////////////////////////////////////////////////////////