From a5ec1e4b279d69f59ea212c2a55ea262b74a3812 Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Thu, 9 Oct 2008 17:19:57 -0400
Subject: [PATCH 1/3] - Added support for shadow file, shadow file command -
Added support for TDB::onChange callback
---
configure.ac | 2 +-
src/TDB.cpp | 40 ++++++++--
src/TDB.h | 6 +-
src/command.cpp | 2 +-
src/report.cpp | 3 -
src/task.cpp | 191 +++++++++++++++++++++++++++++++-----------------
src/task.h | 5 +-
7 files changed, 168 insertions(+), 81 deletions(-)
diff --git a/configure.ac b/configure.ac
index 9d7bc2d33..6ebb57ac6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
-AC_INIT(task, 1.4.2, bugs@beckingham.net)
+AC_INIT(task, 1.4.3, bugs@beckingham.net)
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([src/task.cpp])
AC_CONFIG_HEADER([auto.h])
diff --git a/src/TDB.cpp b/src/TDB.cpp
index abb3812ae..3f1f763b2 100644
--- a/src/TDB.cpp
+++ b/src/TDB.cpp
@@ -206,7 +206,9 @@ bool TDB::deleteT (const T& t)
sprintf (endTime, "%u", (unsigned int) time (NULL));
it->setAttribute ("end", endTime);
- return overwritePending (all);
+ bool status = overwritePending (all);
+ dbChanged ();
+ return status;
}
return false;
@@ -230,14 +232,16 @@ bool TDB::completeT (const T& t)
sprintf (endTime, "%u", (unsigned int) time (NULL));
it->setAttribute ("end", endTime);
- return overwritePending (all);
+ bool status = overwritePending (all);
+ dbChanged ();
+ return status;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
-bool TDB::addT (const T& t) const
+bool TDB::addT (const T& t)
{
T task (t);
std::vector tags;
@@ -256,9 +260,15 @@ bool TDB::addT (const T& t) const
if (task.getStatus () == T::pending ||
task.getStatus () == T::recurring)
- return writePending (task);
+ {
+ bool status = writePending (task);
+ dbChanged ();
+ return status;
+ }
- return writeCompleted (task);
+ bool status = writeCompleted (task);
+ dbChanged ();
+ return status;
}
////////////////////////////////////////////////////////////////////////////////
@@ -283,7 +293,9 @@ bool TDB::modifyT (const T& t)
pending.push_back (*it);
}
- return overwritePending (pending);
+ bool status = overwritePending (pending);
+ dbChanged ();
+ return status;
}
////////////////////////////////////////////////////////////////////////////////
@@ -493,4 +505,20 @@ int TDB::nextId ()
}
////////////////////////////////////////////////////////////////////////////////
+void TDB::onChange (void (*callback)())
+{
+ if (callback)
+ mOnChange.push_back (callback);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Iterate over callbacks.
+void TDB::dbChanged ()
+{
+ foreach (i, mOnChange)
+ if (*i)
+ (**i) ();
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/TDB.h b/src/TDB.h
index ccdeea788..807a58335 100644
--- a/src/TDB.h
+++ b/src/TDB.h
@@ -45,25 +45,29 @@ public:
bool allCompletedT (std::vector &) const;
bool deleteT (const T&);
bool completeT (const T&);
- bool addT (const T&) const;
+ bool addT (const T&);
bool modifyT (const T&);
bool logRead (std::vector &) const;
bool logCommand (int, char**) const;
int gc ();
int nextId ();
+ void onChange (void (*)());
+
private:
bool lock (FILE*) const;
bool overwritePending (std::vector &);
bool writePending (const T&) const;
bool writeCompleted (const T&) const;
bool readLockedFile (const std::string&, std::vector &) const;
+ void dbChanged ();
private:
std::string mPendingFile;
std::string mCompletedFile;
std::string mLogFile;
int mId;
+ std::vector mOnChange;
};
#endif
diff --git a/src/command.cpp b/src/command.cpp
index c0d288598..6d0b70508 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -47,7 +47,7 @@
#endif
////////////////////////////////////////////////////////////////////////////////
-void handleAdd (const TDB& tdb, T& task, Config& conf)
+void handleAdd (TDB& tdb, T& task, Config& conf)
{
char entryTime[16];
sprintf (entryTime, "%u", (unsigned int) time (NULL));
diff --git a/src/report.cpp b/src/report.cpp
index c82abeaac..c9f11ad75 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -659,9 +659,6 @@ void handleInfo (TDB& tdb, T& task, Config& conf)
if (table.rowCount ())
std::cout << optionalBlankLine (conf)
<< table.render ()
- << optionalBlankLine (conf)
- << table.rowCount ()
- << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl;
else
std::cout << "No matches." << std::endl;
diff --git a/src/task.cpp b/src/task.cpp
index 29951dee3..11f9a6524 100644
--- a/src/task.cpp
+++ b/src/task.cpp
@@ -46,6 +46,11 @@
#include
#endif
+////////////////////////////////////////////////////////////////////////////////
+// Globals for exclusive use by callback function.
+static TDB* gTdb = NULL;
+static Config* gConf = NULL;
+
////////////////////////////////////////////////////////////////////////////////
static void shortUsage (Config& conf)
{
@@ -284,6 +289,7 @@ int main (int argc, char** argv)
// Load the config file from the home directory. If the file cannot be
// found, offer to create a sample one.
Config conf;
+ gConf = &conf;
loadConfFile (argc, argv, conf);
// When redirecting output to a file, do not use color, curses.
@@ -294,63 +300,17 @@ int main (int argc, char** argv)
}
TDB tdb;
+ gTdb = &tdb;
tdb.dataDirectory (expandPath (conf.get ("data.location")));
// Log commands, if desired.
if (conf.get ("command.logging") == "on")
tdb.logCommand (argc, argv);
- // If argc == 1 and the default.command configuration variable is set,
- // then use that, otherwise stick with argc/argv.
- std::vector args;
- std::string defaultCommand = conf.get ("default.command");
- if (argc == 1 && defaultCommand != "")
- {
- // Stuff the command line.
- split (args, defaultCommand, ' ');
- std::cout << "[task " << defaultCommand << "]" << std::endl;
- }
- else
- {
- // Parse the command line.
- for (int i = 1; i < argc; ++i)
- args.push_back (argv[i]);
- }
+ // Set up TDB callback.
+ tdb.onChange (&onChangeCallback);
- std::string command;
- T task;
- parse (args, command, task, conf);
-
- if (command == "add") handleAdd (tdb, task, conf);
- else if (command == "projects") handleProjects (tdb, task, conf);
- else if (command == "tags") handleTags (tdb, task, conf);
- else if (command == "list") handleList (tdb, task, conf);
- else if (command == "info") handleInfo (tdb, task, conf);
- else if (command == "undelete") handleUndelete (tdb, task, conf);
- else if (command == "long") handleLongList (tdb, task, conf);
- else if (command == "ls") handleSmallList (tdb, task, conf);
- else if (command == "colors") handleColor ( conf);
- else if (command == "completed") handleCompleted (tdb, task, conf);
- else if (command == "delete") handleDelete (tdb, task, conf);
- else if (command == "start") handleStart (tdb, task, conf);
- else if (command == "done") handleDone (tdb, task, conf);
- else if (command == "undo") handleUndo (tdb, task, conf);
- else if (command == "export") handleExport (tdb, task, conf);
- else if (command == "version") handleVersion ( conf);
- else if (command == "summary") handleReportSummary (tdb, task, conf);
- else if (command == "next") handleReportNext (tdb, task, conf);
- else if (command == "history") handleReportHistory (tdb, task, conf);
- else if (command == "ghistory") handleReportGHistory (tdb, task, conf);
- else if (command == "calendar") handleReportCalendar (tdb, task, conf);
- else if (command == "active") handleReportActive (tdb, task, conf);
- else if (command == "overdue") handleReportOverdue (tdb, task, conf);
- else if (command == "oldest") handleReportOldest (tdb, task, conf);
- else if (command == "newest") handleReportNewest (tdb, task, conf);
- else if (command == "stats") handleReportStats (tdb, task, conf);
- else if (command == "usage") handleReportUsage (tdb, task, conf);
- else if (command == "" && task.getId ()) handleModify (tdb, task, conf);
- else if (command == "help") longUsage (conf);
- else shortUsage (conf);
+ runTaskCommand (argc, argv, tdb, conf);
}
catch (std::string& error)
@@ -400,12 +360,18 @@ int getDueState (const std::string& due)
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
- Date now;
- if (dt < now)
+ // rightNow is the current date + time.
+ Date rightNow;
+
+ // By performing this conversion, today is set up as the same date, but
+ // midnight.
+ Date today (rightNow.month (), rightNow.day (), rightNow.year ());
+
+ if (dt < today)
return 2;
- Date nextweek = now + 7 * 86400;
+ Date nextweek = today + 7 * 86400;
if (dt < nextweek)
return 1;
}
@@ -553,7 +519,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -572,7 +537,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -588,7 +552,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -607,7 +570,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -623,7 +585,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -639,7 +600,6 @@ Date getNextRecurrence (Date& current, std::string& period)
while (! Date::valid (m, d, y))
--d;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -648,7 +608,6 @@ Date getNextRecurrence (Date& current, std::string& period)
{
y += 2;
-// std::cout << "# next " << current.toString () << " + " << period << " = " << m << "/" << d << "/" << y << std::endl;
return Date (m, d, y);
}
@@ -666,7 +625,6 @@ void updateRecurrenceMask (
T& task)
{
std::string parent = task.getAttribute ("parent");
-// std::cout << "# updateRecurrenceMask of " << parent << std::endl;
if (parent != "")
{
std::vector ::iterator it;
@@ -674,11 +632,8 @@ void updateRecurrenceMask (
{
if (it->getUUID () == parent)
{
-// std::cout << "# located parent task" << std::endl;
unsigned int index = atoi (task.getAttribute ("imask").c_str ());
-// std::cout << "# child imask=" << index << std::endl;
std::string mask = it->getAttribute ("mask");
-// std::cout << "# parent mask=" << mask << std::endl;
if (mask.length () > index)
{
mask[index] = (task.getStatus () == T::pending) ? '-'
@@ -686,15 +641,11 @@ void updateRecurrenceMask (
: (task.getStatus () == T::deleted) ? 'X'
: '?';
-// std::cout << "# setting parent mask to=" << mask << std::endl;
it->setAttribute ("mask", mask);
-// std::cout << "# tdb.modifyT (parent)" << std::endl;
tdb.modifyT (*it);
}
else
{
-// std::cout << "# mask of insufficient length" << std::endl;
-// std::cout << "# should never occur" << std::endl;
std::string mask;
for (unsigned int i = 0; i < index; ++i)
mask += "?";
@@ -712,3 +663,107 @@ void updateRecurrenceMask (
}
////////////////////////////////////////////////////////////////////////////////
+// Using gTdb and gConf, generate a report.
+void onChangeCallback ()
+{
+ if (gConf && gTdb)
+ {
+ gConf->set ("curses", "off");
+ gConf->set ("color", "off");
+
+ // Determine if shadow file is enabled.
+ std::string shadowFile = expandPath (gConf->get ("shadow.file"));
+ if (shadowFile != "")
+ {
+ std::string command = gConf->get ("shadow.command", "list");
+ int width = gConf->get ("shadow.width", 80);
+
+ // Run report.
+ try
+ {
+ std::vector args;
+ split (args, command, ' ');
+ runTaskCommand (args, *gTdb, *gConf);
+ }
+
+ catch (std::string& error)
+ {
+ std::cout << error << std::endl;
+ }
+
+ catch (...)
+ {
+ std::cout << "Unknown error." << std::endl;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void runTaskCommand (
+ int argc,
+ char** argv,
+ TDB& tdb,
+ Config& conf)
+{
+ std::vector args;
+ for (int i = 1; i < argc; ++i)
+ args.push_back (argv[i]);
+
+ runTaskCommand (args, tdb, conf);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void runTaskCommand (
+ std::vector & args,
+ TDB& tdb,
+ Config& conf)
+{
+ // If argc == 1 and the default.command configuration variable is set,
+ // then use that, otherwise stick with argc/argv.
+ std::string defaultCommand = conf.get ("default.command");
+ if (args.size () == 0 && defaultCommand != "")
+ {
+ // Stuff the command line.
+ args.clear ();
+ split (args, defaultCommand, ' ');
+ std::cout << "[task " << defaultCommand << "]" << std::endl;
+ }
+
+ std::string command;
+ T task;
+ parse (args, command, task, conf);
+
+ if (command == "add") handleAdd (tdb, task, conf);
+ else if (command == "projects") handleProjects (tdb, task, conf);
+ else if (command == "tags") handleTags (tdb, task, conf);
+ else if (command == "list") handleList (tdb, task, conf);
+ else if (command == "info") handleInfo (tdb, task, conf);
+ else if (command == "undelete") handleUndelete (tdb, task, conf);
+ else if (command == "long") handleLongList (tdb, task, conf);
+ else if (command == "ls") handleSmallList (tdb, task, conf);
+ else if (command == "colors") handleColor ( conf);
+ else if (command == "completed") handleCompleted (tdb, task, conf);
+ else if (command == "delete") handleDelete (tdb, task, conf);
+ else if (command == "start") handleStart (tdb, task, conf);
+ else if (command == "done") handleDone (tdb, task, conf);
+ else if (command == "undo") handleUndo (tdb, task, conf);
+ else if (command == "export") handleExport (tdb, task, conf);
+ else if (command == "version") handleVersion ( conf);
+ else if (command == "summary") handleReportSummary (tdb, task, conf);
+ else if (command == "next") handleReportNext (tdb, task, conf);
+ else if (command == "history") handleReportHistory (tdb, task, conf);
+ else if (command == "ghistory") handleReportGHistory (tdb, task, conf);
+ else if (command == "calendar") handleReportCalendar (tdb, task, conf);
+ else if (command == "active") handleReportActive (tdb, task, conf);
+ else if (command == "overdue") handleReportOverdue (tdb, task, conf);
+ else if (command == "oldest") handleReportOldest (tdb, task, conf);
+ else if (command == "newest") handleReportNewest (tdb, task, conf);
+ else if (command == "stats") handleReportStats (tdb, task, conf);
+ else if (command == "usage") handleReportUsage (tdb, task, conf);
+ else if (command == "" && task.getId ()) handleModify (tdb, task, conf);
+ else if (command == "help") longUsage (conf);
+ else shortUsage (conf);
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/task.h b/src/task.h
index 7b732f423..d7406e513 100644
--- a/src/task.h
+++ b/src/task.h
@@ -66,9 +66,12 @@ void handleRecurrence (TDB&, std::vector &);
bool generateDueDates (T&, std::vector &);
Date getNextRecurrence (Date&, std::string&);
void updateRecurrenceMask (TDB&, std::vector &, T&);
+void onChangeCallback ();
+void runTaskCommand (int, char**, TDB&, Config&);
+void runTaskCommand (std::vector &, TDB&, Config&);
// command.cpp
-void handleAdd (const TDB&, T&, Config&);
+void handleAdd (TDB&, T&, Config&);
void handleProjects (TDB&, T&, Config&);
void handleTags (TDB&, T&, Config&);
void handleUndelete (TDB&, T&, Config&);
From 3ed126975375f62b9777bcf895ab9565380881df Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Thu, 9 Oct 2008 18:24:35 -0400
Subject: [PATCH 2/3] - Added support for plain text shadow files.
---
AUTHORS | 1 +
ChangeLog | 11 ++++++++--
NEWS | 2 +-
TUTORIAL | 2 +-
html/advanced.html | 5 +++++
html/task.html | 37 +++++++++------------------------
html/versions.html | 41 ++++++++++++++++++++++++++++++++++++-
src/task.cpp | 51 ++++++++++++++++++++++++++++------------------
8 files changed, 98 insertions(+), 52 deletions(-)
diff --git a/AUTHORS b/AUTHORS
index d045cad9b..027de8b31 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -17,4 +17,5 @@ With thanks to:
Stas Antons
Vincent Fleuranceau
T. Charles Yun
+ ArchiMark
diff --git a/ChangeLog b/ChangeLog
index 756fddc64..92da6b0b0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,15 @@ represents a feature release, and the Z represents a patch.
------ current release ---------------------------
+1.4.3 (10/9/2008)
+ + Fixed misleading task count at bottom on "info" report.
+ + Added support for a shadow file that contains a plain text task report,
+ with the "shadow.file" and "shadow.command" configuration variables.
+ The shadow file is automatically updated whenever the task database
+ changes. Useful for integrating with "Samurize".
+
+------ old releases ------------------------------
+
1.4.2 (9/18/2008)
+ "task undo" can now retract a "task done" command, provided no reports
have been run (and therefore TDB::gc run)
@@ -30,8 +39,6 @@ represents a feature release, and the Z represents a patch.
+ Bug: Source now properly includes in order to build clean
using gcc 4.3 (thanks to H. İbrahim Güngör)
------- old releases ------------------------------
-
1.4.1 (7/18/2008)
+ Bug: Descriptions can not be altered with "task 123 New description"
+ Tweak: For "task calendar" month names are now centered over the month
diff --git a/NEWS b/NEWS
index 18b4251e8..e936a4cb7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Welcome to Task 1.4.0.
+Welcome to Task 1.4.3.
Task has been built and tested on the following configurations:
diff --git a/TUTORIAL b/TUTORIAL
index 7f635f4c2..e389b623a 100644
--- a/TUTORIAL
+++ b/TUTORIAL
@@ -1,5 +1,5 @@
-This TUTORIAL file has been deprecated. It is superceded by a richer and more
+This TUTORIAL file has been deprecated. It is superseded by a richer and more
extensive online version that can be found at:
http://www.beckingham.net/task.html
diff --git a/html/advanced.html b/html/advanced.html
index 126bbf3a2..6c1baebfa 100644
--- a/html/advanced.html
+++ b/html/advanced.html
@@ -319,6 +319,7 @@ ID Project Pri Description
in the .taskrc file, then task will record every command that is
run. When this command is run, task will display a count of how
many times each command was used.
+
@@ -326,6 +327,10 @@ ID Project Pri Description
actually used.
+
+ This command is deprecated, and will be removed in task 1.5.0
+
+
% task version
This can be used to show the version number of task, and to display
diff --git a/html/task.html b/html/task.html
index 5218402bf..88a6048fd 100644
--- a/html/task.html
+++ b/html/task.html
@@ -1,7 +1,7 @@
"task undo" can now retract a "task done" command, provided no
- reports have been run.
-
Task now correctly sorts on entire strings, instead of just the
- first character (thanks to Andy Lester).
-
Task now uses dashes (-----) to underline column headings when
- color is disabled (thanks to Vincent Fleuranceau).
-
Task now allows mixed case attribute names (pri:, PRI:, Pri: ...)
- and commands (add, ADD, Add ...) (thanks to Vincent Fleuranceau).
-
Task now supports a default project and priority for new tasks, via
- the new "default.project" and "default.priority" configuration variables
- (thanks to Vincent Fleuranceau).
-
Task supports improved word-wrapping to the terminal width.
-
Task now supports "default.command" configuration variable (for example
- it could contain "list due:tomorrow") which is the command that is run
- whenever task is invoked with no arguments.
-
Task supports modifying the existing description of a task, with the
- following syntax: task <id> "new description ...".
-
Fixed bug so that relative dates in filters (task list due:eom,
- task list due:tomorrow, task list due:23rd ...) are now properly
- supported.
-
Fixed bug so that source now properly includes <string.h> in
- order to build clean using gcc 4.3 (thanks to H. İbrahim Güngör)
+
Fixed misleading task count at bottom of "info" report.
+
Added support for a shadow file that contains a plain text task report,
+ with the "shadow.file" and "shadow.command" configuration variables.
+ The shadow file is automatically updated whenever the task database
+ changes. Useful for integrating with "Samurize".
"task undo" can now retract a "task done" command, provided no
+ reports have been run.
+
Task now correctly sorts on entire strings, instead of just the
+ first character (thanks to Andy Lester).
+
Task now uses dashes (-----) to underline column headings when
+ color is disabled (thanks to Vincent Fleuranceau).
+
Task now allows mixed case attribute names (pri:, PRI:, Pri: ...)
+ and commands (add, ADD, Add ...) (thanks to Vincent Fleuranceau).
+
Task now supports a default project and priority for new tasks, via
+ the new "default.project" and "default.priority" configuration variables
+ (thanks to Vincent Fleuranceau).
+
Task supports improved word-wrapping to the terminal width.
+
Task now supports "default.command" configuration variable (for example
+ it could contain "list due:tomorrow") which is the command that is run
+ whenever task is invoked with no arguments.
+
Task supports modifying the existing description of a task, with the
+ following syntax: task <id> "new description ...".
+
Fixed bug so that relative dates in filters (task list due:eom,
+ task list due:tomorrow, task list due:23rd ...) are now properly
+ supported.
+
Fixed bug so that source now properly includes <string.h> in
+ order to build clean using gcc 4.3 (thanks to H. İbrahim Güngör)
+
+ If specified, designates a file path that will be autoamtically
+ written to by task, whenever the task database changes. In other
+ words, it is automatically kept up to date.
+
+
+
+ The shadow.command configuration variable is used to determine
+ which report is written to the shadow file. There is no color
+ used in the shadow file.
+
+
+
+ This feature can be useful in maintaining a current file for
+ use by the "Samurize" program.
+
+
+
+
shadow.command
+
+
+ This is the command that is run to maintain the shadow file,
+ determined by the shadow.file configuration variable. The
+ format is identical to that of default.command - please see
+ the documentation for default.command.
+
+
+
+ If this command is not specified, task will use the default.command
+ value instead. If that is not specified, the command "list" is used.
+