diff --git a/src/CLI.cpp b/src/CLI.cpp index 87e5beb58..f6b03f9da 100644 --- a/src/CLI.cpp +++ b/src/CLI.cpp @@ -217,6 +217,95 @@ const std::string A::dump () const return output; } +//////////////////////////////////////////////////////////////////////////////// +// Static method. +void CLI::getOverride (int argc, const char** argv, std::string& home, File& rc) +{ + bool terminated = false; + for (int i = 1; i < argc; ++i) + { + std::string raw = argv[i]; + + if (raw == "--") + terminated = true; + + if (! terminated && + raw.length () > 3 && + raw.substr (0, 3) == "rc:") + { + rc = raw.substr (3); + + home = "."; + std::string::size_type last_slash = rc._data.rfind ("/"); + if (last_slash != std::string::npos) + home = rc.parent (); + + context.header (format (STRING_PARSER_ALTERNATE_RC, rc._data)); + + // Keep looping, because if there are multiple rc:file arguments, the last + // one should dominate. + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Look for CONFIG data.location and initialize a Path object. +void CLI::getDataLocation (int argc, const char** argv, Path& data) +{ + std::string location = context.config.get ("data.location"); + if (location != "") + data = location; + + bool terminated = false; + for (int i = 1; i < argc; ++i) + { + std::string raw = argv[i]; + + if (raw == "--") + terminated = true; + + if (! terminated && + raw.length () > 17 && + raw.substr (0, 16) == "rc.data.location") + { + data = Directory (raw.substr (17)); + context.header (format (STRING_PARSER_ALTERNATE_DATA, (std::string) data)); + + // Keep looping, because if there are multiple rc:file arguments, the last + // one should dominate. + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void CLI::applyOverrides (int argc, const char** argv) +{ + bool terminated = false; + for (int i = 1; i < argc; ++i) + { + std::string raw = argv[i]; + + if (raw == "--") + terminated = true; + + if (! terminated && + raw.length () > 3 && + raw.substr (0, 3) == "rc.") + { + std::string::size_type sep = raw.find ('=', 3); + if (sep == std::string::npos) + sep = raw.find (':', 3); + if (sep != std::string::npos) + { + std::string name = raw.substr (3, sep - 3); + std::string value = raw.substr (sep + 1); + context.config.set (name, value); + context.footnote (format (STRING_PARSER_OVERRIDE_RC, name, value)); + } + } + } +} + //////////////////////////////////////////////////////////////////////////////// CLI::CLI () : _strict (false) @@ -377,54 +466,6 @@ void CLI::applyOverrides () } } -//////////////////////////////////////////////////////////////////////////////// -void CLI::getOverride (std::string& home, File& rc) -{ - std::vector ::const_iterator a; - for (a = _args.begin (); a != _args.end (); ++a) - { - if (a->hasTag ("RC")) - { - rc = File (a->attribute ("file")); - 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 = "."; - - context.header (format (STRING_PARSER_ALTERNATE_RC, rc._data)); - - // Keep looping, because if there are multiple rc:file arguments, the last - // one should dominate. - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Look for CONFIG data.location and initialize a Path object. -void CLI::getDataLocation (Path& data) -{ - std::string location = context.config.get ("data.location"); - if (location != "") - data = location; - - std::vector ::const_iterator a; - for (a = _args.begin (); a != _args.end (); ++a) - { - if (a->hasTag ("CONFIG") && - a->attribute ("name") == "data.location") - { - data = Directory (a->attribute ("value")); - context.header (format (STRING_PARSER_ALTERNATE_DATA, (std::string) data)); - } - - // Keep looping, because if there are multiple overrides, the last one - // should dominate. - } -} - //////////////////////////////////////////////////////////////////////////////// // Extract all the FILTER-tagged items. const std::string CLI::getFilter () diff --git a/src/CLI.h b/src/CLI.h index 9478f26c3..f6461ab85 100644 --- a/src/CLI.h +++ b/src/CLI.h @@ -65,6 +65,9 @@ class CLI { public: static int minimumMatchLength; + static void getOverride (int, const char**, std::string&, File&); + static void getDataLocation (int, const char**, Path&); + static void applyOverrides (int, const char**); public: CLI (); @@ -75,8 +78,6 @@ public: void add (const std::string&); void analyze (bool parse = true, bool strict = false); void applyOverrides (); - void getOverride (std::string&, File&); - void getDataLocation (Path&); const std::string getFilter (); const std::vector getWords (); bool canonicalize (std::string&, const std::string&, const std::string&) const; diff --git a/src/Context.cpp b/src/Context.cpp index 76ab6e172..2ed260817 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -94,8 +94,8 @@ static const char* attributeNames[] = //////////////////////////////////////////////////////////////////////////////// Context::Context () -: rc_file () -, data_dir () +: rc_file ("~/.taskrc") +, data_dir ("~/.task") , config () , tdb2 () , dom () @@ -127,17 +127,19 @@ int Context::initialize (int argc, const char** argv) try { - // Assume default .taskrc and .task locations. - assumeLocations (); + //////////////////////////////////////////////////////////////////////////// + // + // [1] Load the correct config file. + // - Default to ~/.taskrc (ctor). + // - Allow command line override rc: + // - Allow $TASKRC override. + // - Load resultant file. + // - Apply command line overrides to the config. + // + //////////////////////////////////////////////////////////////////////////// - // The CLI parser needs all the help it can get. - setupEntities (); + CLI::getOverride (argc, argv, home_dir, rc_file); - // Scan command line for 'rc:' only. - cli.initialize (argc, argv); // task arg0 arg1 ... - cli.getOverride (home_dir, rc_file); // <-- - - // TASKRC environment variable overrides the command line. char* override = getenv ("TASKRC"); if (override) { @@ -145,21 +147,22 @@ int Context::initialize (int argc, const char** argv) header (format (STRING_CONTEXT_RC_OVERRIDE, rc_file._data)); } - // Dump any existing values and load rc file. config.clear (); config.load (rc_file); - loadAliases (); + CLI::applyOverrides (argc, argv); - // These are useful for parsing. - Lexer::dateFormat = config.get ("dateformat"); - Variant::dateFormat = config.get ("dateformat"); - Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); - Variant::searchUsingRegex = config.getBoolean ("regex"); + //////////////////////////////////////////////////////////////////////////// + // + // [2] Locate the data directory. + // - Default to ~/.task (ctor). + // - Allow command line override rc.data.location: + // - Allow $TASKDATA override. + // - Inform TDB2 where to find data. + // - Create the rc_file and data_dir, if necessary. + // + //////////////////////////////////////////////////////////////////////////// - // 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. - cli.getDataLocation (data_dir); // <-- rc.data.location= + CLI::getDataLocation (argc, argv, data_dir); override = getenv ("TASKDATA"); if (override) @@ -169,51 +172,81 @@ int Context::initialize (int argc, const char** argv) header (format (STRING_CONTEXT_DATA_OVERRIDE, data_dir._data)); } - // Create missing config file and data directory, if necessary. - cli.applyOverrides (); - - // Setting the debug switch has ripple effects. - propagateDebug (); - - // These may have changed, so reapply. - Lexer::dateFormat = config.get ("dateformat"); - Variant::dateFormat = config.get ("dateformat"); - Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); - Variant::searchUsingRegex = config.getBoolean ("regex"); - + tdb2.set_location (data_dir); createDefaultConfig (); - // Initialize the color rules, if necessary. - if (color ()) - initializeColorRules (); + //////////////////////////////////////////////////////////////////////////// + // + // [3] Instantiate Command objects and capture entities. + // + //////////////////////////////////////////////////////////////////////////// - // Instantiate built-in command objects. Command::factory (commands); std::map ::iterator cmd; for (cmd = commands.begin (); cmd != commands.end (); ++cmd) { cli.entity ("cmd", cmd->first); + cli.entity ((cmd->second->read_only () ? "readcmd" : "writecmd"), cmd->first); if (cmd->first[0] == '_') cli.entity ("helper", cmd->first); - - cli.entity ((cmd->second->read_only () ? "readcmd" : "writecmd"), cmd->first); } - // Instantiate built-in column objects. - Column::factory (columns); + //////////////////////////////////////////////////////////////////////////// + // + // [4] Instantiate Column objects and capture entities. + // + //////////////////////////////////////////////////////////////////////////// - // Extend the fixed list of attribute names with any dynamic ones. + Column::factory (columns); std::map ::iterator col; for (col = columns.begin (); col != columns.end (); ++col) cli.entity ("attribute", col->first); - staticInitialization (); // Decouple code from Context. - cli.analyze (true, true); // Parse all elements, strict mode. + cli.entity ("pseudo", "limit"); - tdb2.set_location (data_dir); // Prepare the task database. + //////////////////////////////////////////////////////////////////////////// + // + // [5] Capture modifier and operator entities. + // + //////////////////////////////////////////////////////////////////////////// + + for (unsigned int i = 0; i < NUM_MODIFIER_NAMES; ++i) + cli.entity ("modifier", modifierNames[i]); + + std::vector operators; + Eval::getOperators (operators); + std::vector ::iterator op; + for (op = operators.begin (); op != operators.end (); ++op) + cli.entity ("operator", *op); + + //////////////////////////////////////////////////////////////////////////// + // + // [6] Complete the Context initialization. + // + //////////////////////////////////////////////////////////////////////////// + + initializeColorRules (); + staticInitialization (); + propagateDebug (); + loadAliases (); + + //////////////////////////////////////////////////////////////////////////// + // + // [7] Parse the command line. + // + //////////////////////////////////////////////////////////////////////////// + + // Scan command line for 'rc:' only. + cli.initialize (argc, argv); + cli.analyze (true, true); + + //////////////////////////////////////////////////////////////////////////// + // + // [8] Run on.launch hooks. + // + //////////////////////////////////////////////////////////////////////////// - // First opportunity to run a hook script. hooks.initialize (); } @@ -589,8 +622,10 @@ void Context::staticInitialization () Task::defaultProject = config.get ("default.project"); Task::defaultPriority = config.get ("default.priority"); Task::defaultDue = config.get ("default.due"); - Task::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); - Task::regex = config.getBoolean ("regex"); + + Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); + Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex"); + Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat"); std::map ::iterator i; for (i = columns.begin (); i != columns.end (); ++i) @@ -614,47 +649,11 @@ void Context::staticInitialization () // Tag- and project-specific coefficients. std::vector all; config.all (all); - std::vector ::iterator var; for (var = all.begin (); var != all.end (); ++var) - { if (var->substr (0, 13) == "urgency.user." || var->substr (0, 12) == "urgency.uda.") Task::coefficients[*var] = config.getReal (*var); - } - - Lexer::dateFormat = config.get ("dateformat"); - Variant::dateFormat = config.get ("dateformat"); - Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive"); -} - -//////////////////////////////////////////////////////////////////////////////// -void Context::assumeLocations () -{ - rc_file = File ("~/.taskrc"); - data_dir = Directory ("~/.task"); -} - -//////////////////////////////////////////////////////////////////////////////// -void Context::setupEntities () -{ - // Entities: Pseudo-attributes. Hard-coded. - cli.entity ("pseudo", "limit"); - - // Entities: Attributes. - for (unsigned int i = 0; i < NUM_ATTRIBUTE_NAMES; ++i) - cli.entity ("attribute", attributeNames[i]); - - // Entities: Modifiers. - for (unsigned int i = 0; i < NUM_MODIFIER_NAMES; ++i) - cli.entity ("modifier", modifierNames[i]); - - // Entities: Operators. - std::vector operators; - Eval::getOperators (operators); - std::vector ::iterator op; - for (op = operators.begin (); op != operators.end (); ++op) - cli.entity ("operator", *op); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Context.h b/src/Context.h index 0d1fd5c72..f9c608ab0 100644 --- a/src/Context.h +++ b/src/Context.h @@ -74,8 +74,6 @@ public: private: void staticInitialization (); - void assumeLocations (); - void setupEntities (); void createDefaultConfig (); void updateXtermTitle (); void updateVerbosity (); diff --git a/src/rules.cpp b/src/rules.cpp index 14ab1942c..baa6e950d 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -41,6 +41,10 @@ static Date now; //////////////////////////////////////////////////////////////////////////////// void initializeColorRules () { + // If color is not enable/supported, short circuit. + if (! context.color ()) + return; + try { gsColor.clear (); diff --git a/test/bug.819.t b/test/bug.819.t index 256bf494c..320426235 100755 --- a/test/bug.819.t +++ b/test/bug.819.t @@ -51,8 +51,8 @@ qx{../src/task rc:$rc add 'baz (qux)' 2>&1}; my $output = qx{../src/task rc:$rc ls 2>&1}; like ($output, qr/foo's bar\./, "$ut: foo's bar. --> preserved"); -like ($output, qr/foo \(bar\)/, "$ut: foo \(bar\) -- preserved"); -like ($output, qr/baz \(qux\)/, "$ut: baz \(qux\) -- preserved"); +like ($output, qr/foo \(bar\)/, "$ut: foo \(bar\) --> preserved"); +like ($output, qr/baz \(qux\)/, "$ut: baz \(qux\) --> preserved"); # Cleanup. unlink qw(pending.data completed.data undo.data backlog.data), $rc;