diff --git a/ChangeLog b/ChangeLog index 08992f704..0e5243485 100644 --- a/ChangeLog +++ b/ChangeLog @@ -17,6 +17,7 @@ "description.contains:th". + The 'version' command now complains about use of deprecated color names and duplicate entries. + + Task now supports nested .taskrc files using the "include /path" directive. + Fixed bug that showed a calendar for the year 2037 when 'task calendar due' was run, and there are no tasks with due dates. + Fixed bug #316 which caused the timesheet report to display an oddly sorted diff --git a/src/Config.cpp b/src/Config.cpp index 1ee2a1f86..2b799ed95 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -58,8 +58,16 @@ Config::Config (const std::string& file) // Read the Configuration file and populate the *this map. The file format is // simply lines with name=value pairs. Whitespace between name, = and value is // not tolerated, but blank lines and comments starting with # are allowed. -bool Config::load (const std::string& file) +// +// Nested files are now supported, with the following construct: +// include /absolute/path/to/file +// +bool Config::load (const std::string& file, int nest /* = 1 */) { + if (nest > 10) + throw std::string ("Configuration file nested to more than 10 levels deep" + " - this has to be a mistake."); + std::ifstream in; in.open (file.c_str (), std::ifstream::in); if (in.good ()) @@ -82,9 +90,29 @@ bool Config::load (const std::string& file) { std::string key = trim (line.substr (0, equal), " \t"); // no i18n std::string value = trim (line.substr (equal+1, line.length () - equal), " \t"); // no i18n + (*this)[key] = value; sequence.push_back (key); } + else + { + std::string::size_type include = line.find ("include"); // no i18n. + if (include != std::string::npos) + { + std::string included = expandPath ( trim ( line.substr (include + 7, std::string::npos), " \t")); + if (isAbsolutePath (included)) + { + if (!access (included.c_str (), F_OK | R_OK)) + this->load (included, nest + 1); + else + throw std::string ("Could not read include file '") + included + "'"; + } + else + throw std::string ("Can only include files with absolute paths, not '") + included + "'"; + } + else + throw std::string ("Malformed entry in ") + file + ": '" + line + "'"; + } } } diff --git a/src/Config.h b/src/Config.h index 8eda3fbb3..32296bf00 100644 --- a/src/Config.h +++ b/src/Config.h @@ -40,7 +40,7 @@ public: Config (const Config&); Config& operator= (const Config&); - bool load (const std::string&); + bool load (const std::string&, int nest = 1); void createDefaultRC (const std::string&, const std::string&); void createDefaultData (const std::string&); void setDefaults (); diff --git a/src/tests/bug.bulk.t b/src/tests/bug.bulk.t index 5c8d9602c..899221712 100755 --- a/src/tests/bug.bulk.t +++ b/src/tests/bug.bulk.t @@ -52,7 +52,7 @@ qx{../task rc:bulk.rc add t6 due:saturday}; my $output = qx{echo "quit"|../task rc:bulk.rc pro:p1 pri:M 4 5 6}; like ($output, qr/Modified 0 tasks/, '"quit" prevents any further modifications'); -my $output = qx{echo "all"|../task rc:bulk.rc pro:p1 pri:M 4 5 6}; +$output = qx{echo "All"|../task rc:bulk.rc pro:p1 pri:M 4 5 6}; unlike ($output, qr/Task 4 "t4"\n - No changes were made/, 'Task 4 modified'); unlike ($output, qr/Task 5 "t5"\n - No changes were made/, 'Task 5 modified'); unlike ($output, qr/Task 6 "t6"\n - No changes were made/, 'Task 6 modified'); diff --git a/src/tests/config.t.cpp b/src/tests/config.t.cpp index 684a79892..c2e487514 100644 --- a/src/tests/config.t.cpp +++ b/src/tests/config.t.cpp @@ -105,6 +105,11 @@ int main (int argc, char** argv) // 22 default report setting created in Config::Config. t.ok (all.size () >= 8, "Config::all"); + // TODO Test includes + // TODO Test included nesting limit + // TODO Test included absolute vs relative + // TODO Test included missing file + return 0; } diff --git a/src/tests/util.t.cpp b/src/tests/util.t.cpp index 3cb36a525..8f8404317 100644 --- a/src/tests/util.t.cpp +++ b/src/tests/util.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (67); + UnitTest t (74); // TODO bool confirm (const std::string&); // TODO int confirm3 (const std::string&); @@ -113,6 +113,15 @@ int main (int argc, char** argv) // std::string expandPath (const std::string&); t.ok (expandPath ("foo") == "foo", "expandPath nop"); t.ok (expandPath ("~/") != "~/", "expandPath ~/"); + t.ok (expandPath ("~") != "~", "expandPath ~"); + + // bool isAbsolutePath (const std::string&); + t.notok (isAbsolutePath ("."), "isAbsolutePath ."); + t.notok (isAbsolutePath ("~"), "isAbsolutePath ~"); + t.ok (isAbsolutePath (expandPath ("~")), "isAbsolutePath (expandPath ~)"); + t.ok (isAbsolutePath (expandPath ("~/")), "isAbsolutePath (expandPath ~/)"); + t.ok (isAbsolutePath ("/"), "isAbsolutePath /"); + t.ok (isAbsolutePath ("/tmp"), "isAbsolutePath /tmp"); // TODO bool slurp (const std::string&, std::vector &, bool trimLines = false); // TODO bool slurp (const std::string&, std::string&, bool trimLines = false); diff --git a/src/util.cpp b/src/util.cpp index 9ac5e0bd8..ab4826042 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -355,6 +355,13 @@ std::string expandPath (const std::string& in) copy.replace (tilde, 1, pw->pw_dir); } else if ((tilde = copy.find ("~")) != std::string::npos) + { + struct passwd* pw = getpwuid (getuid ()); + std::string home = pw->pw_dir; + home += "/"; + copy.replace (tilde, 1, home); + } + else if ((tilde = copy.find ("~")) != std::string::npos) { std::string::size_type slash; if ((slash = copy.find ("/", tilde)) != std::string::npos) @@ -369,6 +376,15 @@ std::string expandPath (const std::string& in) return copy; } +//////////////////////////////////////////////////////////////////////////////// +bool isAbsolutePath (const std::string& in) +{ + if (in.length () && in[0] == '/') + return true; + + return false; +} + //////////////////////////////////////////////////////////////////////////////// // On Solaris no flock function exists. #ifdef SOLARIS diff --git a/src/util.h b/src/util.h index 0a2102abf..d4e1d2337 100644 --- a/src/util.h +++ b/src/util.h @@ -61,6 +61,7 @@ std::string formatBytes (size_t); int autoComplete (const std::string&, const std::vector&, std::vector&); const std::string uuid (); std::string expandPath (const std::string&); +bool isAbsolutePath (const std::string&); #ifdef SOLARIS #define LOCK_SH 1