From 2cae1df42f7bc72fa1150baabd1152faf2ee4d42 Mon Sep 17 00:00:00 2001
From: Paul Beckingham
Date: Sat, 1 Nov 2008 16:31:30 -0400
Subject: [PATCH] - Merged 1.4.3 to master
---
AUTHORS | 1 +
ChangeLog | 9 +
NEWS | 2 +-
TUTORIAL | 6 +
announcement.txt | 36 ----
configure.ac | 4 +-
html/30second.html | 2 +-
html/advanced.html | 20 ++-
html/color.html | 2 +-
html/config.html | 38 +++-
html/date.html | 2 +-
html/filter.html | 92 ++++++++++
html/recur.html | 2 +-
html/setup.html | 2 +-
html/shadow.html | 98 ++++++++++
html/shell.html | 2 +-
html/simple.html | 2 +-
html/task.html | 22 +--
html/usage.html | 2 +-
html/versions.html | 6 +
src/.gitignore | 1 -
src/TDB.cpp | 27 ++-
src/TDB.h | 10 +-
src/command.cpp | 277 ++++++++++++++++-------------
src/report.cpp | 433 +++++++++++++++++++++++++++++----------------
src/task.cpp | 231 +++++++++++++++++-------
src/task.h | 52 +++---
27 files changed, 950 insertions(+), 431 deletions(-)
create mode 100644 TUTORIAL
delete mode 100644 announcement.txt
create mode 100644 html/filter.html
create mode 100644 html/shadow.html
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 c9f0cdaab..c2b641218 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -8,6 +8,15 @@
------ old releases ------------------------------
+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)
diff --git a/NEWS b/NEWS
index 7698ef623..e936a4cb7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,4 @@
-Welcome to Task 1.5.0.
+Welcome to Task 1.4.3.
Task has been built and tested on the following configurations:
diff --git a/TUTORIAL b/TUTORIAL
new file mode 100644
index 000000000..e389b623a
--- /dev/null
+++ b/TUTORIAL
@@ -0,0 +1,6 @@
+
+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/announcement.txt b/announcement.txt
deleted file mode 100644
index 037106408..000000000
--- a/announcement.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Some considerable time ago - longer than I had hoped - I demonstrated an
-alternate implementation of the todo script, called task, in the form of a
-YouTube movie:
-
- http://www.youtube.com/watch?v=l68LCl6BYvs
-
-A lot has happened since then, and the task program has been slowly improving
-thanks to feedback from some early testers, and continuous use by me. Today,
-I have uploaded a new movie:
-
- http://www.youtube.com/watch?v=D2Kn4DMOVSw
-
-This movie includes most of the changes and improvements to task, but behind
-the scenes are the biggest changes. There was a rewrite of the underlying
-storage mechanism yielding a clean API for the front end, and the code was
-reviewed for portability and converted to use GNU autoconf/automake.
-
-Task has been released under GPL, and so far has been tested on:
-
- Max OS X 10.4 (Tiger)
- Max OS X 10.5 (Leopard)
- Fedora 8
- Fedora 9
- Ubuntu 8 (Hardy Heron)
- Solaris 10
-
-Task has been making me more organized and productive for some time now.
-Perhaps some of you might find it useful, and I welcome feedback of all kinds.
-
-You can find the task source code at:
-
- http://www.beckingham.net/task-1.0.0.tar.gz
-
-Thank you.
-Paul Beckingham
-
diff --git a/configure.ac b/configure.ac
index e89a1aa53..0c1c977b3 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.5.0, 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])
@@ -19,7 +19,7 @@ AC_CHECK_LIB(ncurses,endwin)
# Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stdlib.h sys/file.h sys/stat.h sys/time.h unistd.h])
-AC_CHECK_HEADERS([string vector map])
+AC_CHECK_HEADERS([sstream string vector map])
# Checks for typedefs, structures, and compiler characteristics.
AC_HEADER_STDBOOL
diff --git a/html/30second.html b/html/30second.html
index f1450c650..227b0bbf2 100644
--- a/html/30second.html
+++ b/html/30second.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ 30-Second Tutorial
diff --git a/html/advanced.html b/html/advanced.html
index 07f293754..97a344cdb 100644
--- a/html/advanced.html
+++ b/html/advanced.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Advanced Usage
@@ -313,6 +313,24 @@ ID Project Pri Description
This command displays all the colors that task supports.
+ % task usage
+
+ If logging has been enabled by the "command.logging=on" directive
+ 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.
+
+
+
+
+ This command is for the purpose of seeing whether command are
+ 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/color.html b/html/color.html
index 72c70772b..f88357e0c 100644
--- a/html/color.html
+++ b/html/color.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Color Usage
diff --git a/html/config.html b/html/config.html
index 37cd127b8..144df2498 100644
--- a/html/config.html
+++ b/html/config.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Task Configuration
@@ -279,6 +279,42 @@ ID Project Pri Description
preceding "task" program name.
+
+ shadow.file
+
+
+ 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.
+
+
+
diff --git a/html/date.html b/html/date.html
index 0d4c8b711..f5dcee84a 100644
--- a/html/date.html
+++ b/html/date.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Date Handling
diff --git a/html/filter.html b/html/filter.html
new file mode 100644
index 000000000..af801375b
--- /dev/null
+++ b/html/filter.html
@@ -0,0 +1,92 @@
+
+
+
+ Task Filters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Task Filters
+
+
+
+
+
+
+ Copyright 2006-2008, P. Beckingham. All rights reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/html/recur.html b/html/recur.html
index ca7e9d626..2a72f76b6 100644
--- a/html/recur.html
+++ b/html/recur.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Recurring Tasks
diff --git a/html/setup.html b/html/setup.html
index 209260e28..68ecbf8b6 100644
--- a/html/setup.html
+++ b/html/setup.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Task Setup
diff --git a/html/shadow.html b/html/shadow.html
new file mode 100644
index 000000000..c1d0f5926
--- /dev/null
+++ b/html/shadow.html
@@ -0,0 +1,98 @@
+
+
+
+ Task Shadow Files
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Task Shadow Files
+
+
+
+
+
+
+ Copyright 2006-2008, P. Beckingham. All rights reserved.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/html/shell.html b/html/shell.html
index 147ea2aa4..6bade3cc6 100644
--- a/html/shell.html
+++ b/html/shell.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Interacting with the Shell
diff --git a/html/simple.html b/html/simple.html
index cfa72564f..85ca5782b 100644
--- a/html/simple.html
+++ b/html/simple.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Simple Usage
diff --git a/html/task.html b/html/task.html
index aeafd3ecf..0c0236789 100644
--- a/html/task.html
+++ b/html/task.html
@@ -1,7 +1,7 @@
- Task 1.5.0
+ Latest Release
@@ -54,8 +54,8 @@
Date Handling
Troubleshooting
Old Versions
-
- Filters (coming soon)
+ Filters (coming soon)
+ Shadow Files (coming soon)
@@ -75,26 +75,28 @@
- New in version 1.5.0 (?)
+ New in version 1.4.3 (9/18/2008)
- Removed deprecated TUTORIAL file.
- Removed "usage" command, and support for "command.logging" configuration
- variable.
+ 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".
diff --git a/html/usage.html b/html/usage.html
index 97b98f5df..92b7b1d88 100644
--- a/html/usage.html
+++ b/html/usage.html
@@ -1,7 +1,7 @@
- Task 1.4.1
+ Task Usage
diff --git a/html/versions.html b/html/versions.html
index 190a7251a..409c74fa4 100644
--- a/html/versions.html
+++ b/html/versions.html
@@ -38,6 +38,12 @@
New in version 1.4.2 (9/18/2008)
task-1.4.2.tar.gz
+
+ Mac OS X 10.5 (Leopard) Intel-only:
+ task-1.4.2.pkg
+
+ Debian package: task_1.4.2-1_i386.deb
+ (Thanks to Richard Querin )
diff --git a/src/.gitignore b/src/.gitignore
index 2327b8b98..5761abcfd 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,2 +1 @@
-./Makefile
*.o
diff --git a/src/TDB.cpp b/src/TDB.cpp
index dcced05af..deae68184 100644
--- a/src/TDB.cpp
+++ b/src/TDB.cpp
@@ -235,7 +235,7 @@ bool TDB::completeT (const T& t)
}
////////////////////////////////////////////////////////////////////////////////
-bool TDB::addT (const T& t) const
+bool TDB::addT (const T& t)
{
T task (t);
std::vector tags;
@@ -254,7 +254,9 @@ bool TDB::addT (const T& t) const
if (task.getStatus () == T::pending ||
task.getStatus () == T::recurring)
+ {
return writePending (task);
+ }
return writeCompleted (task);
}
@@ -312,6 +314,7 @@ bool TDB::overwritePending (std::vector & all)
fputs (it->compose ().c_str (), out);
fclose (out);
+ dbChanged ();
return true;
}
@@ -319,7 +322,7 @@ bool TDB::overwritePending (std::vector & all)
}
////////////////////////////////////////////////////////////////////////////////
-bool TDB::writePending (const T& t) const
+bool TDB::writePending (const T& t)
{
// Write a single task to the pending file
FILE* out;
@@ -334,6 +337,7 @@ bool TDB::writePending (const T& t) const
fputs (t.compose ().c_str (), out);
fclose (out);
+ dbChanged ();
return true;
}
@@ -341,7 +345,7 @@ bool TDB::writePending (const T& t) const
}
////////////////////////////////////////////////////////////////////////////////
-bool TDB::writeCompleted (const T& t) const
+bool TDB::writeCompleted (const T& t)
{
// Write a single task to the pending file
FILE* out;
@@ -356,6 +360,7 @@ bool TDB::writeCompleted (const T& t) const
fputs (t.compose ().c_str (), out);
fclose (out);
+ dbChanged ();
return true;
}
@@ -439,4 +444,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 9ceb9b3d8..d335e909b 100644
--- a/src/TDB.h
+++ b/src/TDB.h
@@ -45,23 +45,27 @@ 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;
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 writePending (const T&);
+ bool writeCompleted (const T&);
bool readLockedFile (const std::string&, std::vector &) const;
+ void dbChanged ();
private:
std::string mPendingFile;
std::string mCompletedFile;
int mId;
+ std::vector mOnChange;
};
#endif
diff --git a/src/command.cpp b/src/command.cpp
index c0d288598..b696a8f93 100644
--- a/src/command.cpp
+++ b/src/command.cpp
@@ -26,6 +26,7 @@
////////////////////////////////////////////////////////////////////////////////
#include
#include
+#include
#include
#include
#include
@@ -47,7 +48,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));
@@ -67,7 +68,6 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
task.setAttribute ("mask", "");
}
-/**/
// Override with default.project, if not specified.
if (task.getAttribute ("project") == "")
task.setAttribute ("project", conf.get ("default.project", ""));
@@ -79,8 +79,8 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
if (validPriority (defaultPriority))
task.setAttribute ("priority", defaultPriority);
}
-/**/
+ // Disallow blank descriptions.
if (task.getDescription () == "")
throw std::string ("Cannot add a blank task.");
@@ -89,8 +89,10 @@ void handleAdd (const TDB& tdb, T& task, Config& conf)
}
////////////////////////////////////////////////////////////////////////////////
-void handleProjects (TDB& tdb, T& task, Config& conf)
+std::string handleProjects (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Get all the tasks, including deleted ones.
std::vector tasks;
tdb.pendingT (tasks);
@@ -127,21 +129,25 @@ void handleProjects (TDB& tdb, T& task, Config& conf)
table.addCell (row, 1, i->second);
}
- std::cout << optionalBlankLine (conf)
- << table.render ()
- << optionalBlankLine (conf)
- << unique.size ()
- << (unique.size () == 1 ? " project" : " projects")
- << std::endl;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << unique.size ()
+ << (unique.size () == 1 ? " project" : " projects")
+ << std::endl;
}
else
- std::cout << "No projects."
- << std::endl;
+ out << "No projects."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleTags (TDB& tdb, T& task, Config& conf)
+std::string handleTags (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Get all the tasks.
std::vector tasks;
tdb.pendingT (tasks);
@@ -166,20 +172,23 @@ void handleTags (TDB& tdb, T& task, Config& conf)
std::cout << i->first << std::endl;
if (unique.size ())
- std::cout << optionalBlankLine (conf)
- << unique.size ()
- << (unique.size () == 1 ? " tag" : " tags")
- << std::endl;
+ out << optionalBlankLine (conf)
+ << unique.size ()
+ << (unique.size () == 1 ? " tag" : " tags")
+ << std::endl;
else
- std::cout << "No tags."
- << std::endl;
+ out << "No tags."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// If a task is deleted, but is still in the pending file, then it may be
// undeleted simply by changing it's status.
-void handleUndelete (TDB& tdb, T& task, Config& conf)
+std::string handleUndelete (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
std::vector all;
tdb.allPendingT (all);
@@ -193,8 +202,8 @@ void handleUndelete (TDB& tdb, T& task, Config& conf)
{
if (it->getAttribute ("recur") != "")
{
- std::cout << "Task does not support 'undelete' for recurring tasks." << std::endl;
- return;
+ out << "Task does not support 'undelete' for recurring tasks." << std::endl;
+ return out.str ();
}
T restored (*it);
@@ -202,27 +211,31 @@ void handleUndelete (TDB& tdb, T& task, Config& conf)
restored.removeAttribute ("end");
tdb.modifyT (restored);
- std::cout << "Task " << id << " successfully undeleted." << std::endl;
- return;
+ out << "Task " << id << " successfully undeleted." << std::endl;
+ return out.str ();
}
else
{
- std::cout << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl;
- return;
+ out << "Task " << id << " is not deleted - therefore cannot undelete." << std::endl;
+ return out.str ();
}
}
}
- std::cout << "Task " << id
- << " not found - tasks can only be reliably undeleted if the undelete" << std::endl
- << "command is run immediately after the errant delete command." << std::endl;
+ out << "Task " << id
+ << " not found - tasks can only be reliably undeleted if the undelete" << std::endl
+ << "command is run immediately after the errant delete command." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// If a task is done, but is still in the pending file, then it may be undone
// simply by changing it's status.
-void handleUndo (TDB& tdb, T& task, Config& conf)
+std::string handleUndo (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
std::vector all;
tdb.allPendingT (all);
@@ -235,35 +248,36 @@ void handleUndo (TDB& tdb, T& task, Config& conf)
if (it->getStatus () == T::completed)
{
if (it->getAttribute ("recur") != "")
- {
- std::cout << "Task does not support 'undo' for recurring tasks." << std::endl;
- return;
- }
+ return std::string ("Task does not support 'undo' for recurring tasks.\n");
T restored (*it);
restored.setStatus (T::pending);
restored.removeAttribute ("end");
tdb.modifyT (restored);
- std::cout << "Task " << id << " successfully undone." << std::endl;
- return;
+ out << "Task " << id << " successfully undone." << std::endl;
+ return out.str ();
}
else
{
- std::cout << "Task " << id << " is not done - therefore cannot be undone." << std::endl;
- return;
+ out << "Task " << id << " is not done - therefore cannot be undone." << std::endl;
+ return out.str ();
}
}
}
- std::cout << "Task " << id
- << " not found - tasks can only be reliably undone if the undo" << std::endl
- << "command is run immediately after the errant done command." << std::endl;
+ out << "Task " << id
+ << " not found - tasks can only be reliably undone if the undo" << std::endl
+ << "command is run immediately after the errant done command." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleVersion (Config& conf)
+std::string handleVersion (Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -329,40 +343,42 @@ void handleVersion (Config& conf)
}
}
- std::cout << "Copyright (C) 2006 - 2008, P. Beckingham."
- << std::endl
- << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE)
- << " "
- << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION)
- << std::endl
- << disclaimer.render ()
- << std::endl
- << table.render ()
- << link.render ()
- << std::endl;
+ out << "Copyright (C) 2006 - 2008, P. Beckingham."
+ << std::endl
+ << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, PACKAGE) : PACKAGE)
+ << " "
+ << (conf.get ("color", true) ? Text::colorize (Text::bold, Text::nocolor, VERSION) : VERSION)
+ << std::endl
+ << disclaimer.render ()
+ << std::endl
+ << table.render ()
+ << link.render ()
+ << std::endl;
// Verify installation. This is mentioned in the documentation as the way to
// ensure everything is properly installed.
if (all.size () == 0)
- std::cout << "Configuration error: .taskrc contains no entries"
- << std::endl;
+ out << "Configuration error: .taskrc contains no entries"
+ << std::endl;
else
{
if (conf.get ("data.location") == "")
- std::cout << "Configuration error: data.location not specified in .taskrc "
- "file."
- << std::endl;
+ out << "Configuration error: data.location not specified in .taskrc "
+ "file."
+ << std::endl;
if (access (expandPath (conf.get ("data.location")).c_str (), X_OK))
- std::cout << "Configuration error: data.location contains a directory name"
- " that doesn't exist, or is unreadable."
- << std::endl;
+ out << "Configuration error: data.location contains a directory name"
+ " that doesn't exist, or is unreadable."
+ << std::endl;
}
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleDelete (TDB& tdb, T& task, Config& conf)
+std::string handleDelete (TDB& tdb, T& task, Config& conf)
{
if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?"))
{
@@ -386,7 +402,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
sibling->getUUID () == parent)
tdb.deleteT (*sibling);
- return;
+ return std::string ("");
}
else
{
@@ -394,7 +410,7 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
t->setStatus (T::deleted);
updateRecurrenceMask (tdb, all, *t);
tdb.deleteT (*t);
- return;
+ return std::string ("");
}
}
else
@@ -405,11 +421,13 @@ void handleDelete (TDB& tdb, T& task, Config& conf)
}
}
else
- std::cout << "Task not deleted." << std::endl;
+ return std::string ("Task not deleted.\n");
+
+ return std::string ("");
}
////////////////////////////////////////////////////////////////////////////////
-void handleStart (TDB& tdb, T& task, Config& conf)
+std::string handleStart (TDB& tdb, T& task, Config& conf)
{
std::vector all;
tdb.pendingT (all);
@@ -431,14 +449,19 @@ void handleStart (TDB& tdb, T& task, Config& conf)
tdb.modifyT (original);
nag (tdb, task, conf);
- return;
+ return std::string ("");
}
else
- std::cout << "Task " << task.getId () << " already started." << std::endl;
+ {
+ std::stringstream out;
+ out << "Task " << task.getId () << " already started." << std::endl;
+ return out.str ();
+ }
}
}
throw std::string ("Task not found.");
+ return std::string (""); // To satisfy gcc.
}
////////////////////////////////////////////////////////////////////////////////
@@ -597,87 +620,91 @@ void handleModify (TDB& tdb, T& task, Config& conf)
}
////////////////////////////////////////////////////////////////////////////////
-void handleColor (Config& conf)
+std::string handleColor (Config& conf)
{
+ std::stringstream out;
+
if (conf.get ("color", true))
{
- std::cout << optionalBlankLine (conf) << "Foreground" << std::endl
- << " "
- << Text::colorize (Text::bold, Text::nocolor, "bold") << " "
- << Text::colorize (Text::underline, Text::nocolor, "underline") << " "
- << Text::colorize (Text::bold_underline, Text::nocolor, "bold_underline") << std::endl
+ out << optionalBlankLine (conf) << "Foreground" << std::endl
+ << " "
+ << Text::colorize (Text::bold, Text::nocolor, "bold") << " "
+ << Text::colorize (Text::underline, Text::nocolor, "underline") << " "
+ << Text::colorize (Text::bold_underline, Text::nocolor, "bold_underline") << std::endl
- << " " << Text::colorize (Text::black, Text::nocolor, "black") << " "
- << Text::colorize (Text::bold_black, Text::nocolor, "bold_black") << " "
- << Text::colorize (Text::underline_black, Text::nocolor, "underline_black") << " "
- << Text::colorize (Text::bold_underline_black, Text::nocolor, "bold_underline_black") << std::endl
+ << " " << Text::colorize (Text::black, Text::nocolor, "black") << " "
+ << Text::colorize (Text::bold_black, Text::nocolor, "bold_black") << " "
+ << Text::colorize (Text::underline_black, Text::nocolor, "underline_black") << " "
+ << Text::colorize (Text::bold_underline_black, Text::nocolor, "bold_underline_black") << std::endl
- << " " << Text::colorize (Text::red, Text::nocolor, "red") << " "
- << Text::colorize (Text::bold_red, Text::nocolor, "bold_red") << " "
- << Text::colorize (Text::underline_red, Text::nocolor, "underline_red") << " "
- << Text::colorize (Text::bold_underline_red, Text::nocolor, "bold_underline_red") << std::endl
+ << " " << Text::colorize (Text::red, Text::nocolor, "red") << " "
+ << Text::colorize (Text::bold_red, Text::nocolor, "bold_red") << " "
+ << Text::colorize (Text::underline_red, Text::nocolor, "underline_red") << " "
+ << Text::colorize (Text::bold_underline_red, Text::nocolor, "bold_underline_red") << std::endl
- << " " << Text::colorize (Text::green, Text::nocolor, "green") << " "
- << Text::colorize (Text::bold_green, Text::nocolor, "bold_green") << " "
- << Text::colorize (Text::underline_green, Text::nocolor, "underline_green") << " "
- << Text::colorize (Text::bold_underline_green, Text::nocolor, "bold_underline_green") << std::endl
+ << " " << Text::colorize (Text::green, Text::nocolor, "green") << " "
+ << Text::colorize (Text::bold_green, Text::nocolor, "bold_green") << " "
+ << Text::colorize (Text::underline_green, Text::nocolor, "underline_green") << " "
+ << Text::colorize (Text::bold_underline_green, Text::nocolor, "bold_underline_green") << std::endl
- << " " << Text::colorize (Text::yellow, Text::nocolor, "yellow") << " "
- << Text::colorize (Text::bold_yellow, Text::nocolor, "bold_yellow") << " "
- << Text::colorize (Text::underline_yellow, Text::nocolor, "underline_yellow") << " "
- << Text::colorize (Text::bold_underline_yellow, Text::nocolor, "bold_underline_yellow") << std::endl
+ << " " << Text::colorize (Text::yellow, Text::nocolor, "yellow") << " "
+ << Text::colorize (Text::bold_yellow, Text::nocolor, "bold_yellow") << " "
+ << Text::colorize (Text::underline_yellow, Text::nocolor, "underline_yellow") << " "
+ << Text::colorize (Text::bold_underline_yellow, Text::nocolor, "bold_underline_yellow") << std::endl
- << " " << Text::colorize (Text::blue, Text::nocolor, "blue") << " "
- << Text::colorize (Text::bold_blue, Text::nocolor, "bold_blue") << " "
- << Text::colorize (Text::underline_blue, Text::nocolor, "underline_blue") << " "
- << Text::colorize (Text::bold_underline_blue, Text::nocolor, "bold_underline_blue") << std::endl
+ << " " << Text::colorize (Text::blue, Text::nocolor, "blue") << " "
+ << Text::colorize (Text::bold_blue, Text::nocolor, "bold_blue") << " "
+ << Text::colorize (Text::underline_blue, Text::nocolor, "underline_blue") << " "
+ << Text::colorize (Text::bold_underline_blue, Text::nocolor, "bold_underline_blue") << std::endl
- << " " << Text::colorize (Text::magenta, Text::nocolor, "magenta") << " "
- << Text::colorize (Text::bold_magenta, Text::nocolor, "bold_magenta") << " "
- << Text::colorize (Text::underline_magenta, Text::nocolor, "underline_magenta") << " "
- << Text::colorize (Text::bold_underline_magenta, Text::nocolor, "bold_underline_magenta") << std::endl
+ << " " << Text::colorize (Text::magenta, Text::nocolor, "magenta") << " "
+ << Text::colorize (Text::bold_magenta, Text::nocolor, "bold_magenta") << " "
+ << Text::colorize (Text::underline_magenta, Text::nocolor, "underline_magenta") << " "
+ << Text::colorize (Text::bold_underline_magenta, Text::nocolor, "bold_underline_magenta") << std::endl
- << " " << Text::colorize (Text::cyan, Text::nocolor, "cyan") << " "
- << Text::colorize (Text::bold_cyan, Text::nocolor, "bold_cyan") << " "
- << Text::colorize (Text::underline_cyan, Text::nocolor, "underline_cyan") << " "
- << Text::colorize (Text::bold_underline_cyan, Text::nocolor, "bold_underline_cyan") << std::endl
+ << " " << Text::colorize (Text::cyan, Text::nocolor, "cyan") << " "
+ << Text::colorize (Text::bold_cyan, Text::nocolor, "bold_cyan") << " "
+ << Text::colorize (Text::underline_cyan, Text::nocolor, "underline_cyan") << " "
+ << Text::colorize (Text::bold_underline_cyan, Text::nocolor, "bold_underline_cyan") << std::endl
- << " " << Text::colorize (Text::white, Text::nocolor, "white") << " "
- << Text::colorize (Text::bold_white, Text::nocolor, "bold_white") << " "
- << Text::colorize (Text::underline_white, Text::nocolor, "underline_white") << " "
- << Text::colorize (Text::bold_underline_white, Text::nocolor, "bold_underline_white") << std::endl
+ << " " << Text::colorize (Text::white, Text::nocolor, "white") << " "
+ << Text::colorize (Text::bold_white, Text::nocolor, "bold_white") << " "
+ << Text::colorize (Text::underline_white, Text::nocolor, "underline_white") << " "
+ << Text::colorize (Text::bold_underline_white, Text::nocolor, "bold_underline_white") << std::endl
- << std::endl << "Background" << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_black, "on_black") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_black, "on_bright_black") << std::endl
+ << std::endl << "Background" << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_black, "on_black") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_black, "on_bright_black") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_red, "on_red") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_red, "on_bright_red") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_red, "on_red") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_red, "on_bright_red") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_green, "on_green") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_green, "on_bright_green") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_green, "on_green") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_green, "on_bright_green") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_yellow, "on_yellow") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_yellow, "on_bright_yellow") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_yellow, "on_yellow") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_yellow, "on_bright_yellow") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_blue, "on_blue") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_blue, "on_bright_blue") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_blue, "on_blue") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_blue, "on_bright_blue") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_magenta, "on_magenta") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_magenta, "on_bright_magenta") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_magenta, "on_magenta") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_magenta, "on_bright_magenta") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_cyan, "on_cyan") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_cyan, "on_bright_cyan") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_cyan, "on_cyan") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_cyan, "on_bright_cyan") << std::endl
- << " " << Text::colorize (Text::nocolor, Text::on_white, "on_white") << " "
- << Text::colorize (Text::nocolor, Text::on_bright_white, "on_bright_white") << std::endl
+ << " " << Text::colorize (Text::nocolor, Text::on_white, "on_white") << " "
+ << Text::colorize (Text::nocolor, Text::on_bright_white, "on_bright_white") << std::endl
- << optionalBlankLine (conf);
+ << optionalBlankLine (conf);
}
else
{
- std::cout << "Color is currently turned off in your .taskrc file." << std::endl;
+ out << "Color is currently turned off in your .taskrc file." << std::endl;
}
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/report.cpp b/src/report.cpp
index 5c1d8ac8f..8d3339c58 100644
--- a/src/report.cpp
+++ b/src/report.cpp
@@ -26,6 +26,7 @@
////////////////////////////////////////////////////////////////////////////////
#include
#include
+#include
#include
#include
#include
@@ -110,8 +111,10 @@ void filter (std::vector& all, T& task)
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed.
-void handleList (TDB& tdb, T& task, Config& conf)
+std::string handleList (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -242,23 +245,27 @@ void handleList (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed. Show a narrow
// list that works better on mobile devices.
-void handleSmallList (TDB& tdb, T& task, Config& conf)
+std::string handleSmallList (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -371,22 +378,26 @@ void handleSmallList (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed.
-void handleCompleted (TDB& tdb, T& task, Config& conf)
+std::string handleCompleted (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -459,21 +470,25 @@ void handleCompleted (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Display all information for the given task.
-void handleInfo (TDB& tdb, T& task, Config& conf)
+std::string handleInfo (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -657,21 +672,22 @@ 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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << std::endl;
else
- std::cout << "No matches." << std::endl;
+ out << "No matches." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed.
-void handleLongList (TDB& tdb, T& task, Config& conf)
+std::string handleLongList (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -827,22 +843,26 @@ void handleLongList (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches." << std::endl;
+ out << "No matches." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Project Tasks Avg Age Status
// A 12 13d XXXXXXXX------
// B 109 3d 12h XX------------
-void handleReportSummary (TDB& tdb, T& task, Config& conf)
+std::string handleReportSummary (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Generate unique list of project names.
tdb.gc ();
std::map allProjects;
@@ -984,14 +1004,16 @@ void handleReportSummary (TDB& tdb, T& task, Config& conf)
}
if (table.rowCount ())
- std::cout << optionalBlankLine (conf)
- << table.render ()
- << optionalBlankLine (conf)
- << table.rowCount ()
- << (table.rowCount () == 1 ? " project" : " projects")
- << std::endl;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " project" : " projects")
+ << std::endl;
else
- std::cout << "No projects." << std::endl;
+ out << "No projects." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -1013,8 +1035,10 @@ void handleReportSummary (TDB& tdb, T& task, Config& conf)
//
// Make the "three" tasks a configurable number
//
-void handleReportNext (TDB& tdb, T& task, Config& conf)
+std::string handleReportNext (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Load all pending.
tdb.gc ();
std::vector pending;
@@ -1154,15 +1178,17 @@ void handleReportNext (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -1187,8 +1213,10 @@ time_t monthlyEpoch (const std::string& date)
return 0;
}
-void handleReportHistory (TDB& tdb, T& task, Config& conf)
+std::string handleReportHistory (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
std::map groups;
std::map addedGroup;
std::map completedGroup;
@@ -1363,16 +1391,20 @@ void handleReportHistory (TDB& tdb, T& task, Config& conf)
}
if (table.rowCount ())
- std::cout << optionalBlankLine (conf)
- << table.render ()
- << std::endl;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << std::endl;
else
- std::cout << "No tasks." << std::endl;
+ out << "No tasks." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleReportGHistory (TDB& tdb, T& task, Config& conf)
+std::string handleReportGHistory (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -1574,24 +1606,105 @@ void handleReportGHistory (TDB& tdb, T& task, Config& conf)
if (table.rowCount ())
{
- std::cout << optionalBlankLine (conf)
- << table.render ()
- << std::endl;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << std::endl;
if (conf.get ("color", true))
- std::cout << "Legend: "
- << Text::colorize (Text::black, Text::on_red, "added")
- << ", "
- << Text::colorize (Text::black, Text::on_green, "completed")
- << ", "
- << Text::colorize (Text::black, Text::on_yellow, "deleted")
- << optionalBlankLine (conf)
- << std::endl;
+ out << "Legend: "
+ << Text::colorize (Text::black, Text::on_red, "added")
+ << ", "
+ << Text::colorize (Text::black, Text::on_green, "completed")
+ << ", "
+ << Text::colorize (Text::black, Text::on_yellow, "deleted")
+ << optionalBlankLine (conf)
+ << std::endl;
else
- std::cout << "Legend: + added, X completed, - deleted" << std::endl;
+ out << "Legend: + added, X completed, - deleted" << std::endl;
}
else
- std::cout << "No tasks." << std::endl;
+ out << "No tasks." << std::endl;
+
+ return out.str ();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// A summary of the command usage. Not useful to users, but used to display
+// usage statistics for feedback.
+//
+// 2006-12-04 19:59:43 "task list"
+//
+std::string handleReportUsage (const TDB& tdb, T& task, Config& conf)
+{
+ std::stringstream out;
+
+ if (conf.get ("command.logging") == "on")
+ {
+ std::map usage;
+ std::vector all;
+ tdb.logRead (all);
+ for (unsigned int i = 0; i < all.size (); ++i)
+ {
+ // 0123456789012345678901
+ // v 21
+ // 2006-12-04 19:59:43 "task list"
+ std::string command = all[i].substr (21, all[i].length () - 22);
+
+ // Parse as a command line.
+ std::vector args;
+ split (args, command, " ");
+
+ try
+ {
+ T task;
+ std::string commandName;
+ parse (args, commandName, task, conf);
+
+ usage[commandName]++;
+ }
+
+ // Deliberately ignore errors from parsing the command log, as there may
+ // be commands from a prior version of task in there, which were
+ // abbreviated, and are now ambiguous.
+ catch (...) {}
+ }
+
+ // Now render the table.
+ Table table;
+ table.addColumn ("Command");
+ table.addColumn ("Frequency");
+
+ if (conf.get ("color", true))
+ {
+ table.setColumnUnderline (0);
+ table.setColumnUnderline (1);
+ }
+ else
+ table.setTableDashedUnderline ();
+
+ table.setColumnJustification (1, Table::right);
+ table.sortOn (1, Table::descendingNumeric);
+ table.setDateFormat (conf.get ("dateformat", "m/d/Y"));
+
+ foreach (i, usage)
+ {
+ int row = table.addRow ();
+ table.addCell (row, 0, (i->first == "" ? "(modify)" : i->first));
+ table.addCell (row, 1, i->second);
+ }
+
+ if (table.rowCount ())
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << std::endl;
+ else
+ out << "No usage." << std::endl;
+ }
+ else
+ out << "Command logging is not enabled, so no history has been kept."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -1719,8 +1832,10 @@ std::string renderMonths (
}
////////////////////////////////////////////////////////////////////////////////
-void handleReportCalendar (TDB& tdb, T& task, Config& conf)
+std::string handleReportCalendar (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Load all the pending tasks.
tdb.gc ();
std::vector pending;
@@ -1751,7 +1866,7 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
int mTo = newest.month ();
int yTo = newest.year ();
- std::cout << std::endl;
+ out << std::endl;
std::string output;
int monthsPerLine = (conf.get ("monthsperline", 1));
@@ -1768,11 +1883,11 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
int left = (18 - month.length ()) / 2 + 1;
int right = 18 - left - month.length ();
- std::cout << std::setw (left) << ' '
- << month
- << ' '
- << nextY
- << std::setw (right) << ' ';
+ out << std::setw (left) << ' '
+ << month
+ << ' '
+ << nextY
+ << std::setw (right) << ' ';
if (++nextM > 12)
{
@@ -1781,10 +1896,10 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
}
}
- std::cout << std::endl
- << optionalBlankLine (conf)
- << renderMonths (mFrom, yFrom, today, pending, conf)
- << std::endl;
+ out << std::endl
+ << optionalBlankLine (conf)
+ << renderMonths (mFrom, yFrom, today, pending, conf)
+ << std::endl;
mFrom += monthsPerLine;
if (mFrom > 12)
@@ -1794,20 +1909,24 @@ void handleReportCalendar (TDB& tdb, T& task, Config& conf)
}
}
- std::cout << "Legend: "
- << Text::colorize (Text::cyan, Text::nocolor, "today")
- << ", "
- << Text::colorize (Text::black, Text::on_yellow, "due")
- << ", "
- << Text::colorize (Text::black, Text::on_red, "overdue")
- << "."
- << optionalBlankLine (conf)
- << std::endl;
+ out << "Legend: "
+ << Text::colorize (Text::cyan, Text::nocolor, "today")
+ << ", "
+ << Text::colorize (Text::black, Text::on_yellow, "due")
+ << ", "
+ << Text::colorize (Text::black, Text::on_red, "overdue")
+ << "."
+ << optionalBlankLine (conf)
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleReportActive (TDB& tdb, T& task, Config& conf)
+std::string handleReportActive (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -1913,19 +2032,23 @@ void handleReportActive (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No active tasks." << std::endl;
+ out << "No active tasks." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleReportOverdue (TDB& tdb, T& task, Config& conf)
+std::string handleReportOverdue (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -2020,21 +2143,25 @@ void handleReportOverdue (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No overdue tasks." << std::endl;
+ out << "No overdue tasks." << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed.
-void handleReportOldest (TDB& tdb, T& task, Config& conf)
+std::string handleReportOldest (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -2164,22 +2291,26 @@ void handleReportOldest (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
// Successively apply filters based on the task object built from the command
// line. Tasks that match all the specified criteria are listed.
-void handleReportNewest (TDB& tdb, T& task, Config& conf)
+std::string handleReportNewest (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
#ifdef HAVE_LIBNCURSES
@@ -2310,21 +2441,25 @@ void handleReportNewest (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;
+ out << optionalBlankLine (conf)
+ << table.render ()
+ << optionalBlankLine (conf)
+ << table.rowCount ()
+ << (table.rowCount () == 1 ? " task" : " tasks")
+ << std::endl;
else
- std::cout << "No matches."
- << std::endl;
+ out << "No matches."
+ << std::endl;
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
-void handleReportStats (TDB& tdb, T& task, Config& conf)
+std::string handleReportStats (TDB& tdb, T& task, Config& conf)
{
+ std::stringstream out;
+
// Get all the tasks.
std::vector tasks;
tdb.allT (tasks);
@@ -2371,40 +2506,42 @@ void handleReportStats (TDB& tdb, T& task, Config& conf)
if (tags.size ()) ++taggedT;
}
- std::cout << "Pending " << pendingT << std::endl
- << "Recurring " << recurringT << std::endl
- << "Completed " << completedT << std::endl
- << "Deleted " << deletedT << std::endl
- << "Total " << totalT << std::endl;
+ out << "Pending " << pendingT << std::endl
+ << "Recurring " << recurringT << std::endl
+ << "Completed " << completedT << std::endl
+ << "Deleted " << deletedT << std::endl
+ << "Total " << totalT << std::endl;
if (tasks.size ())
{
Date e (earliest);
- std::cout << "Oldest task " << e.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
+ out << "Oldest task " << e.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
Date l (latest);
- std::cout << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
- std::cout << "Task used for " << formatSeconds (latest - earliest) << std::endl;
+ out << "Newest task " << l.toString (conf.get ("dateformat", "m/d/Y")) << std::endl;
+ out << "Task used for " << formatSeconds (latest - earliest) << std::endl;
}
if (totalT)
- std::cout << "Task added every " << formatSeconds ((latest - earliest) / totalT) << std::endl;
+ out << "Task added every " << formatSeconds ((latest - earliest) / totalT) << std::endl;
if (completedT)
- std::cout << "Task completed every " << formatSeconds ((latest - earliest) / completedT) << std::endl;
+ out << "Task completed every " << formatSeconds ((latest - earliest) / completedT) << std::endl;
if (deletedT)
- std::cout << "Task deleted every " << formatSeconds ((latest - earliest) / deletedT) << std::endl;
+ out << "Task deleted every " << formatSeconds ((latest - earliest) / deletedT) << std::endl;
if (pendingT || completedT)
- std::cout << "Average time pending "
+ out << "Average time pending "
<< formatSeconds ((int) ((daysPending / (pendingT + completedT)) * 86400))
<< std::endl;
if (totalT)
{
- std::cout << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl;
- std::cout << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl;
+ out << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl;
+ out << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl;
}
+
+ return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/task.cpp b/src/task.cpp
index e53f9675f..6bf8b06bf 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)
{
@@ -280,6 +285,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.
@@ -290,69 +296,41 @@ int main (int argc, char** argv)
}
TDB tdb;
- tdb.dataDirectory (expandPath (conf.get ("data.location")));
+ gTdb = &tdb;
+ std::string dataLocation = expandPath (conf.get ("data.location"));
+ tdb.dataDirectory (dataLocation);
- // 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 != "")
+ // Log commands, if desired.
+ if (conf.get ("command.logging") == "on")
+ tdb.logCommand (argc, argv);
+
+ // Set up TDB callback.
+ std::string shadowFile = expandPath (conf.get ("shadow.file"));
+ if (shadowFile != "")
{
- // 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]);
+ if (shadowFile == dataLocation + "/pending.data")
+ throw std::string ("Configuration variable 'shadow.file' is set to "
+ "overwrite your pending tasks. Please change it.");
+
+ if (shadowFile == dataLocation + "/completed.data")
+ throw std::string ("Configuration variable 'shadow.file' is set to "
+ "overwrite your completed tasks. Please change it.");
+
+ 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 == "" && task.getId ()) handleModify (tdb, task, conf);
- else if (command == "help") longUsage (conf);
- else shortUsage (conf);
+ std::cout << runTaskCommand (argc, argv, tdb, conf);
}
catch (std::string& error)
{
- std::cout << error << std::endl;
+ std::cerr << error << std::endl;
return -1;
}
catch (...)
{
- std::cout << "Unknown error." << std::endl;
+ std::cerr << "Unknown error." << std::endl;
return -2;
}
@@ -391,12 +369,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;
}
@@ -544,7 +528,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);
}
@@ -563,7 +546,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);
}
@@ -579,7 +561,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);
}
@@ -598,7 +579,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);
}
@@ -614,7 +594,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);
}
@@ -630,7 +609,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 +617,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);
}
@@ -657,7 +634,6 @@ void updateRecurrenceMask (
T& task)
{
std::string parent = task.getAttribute ("parent");
-// std::cout << "# updateRecurrenceMask of " << parent << std::endl;
if (parent != "")
{
std::vector ::iterator it;
@@ -665,11 +641,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) ? '-'
@@ -677,15 +650,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 += "?";
@@ -703,3 +672,129 @@ void updateRecurrenceMask (
}
////////////////////////////////////////////////////////////////////////////////
+// Using gTdb and gConf, generate a report.
+void onChangeCallback ()
+{
+ try
+ {
+ if (gConf && gTdb)
+ {
+ // Determine if shadow file is enabled.
+ std::string shadowFile = expandPath (gConf->get ("shadow.file"));
+ if (shadowFile != "")
+ {
+ std::string oldCurses = gConf->get ("curses");
+ std::string oldColor = gConf->get ("color");
+ gConf->set ("curses", "off");
+ gConf->set ("color", "off");
+
+ // Run report. Use shadow.command, using default.command as a fallback
+ // with "list" as a default.
+ std::string command = gConf->get ("shadow.command",
+ gConf->get ("default.command", "list"));
+ std::vector args;
+ split (args, command, ' ');
+ std::string result = runTaskCommand (args, *gTdb, *gConf);
+
+ std::ofstream out (shadowFile.c_str ());
+ if (out.good ())
+ {
+ out << result;
+ out.close ();
+ }
+ else
+ throw std::string ("Could not write file '") + shadowFile + "'";
+
+ gConf->set ("curses", oldCurses);
+ gConf->set ("color", oldColor);
+ }
+ else
+ throw std::string ("No specified shadow file '") + shadowFile + "'.";
+ }
+ else
+ throw std::string ("Internal error (TDB/Config).");
+ }
+
+ catch (std::string& error)
+ {
+ std::cout << error << std::endl;
+ }
+
+ catch (...)
+ {
+ std::cout << "Unknown error." << std::endl;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+std::string runTaskCommand (
+ int argc,
+ char** argv,
+ TDB& tdb,
+ Config& conf)
+{
+ std::vector args;
+ for (int i = 1; i < argc; ++i)
+ args.push_back (argv[i]);
+
+ return runTaskCommand (args, tdb, conf);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+std::string 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);
+
+ std::string out = "";
+
+ if (command == "add") handleAdd (tdb, task, conf);
+ else if (command == "projects") out = handleProjects (tdb, task, conf);
+ else if (command == "tags") out = handleTags (tdb, task, conf);
+ else if (command == "list") out = handleList (tdb, task, conf);
+ else if (command == "info") out = handleInfo (tdb, task, conf);
+ else if (command == "undelete") out = handleUndelete (tdb, task, conf);
+ else if (command == "long") out = handleLongList (tdb, task, conf);
+ else if (command == "ls") out = handleSmallList (tdb, task, conf);
+ else if (command == "colors") out = handleColor ( conf);
+ else if (command == "completed") out = handleCompleted (tdb, task, conf);
+ else if (command == "delete") out = handleDelete (tdb, task, conf);
+ else if (command == "start") out = handleStart (tdb, task, conf);
+ else if (command == "done") handleDone (tdb, task, conf);
+ else if (command == "undo") out = handleUndo (tdb, task, conf);
+ else if (command == "export") handleExport (tdb, task, conf);
+ else if (command == "version") out = handleVersion ( conf);
+ else if (command == "summary") out = handleReportSummary (tdb, task, conf);
+ else if (command == "next") out = handleReportNext (tdb, task, conf);
+ else if (command == "history") out = handleReportHistory (tdb, task, conf);
+ else if (command == "ghistory") out = handleReportGHistory (tdb, task, conf);
+ else if (command == "calendar") out = handleReportCalendar (tdb, task, conf);
+ else if (command == "active") out = handleReportActive (tdb, task, conf);
+ else if (command == "overdue") out = handleReportOverdue (tdb, task, conf);
+ else if (command == "oldest") out = handleReportOldest (tdb, task, conf);
+ else if (command == "newest") out = handleReportNewest (tdb, task, conf);
+ else if (command == "stats") out = handleReportStats (tdb, task, conf);
+ else if (command == "usage") out = handleReportUsage (tdb, task, conf);
+ else if (command == "" && task.getId ()) handleModify (tdb, task, conf);
+ else if (command == "help") longUsage (conf);
+ else shortUsage (conf);
+
+ return out;
+}
+
+////////////////////////////////////////////////////////////////////////////////
diff --git a/src/task.h b/src/task.h
index 9c417435b..8bfb92f0c 100644
--- a/src/task.h
+++ b/src/task.h
@@ -66,38 +66,42 @@ void handleRecurrence (TDB&, std::vector &);
bool generateDueDates (T&, std::vector &);
Date getNextRecurrence (Date&, std::string&);
void updateRecurrenceMask (TDB&, std::vector &, T&);
+void onChangeCallback ();
+std::string runTaskCommand (int, char**, TDB&, Config&);
+std::string runTaskCommand (std::vector &, TDB&, Config&);
// command.cpp
-void handleAdd (const TDB&, T&, Config&);
-void handleProjects (TDB&, T&, Config&);
-void handleTags (TDB&, T&, Config&);
-void handleUndelete (TDB&, T&, Config&);
-void handleVersion (Config&);
+void handleAdd (TDB&, T&, Config&);
void handleExport (TDB&, T&, Config&);
-void handleDelete (TDB&, T&, Config&);
-void handleStart (TDB&, T&, Config&);
void handleDone (TDB&, T&, Config&);
-void handleUndo (TDB&, T&, Config&);
void handleModify (TDB&, T&, Config&);
-void handleColor (Config&);
+std::string handleProjects (TDB&, T&, Config&);
+std::string handleTags (TDB&, T&, Config&);
+std::string handleUndelete (TDB&, T&, Config&);
+std::string handleVersion (Config&);
+std::string handleDelete (TDB&, T&, Config&);
+std::string handleStart (TDB&, T&, Config&);
+std::string handleUndo (TDB&, T&, Config&);
+std::string handleColor (Config&);
// report.cpp
void filter (std::vector&, T&);
-void handleList (TDB&, T&, Config&);
-void handleInfo (TDB&, T&, Config&);
-void handleLongList (TDB&, T&, Config&);
-void handleSmallList (TDB&, T&, Config&);
-void handleCompleted (TDB&, T&, Config&);
-void handleReportSummary (TDB&, T&, Config&);
-void handleReportNext (TDB&, T&, Config&);
-void handleReportHistory (TDB&, T&, Config&);
-void handleReportGHistory (TDB&, T&, Config&);
-void handleReportCalendar (TDB&, T&, Config&);
-void handleReportActive (TDB&, T&, Config&);
-void handleReportOverdue (TDB&, T&, Config&);
-void handleReportStats (TDB&, T&, Config&);
-void handleReportOldest (TDB&, T&, Config&);
-void handleReportNewest (TDB&, T&, Config&);
+std::string handleList (TDB&, T&, Config&);
+std::string handleInfo (TDB&, T&, Config&);
+std::string handleLongList (TDB&, T&, Config&);
+std::string handleSmallList (TDB&, T&, Config&);
+std::string handleCompleted (TDB&, T&, Config&);
+std::string handleReportSummary (TDB&, T&, Config&);
+std::string handleReportNext (TDB&, T&, Config&);
+std::string handleReportHistory (TDB&, T&, Config&);
+std::string handleReportGHistory (TDB&, T&, Config&);
+std::string handleReportUsage (const TDB&, T&, Config&);
+std::string handleReportCalendar (TDB&, T&, Config&);
+std::string handleReportActive (TDB&, T&, Config&);
+std::string handleReportOverdue (TDB&, T&, Config&);
+std::string handleReportStats (TDB&, T&, Config&);
+std::string handleReportOldest (TDB&, T&, Config&);
+std::string handleReportNewest (TDB&, T&, Config&);
// util.cpp
bool confirm (const std::string&);