Refactoring

- Moved argument handling out of Context, into Arguments.
This commit is contained in:
Paul Beckingham 2011-05-27 23:20:17 -04:00
parent 3de1275afe
commit d51bd3f445
5 changed files with 310 additions and 188 deletions

243
src/Arguments.cpp Normal file
View file

@ -0,0 +1,243 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sys/select.h>
#include <Context.h>
#include <Nibbler.h>
#include <text.h>
#include <Arguments.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
Arguments::Arguments ()
{
}
////////////////////////////////////////////////////////////////////////////////
Arguments::~Arguments ()
{
}
////////////////////////////////////////////////////////////////////////////////
void Arguments::capture (int argc, char** argv)
{
for (int i = 0; i < argc; ++i)
{
/*
if (i == 0)
{
std::string::size_type cal = context.program.find ("/cal");
if (context.program == "cal" ||
(cal != std::string::npos && context.program.length () == cal + 4))
this->push_back ("calendar");
}
else
*/
this->push_back (argv[i]);
}
}
////////////////////////////////////////////////////////////////////////////////
void Arguments::append_stdin ()
{
// Capture any stdin args.
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&fds);
FD_SET (STDIN_FILENO, &fds);
select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if (FD_ISSET (0, &fds))
{
std::string arg;
while (std::cin >> arg)
{
if (arg == "--")
break;
this->push_back (arg);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Arguments::rc_override (
std::string& home,
File& rc,
std::string& override)
{
// Is there an override for rc:<file>?
std::vector <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:")
{
override = *arg;
rc = File (arg->substr (3));
home = rc;
std::string::size_type last_slash = rc.data.rfind ("/");
if (last_slash != std::string::npos)
home = rc.data.substr (0, last_slash);
else
home = ".";
this->erase (arg);
context.header ("Using alternate .taskrc file " + rc.data); // TODO i18n
break; // Must break - iterator is dead.
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Arguments::get_data_location (std::string& data)
{
std::string location = context.config.get ("data.location");
if (location != "")
data = location;
// Are there any overrides for data.location?
std::vector <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] == '='))
{
data = arg->substr (17);
context.header ("Using alternate data.location " + data); // TODO i18n
}
}
}
////////////////////////////////////////////////////////////////////////////////
// Extracts any rc.name:value args and sets the name/value in context.config,
// 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;
for (arg = this->begin (); arg != this->end (); ++arg)
{
if (*arg == "--")
{
foundTerminator = true;
filtered.push_back (*arg);
}
else if (!foundTerminator && arg->substr (0, 3) == "rc.")
{
std::string name;
std::string value;
Nibbler n (*arg);
if (n.getLiteral ("rc.") && // rc.
n.getUntilOneOf (":=", name) && // xxx
n.skipN (1)) // :
{
n.getUntilEOS (value); // Don't care if it's blank.
context.config.set (name, value);
context.footnote ("Configuration override " + arg->substr (3));
// Overrides are retained for potential use by the default command.
var_overrides += " " + *arg;
}
else
context.footnote ("Problem with override: " + *arg);
}
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);
}
////////////////////////////////////////////////////////////////////////////////
// An alias must be a distinct word on the command line.
// Aliases may not recurse.
void Arguments::resolve_aliases ()
{
std::vector <std::string> expanded;
bool something = false;
std::vector <std::string>::iterator arg;
for (arg = this->begin (); arg != this->end (); ++arg)
{
std::map <std::string, std::string>::iterator match =
context.aliases.find (*arg);
if (match != context.aliases.end ())
{
context.debug (std::string ("Arguments::resolve_aliases '")
+ *arg
+ "' --> '"
+ context.aliases[*arg]
+ "'");
std::vector <std::string> words;
splitq (words, context.aliases[*arg], ' ');
std::vector <std::string>::iterator word;
for (word = words.begin (); word != words.end (); ++word)
expanded.push_back (*word);
something = true;
}
else
expanded.push_back (*arg);
}
// Only overwrite if something happened.
if (something)
{
this->clear ();
for (arg = expanded.begin (); arg != expanded.end (); ++arg)
this->push_back (*arg);
}
}
////////////////////////////////////////////////////////////////////////////////
std::string Arguments::combine ()
{
std::string combined;
join (combined, " ", *(std::vector <std::string>*)this);
return combined;
}
////////////////////////////////////////////////////////////////////////////////

50
src/Arguments.h Normal file
View file

@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
// All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the
//
// Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_ARGUMENTS
#define INCLUDED_ARGUMENTS
#include <vector>
#include <string>
#include <File.h>
class Arguments : public std::vector <std::string>
{
public:
Arguments ();
~Arguments ();
void capture (int, char**);
void append_stdin ();
void rc_override (std::string&, File&, std::string&);
void get_data_location (std::string&);
void apply_overrides (std::string&);
void resolve_aliases ();
std::string combine ();
};
#endif
////////////////////////////////////////////////////////////////////////////////

View file

@ -6,6 +6,7 @@ include_directories (${CMAKE_SOURCE_DIR}
${TASK_INCLUDE_DIRS})
set (task_SRCS API.cpp API.h
Arguments.cpp Arguments.h
Att.cpp Att.h
Cmd.cpp Cmd.h
Color.cpp Color.h

View file

@ -32,7 +32,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <Context.h>
#include <Directory.h>
#include <File.h>
@ -81,18 +80,19 @@ void Context::initialize (int argc, char** argv)
Timer t ("Context::initialize");
// char** argv --> std::vector <std::string> Context::args.
captureCommandLineArgs (argc, argv);
program = argv[0];
args.capture (argc, argv);
// echo one two -- three | task zero --> task zero one two
// 'three' is left in the input buffer.
appendPipedArgs ();
args.append_stdin ();
// Assume default .taskrc and .task locations.
assumeLocations ();
// Process 'rc:<file>' command line override, and remove the argument from the
// Context::args.
overrideRCFile ();
args.rc_override (home_dir, rc_file, file_override);
// Dump any existing values and load rc file.
config.clear ();
@ -101,20 +101,23 @@ void Context::initialize (int argc, char** argv)
// The data location, Context::data_dir, is determined from the assumed
// location (~/.task), or set by data.location in the config file, or
// overridden by rc.data.location on the command line.
determineDataLocation ();
std::string location;
args.get_data_location (location);
data_dir = Directory (location);
extension_dir = data_dir.data + "/extensions";
// Create missing config file and data directory, if necessary.
createDefaultConfig ();
// Apply rc overrides to Context::config.
applyOverrides ();
// Apply rc overrides to Context::config, capturing raw args for later use.
args.apply_overrides (var_overrides);
// Handle Aliases.
loadAliases ();
resolveAliases ();
args.resolve_aliases ();
// Combine command line into one string.
join (commandLine, " ", args);
commandLine = args.combine ();
// Initialize the color rules, if necessary.
if (color ())
@ -445,93 +448,6 @@ void Context::disallowModification () const
+ "' command does not allow further modification of a task.";
}
////////////////////////////////////////////////////////////////////////////////
// Takes a vector of args (foo, rc.name:value, bar), extracts any rc.name:value
// args and sets the name/value in context.config, returning only the plain args
// (foo, bar) as output.
void Context::applyOverrides ()
{
std::vector <std::string> filtered;
bool foundTerminator = false;
std::vector <std::string>::iterator arg;
for (arg = args.begin (); arg != args.end (); ++arg)
{
if (*arg == "--")
{
foundTerminator = true;
filtered.push_back (*arg);
}
else if (!foundTerminator && arg->substr (0, 3) == "rc.")
{
std::string name;
std::string value;
Nibbler n (*arg);
if (n.getLiteral ("rc.") && // rc.
n.getUntilOneOf (":=", name) && // xxx
n.skipN (1)) // :
{
n.getUntilEOS (value); // Don't care if it's blank.
config.set (name, value);
footnote ("Configuration override " + arg->substr (3));
// Overrides are retained for potential use by the default command.
var_overrides += " " + *arg;
}
else
footnote ("Problem with override: " + *arg);
}
else
filtered.push_back (*arg);
}
// Overwrite args with the filtered subset.
args = filtered;
}
////////////////////////////////////////////////////////////////////////////////
void Context::captureCommandLineArgs (int argc, char** argv)
{
for (int i = 0; i < argc; ++i)
{
if (i == 0)
{
program = argv[i];
std::string::size_type cal = program.find ("/cal");
if (program == "cal" ||
(cal != std::string::npos && program.length () == cal + 4))
args.push_back ("calendar");
}
else
args.push_back (argv[i]);
}
}
////////////////////////////////////////////////////////////////////////////////
void Context::appendPipedArgs ()
{
// Capture any stdin args.
struct timeval tv;
fd_set fds;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO (&fds);
FD_SET (STDIN_FILENO, &fds);
select (STDIN_FILENO + 1, &fds, NULL, NULL, &tv);
if (FD_ISSET (0, &fds))
{
std::string arg;
while (std::cin >> arg)
{
if (arg == "--")
break;
args.push_back (arg);
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Context::assumeLocations ()
{
@ -545,58 +461,6 @@ void Context::assumeLocations ()
data_dir = Directory (home_dir + "./task");
}
////////////////////////////////////////////////////////////////////////////////
void Context::overrideRCFile ()
{
// Is there an override for rc:<file>?
std::vector <std::string>::iterator arg;
for (arg = args.begin (); arg != args.end (); ++arg)
{
// Nothing after -- is to be interpreted in any way.
if (*arg == "--")
break;
else if (arg->substr (0, 3) == "rc:")
{
file_override = *arg;
rc_file = File (arg->substr (3));
home_dir = rc_file;
std::string::size_type last_slash = rc_file.data.rfind ("/");
if (last_slash != std::string::npos)
home_dir = rc_file.data.substr (0, last_slash);
else
home_dir = ".";
args.erase (arg);
header ("Using alternate .taskrc file " + rc_file.data); // TODO i18n
break; // Must break - iterator is dead.
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Context::determineDataLocation ()
{
if (config.get ("data.location") != "")
data_dir = Directory (config.get ("data.location"));
// Are there any overrides for data.location?
foreach (arg, args)
{
if (*arg == "--")
break;
else if (arg->substr (0, 16) == "rc.data.location" &&
((*arg)[16] == ':' || (*arg)[16] == '='))
{
data_dir = Directory (arg->substr (17));
extension_dir = data_dir.data + "/extensions";
header ("Using alternate data.location " + data_dir.data); // TODO i18n
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Context::createDefaultConfig ()
{
@ -618,6 +482,7 @@ void Context::createDefaultConfig ()
config.createDefaultData (data_dir);
// Create extension directory, if necessary.
if (! extension_dir.exists ())
extension_dir.create ();
}
@ -635,39 +500,6 @@ void Context::loadAliases ()
aliases[var->substr (6)] = config.get (*var);
}
////////////////////////////////////////////////////////////////////////////////
// An alias must be a distinct word on the command line.
// Aliases may not recurse.
void Context::resolveAliases ()
{
std::vector <std::string> expanded;
bool something = false;
std::vector <std::string>::iterator arg;
for (arg = args.begin (); arg != args.end (); ++arg)
{
std::map <std::string, std::string>::iterator match = aliases.find (*arg);
if (match != aliases.end ())
{
debug (std::string ("Context::resolveAliases '") + *arg + "' --> '" + aliases[*arg] + "'");
std::vector <std::string> words;
splitq (words, aliases[*arg], ' ');
std::vector <std::string>::iterator word;
for (word = words.begin (); word != words.end (); ++word)
expanded.push_back (*word);
something = true;
}
else
expanded.push_back (*arg);
}
if (something)
args = expanded;
}
////////////////////////////////////////////////////////////////////////////////
void Context::parse ()
{

View file

@ -42,6 +42,7 @@
#include <Path.h>
#include <File.h>
#include <Directory.h>
#include <Arguments.h>
class Context
{
@ -74,25 +75,20 @@ public:
void clear ();
void disallowModification () const;
void applyOverrides ();
void decomposeSortField (const std::string&, std::string&, bool&);
private:
void captureCommandLineArgs (int, char**);
void appendPipedArgs ();
void assumeLocations ();
void overrideRCFile ();
void determineDataLocation ();
void createDefaultConfig ();
void loadAliases ();
void resolveAliases ();
void autoFilter (Att&, Filter&);
void autoFilter (Filter&);
void updateXtermTitle ();
public:
std::string program;
std::vector <std::string> args;
Arguments args;
std::string home_dir;
File rc_file;
Path data_dir;