From d51bd3f445d70144c48adeef067f5488127cd3b4 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 27 May 2011 23:20:17 -0400 Subject: [PATCH] Refactoring - Moved argument handling out of Context, into Arguments. --- src/Arguments.cpp | 243 +++++++++++++++++++++++++++++++++++++++++++++ src/Arguments.h | 50 ++++++++++ src/CMakeLists.txt | 1 + src/Context.cpp | 196 +++--------------------------------- src/Context.h | 8 +- 5 files changed, 310 insertions(+), 188 deletions(-) create mode 100644 src/Arguments.cpp create mode 100644 src/Arguments.h diff --git a/src/Arguments.cpp b/src/Arguments.cpp new file mode 100644 index 000000000..7774f7b71 --- /dev/null +++ b/src/Arguments.cpp @@ -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 +#include +#include +#include +#include +#include + +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:? + std::vector ::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 ::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 filtered; + bool foundTerminator = false; + + std::vector ::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 expanded; + bool something = false; + + std::vector ::iterator arg; + for (arg = this->begin (); arg != this->end (); ++arg) + { + std::map ::iterator match = + context.aliases.find (*arg); + + if (match != context.aliases.end ()) + { + context.debug (std::string ("Arguments::resolve_aliases '") + + *arg + + "' --> '" + + context.aliases[*arg] + + "'"); + + std::vector words; + splitq (words, context.aliases[*arg], ' '); + + std::vector ::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 *)this); + return combined; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/Arguments.h b/src/Arguments.h new file mode 100644 index 000000000..8f643759a --- /dev/null +++ b/src/Arguments.h @@ -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 +#include +#include + +class Arguments : public std::vector +{ +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 +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 704fbffd8..1cc8f64e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/Context.cpp b/src/Context.cpp index 8b235a40c..270a7eca1 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -81,18 +80,19 @@ void Context::initialize (int argc, char** argv) Timer t ("Context::initialize"); // char** argv --> std::vector 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:' 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 filtered; - bool foundTerminator = false; - - std::vector ::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:? - std::vector ::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,7 +482,8 @@ void Context::createDefaultConfig () config.createDefaultData (data_dir); // Create extension directory, if necessary. - extension_dir.create (); + 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 expanded; - bool something = false; - - std::vector ::iterator arg; - for (arg = args.begin (); arg != args.end (); ++arg) - { - std::map ::iterator match = aliases.find (*arg); - if (match != aliases.end ()) - { - debug (std::string ("Context::resolveAliases '") + *arg + "' --> '" + aliases[*arg] + "'"); - - std::vector words; - splitq (words, aliases[*arg], ' '); - - std::vector ::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 () { diff --git a/src/Context.h b/src/Context.h index 5436aafe4..a0b9179b8 100644 --- a/src/Context.h +++ b/src/Context.h @@ -42,6 +42,7 @@ #include #include #include +#include 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 args; + Arguments args; std::string home_dir; File rc_file; Path data_dir;