- Applied large patch to make task return meaningful exit codes.
- Added unit tests to prove this.
- Thanks to Pietro Cerutti.
This commit is contained in:
Paul Beckingham
2009-08-29 09:14:26 -04:00
parent cc5c99c0a1
commit 62be3f8acb
11 changed files with 302 additions and 157 deletions

View File

@@ -2,7 +2,9 @@
------ current release --------------------------- ------ current release ---------------------------
1.8.2 1.8.2
+ Added feature #282 that returns useful exit codes to the shell. Now a
script can detect whether no tasks were returned by a report (thanks to
Pietro Cerutti).
------ old releases ------------------------------ ------ old releases ------------------------------

1
NEWS
View File

@@ -9,6 +9,7 @@ New Features in task 1.8
- In addition to being a standard part of Fedora 10 and 11 (yum install task), - In addition to being a standard part of Fedora 10 and 11 (yum install task),
task is now also a standard part of Cygwin 1.5 task is now also a standard part of Cygwin 1.5
- There are new demo movies on taskwarrior.org - There are new demo movies on taskwarrior.org
- Shell-friendly exit codes
Please refer to the ChangeLog file for full details. There are too many to Please refer to the ChangeLog file for full details. There are too many to
list here. list here.

View File

@@ -126,23 +126,26 @@ void Context::initialize ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int Context::run () int Context::run ()
{ {
int rc;
Timer t ("Context::run"); Timer t ("Context::run");
std::string output; std::string output;
try try
{ {
parse (); // Parse command line. parse (); // Parse command line.
output = dispatch (); // Dispatch to command handlers. rc = dispatch (output); // Dispatch to command handlers.
} }
catch (const std::string& error) catch (const std::string& error)
{ {
footnote (error); footnote (error);
rc = 2;
} }
catch (...) catch (...)
{ {
footnote (stringtable.get (100, "Unknown error.")); footnote (stringtable.get (100, "Unknown error."));
rc = 3;
} }
// Dump all debug messages. // Dump all debug messages.
@@ -170,63 +173,63 @@ int Context::run ()
else else
std::cout << *f << std::endl; std::cout << *f << std::endl;
return 0; return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string Context::dispatch () int Context::dispatch (std::string &out)
{ {
int rc = 0;
Timer t ("Context::dispatch"); Timer t ("Context::dispatch");
// TODO Just look at this thing. It cries out for a dispatch table. // TODO Just look at this thing. It cries out for a dispatch table.
std::string out; if (cmd.command == "projects") { rc = handleProjects (out); }
if (cmd.command == "projects") { out = handleProjects (); } else if (cmd.command == "tags") { rc = handleTags (out); }
else if (cmd.command == "tags") { out = handleTags (); } else if (cmd.command == "colors") { rc = handleColor (out); }
else if (cmd.command == "colors") { out = handleColor (); } else if (cmd.command == "version") { rc = handleVersion (out); }
else if (cmd.command == "version") { out = handleVersion (); } else if (cmd.command == "help") { rc = longUsage (out); }
else if (cmd.command == "help") { out = longUsage (); } else if (cmd.command == "stats") { rc = handleReportStats (out); }
else if (cmd.command == "stats") { out = handleReportStats (); } else if (cmd.command == "info") { rc = handleInfo (out); }
else if (cmd.command == "info") { out = handleInfo (); } else if (cmd.command == "history") { rc = handleReportHistory (out); }
else if (cmd.command == "history") { out = handleReportHistory (); } else if (cmd.command == "ghistory") { rc = handleReportGHistory (out); }
else if (cmd.command == "ghistory") { out = handleReportGHistory (); } else if (cmd.command == "summary") { rc = handleReportSummary (out); }
else if (cmd.command == "summary") { out = handleReportSummary (); } else if (cmd.command == "calendar") { rc = handleReportCalendar (out); }
else if (cmd.command == "calendar") { out = handleReportCalendar (); } else if (cmd.command == "timesheet") { rc = handleReportTimesheet (out); }
else if (cmd.command == "timesheet") { out = handleReportTimesheet (); } else if (cmd.command == "add") { rc = handleAdd (out); }
else if (cmd.command == "add") { out = handleAdd (); } else if (cmd.command == "append") { rc = handleAppend (out); }
else if (cmd.command == "append") { out = handleAppend (); } else if (cmd.command == "annotate") { rc = handleAnnotate (out); }
else if (cmd.command == "annotate") { out = handleAnnotate (); } else if (cmd.command == "done") { rc = handleDone (out); }
else if (cmd.command == "done") { out = handleDone (); } else if (cmd.command == "delete") { rc = handleDelete (out); }
else if (cmd.command == "delete") { out = handleDelete (); } else if (cmd.command == "start") { rc = handleStart (out); }
else if (cmd.command == "start") { out = handleStart (); } else if (cmd.command == "stop") { rc = handleStop (out); }
else if (cmd.command == "stop") { out = handleStop (); } else if (cmd.command == "export") { rc = handleExport (out); }
else if (cmd.command == "export") { out = handleExport (); } else if (cmd.command == "import") { rc = handleImport (out); }
else if (cmd.command == "import") { out = handleImport (); } else if (cmd.command == "duplicate") { rc = handleDuplicate (out); }
else if (cmd.command == "duplicate") { out = handleDuplicate (); } else if (cmd.command == "edit") { rc = handleEdit (out); }
else if (cmd.command == "edit") { out = handleEdit (); }
#ifdef FEATURE_SHELL #ifdef FEATURE_SHELL
else if (cmd.command == "shell") { handleShell (); } else if (cmd.command == "shell") { handleShell ( ); }
#endif #endif
else if (cmd.command == "undo") { handleUndo (); } else if (cmd.command == "undo") { handleUndo ( ); }
else if (cmd.command == "_projects") { out = handleCompletionProjects (); } else if (cmd.command == "_projects") { rc = handleCompletionProjects (out); }
else if (cmd.command == "_tags") { out = handleCompletionTags (); } else if (cmd.command == "_tags") { rc = handleCompletionTags (out); }
else if (cmd.command == "_commands") { out = handleCompletionCommands (); } else if (cmd.command == "_commands") { rc = handleCompletionCommands (out); }
else if (cmd.command == "_ids") { out = handleCompletionIDs (); } else if (cmd.command == "_ids") { rc = handleCompletionIDs (out); }
else if (cmd.command == "_config") { out = handleCompletionConfig (); } else if (cmd.command == "_config") { rc = handleCompletionConfig (out); }
else if (cmd.command == "" && else if (cmd.command == "" &&
sequence.size ()) { out = handleModify (); } sequence.size ()) { rc = handleModify (out); }
// Command that display IDs and therefore need TDB::gc first. // Command that display IDs and therefore need TDB::gc first.
else if (cmd.command == "next") { if (!inShadow) tdb.gc (); out = handleReportNext (); } else if (cmd.command == "next") { if (!inShadow) tdb.gc (); rc = handleReportNext (out); }
else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); out = handleCustomReport (cmd.command); } else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); rc = handleCustomReport (cmd.command, out); }
// If the command is not recognized, display usage. // If the command is not recognized, display usage.
else { out = shortUsage (); } else { rc = shortUsage (out); }
// Only update the shadow file if such an update was not suppressed (shadow), // Only update the shadow file if such an update was not suppressed (shadow),
if (cmd.isWriteCommand () && !inShadow) if (cmd.isWriteCommand () && !inShadow)
shadow (); shadow ();
return out; return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -265,7 +268,8 @@ void Context::shadow ()
initialize (); initialize ();
parse (); parse ();
std::string result = dispatch (); std::string result;
(void)dispatch (result);
std::ofstream out (shadowFile.c_str ()); std::ofstream out (shadowFile.c_str ());
if (out.good ()) if (out.good ())
{ {

View File

@@ -50,7 +50,7 @@ public:
void initialize (); // for reinitializing void initialize (); // for reinitializing
int run (); // task classic int run (); // task classic
int interactive (); // task interactive (not implemented) int interactive (); // task interactive (not implemented)
std::string dispatch (); // command handler dispatch int dispatch (std::string&); // command handler dispatch
void shadow (); // shadow file update void shadow (); // shadow file update
int getWidth (); // determine terminal width int getWidth (); // determine terminal width

View File

@@ -49,7 +49,7 @@
extern Context context; extern Context context;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleAdd () int handleAdd (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
@@ -110,12 +110,14 @@ std::string handleAdd ()
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleProjects () int handleProjects (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
context.filter.push_back (Att ("status", "pending")); context.filter.push_back (Att ("status", "pending"));
@@ -195,15 +197,18 @@ std::string handleProjects ()
<< " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")" << " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")"
<< std::endl; << std::endl;
} }
else else {
out << "No projects." out << "No projects."
<< std::endl; << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleCompletionProjects () int handleCompletionProjects (std::string &outs)
{ {
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
@@ -228,12 +233,14 @@ std::string handleCompletionProjects ()
if (project->first.length ()) if (project->first.length ())
out << project->first << std::endl; out << project->first << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleTags () int handleTags (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
context.filter.push_back (Att ("status", "pending")); context.filter.push_back (Att ("status", "pending"));
@@ -290,15 +297,19 @@ std::string handleTags ()
<< " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")" << " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")"
<< std::endl; << std::endl;
} }
else else {
out << "No tags." out << "No tags."
<< std::endl; << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleCompletionTags () int handleCompletionTags (std::string &outs)
{ {
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
@@ -328,11 +339,12 @@ std::string handleCompletionTags ()
foreach (tag, unique) foreach (tag, unique)
out << tag->first << std::endl; out << tag->first << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleCompletionCommands () int handleCompletionCommands (std::string &outs)
{ {
std::vector <std::string> commands; std::vector <std::string> commands;
context.cmd.allCommands (commands); context.cmd.allCommands (commands);
@@ -342,11 +354,12 @@ std::string handleCompletionCommands ()
foreach (command, commands) foreach (command, commands)
out << *command << std::endl; out << *command << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleCompletionConfig () int handleCompletionConfig (std::string &outs)
{ {
std::vector <std::string> configs; std::vector <std::string> configs;
context.config.all (configs); context.config.all (configs);
@@ -356,11 +369,12 @@ std::string handleCompletionConfig ()
foreach (config, configs) foreach (config, configs)
out << *config << std::endl; out << *config << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleCompletionIDs () int handleCompletionIDs (std::string &outs)
{ {
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
@@ -381,7 +395,8 @@ std::string handleCompletionIDs ()
foreach (id, ids) foreach (id, ids)
out << *id << std::endl; out << *id << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -395,8 +410,9 @@ void handleUndo ()
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleVersion () int handleVersion (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
// Create a table for the disclaimer. // Create a table for the disclaimer.
@@ -524,7 +540,7 @@ std::string handleVersion ()
if (unrecognized.size ()) if (unrecognized.size ())
{ {
out << "Your .taskrc file contains these unrecognized variables:" out << "Your .taskrc file contains these unrecognized variables:"
<< std::endl; << std::endl;
foreach (i, unrecognized) foreach (i, unrecognized)
out << " " << *i << std::endl; out << " " << *i << std::endl;
@@ -535,9 +551,11 @@ std::string handleVersion ()
// Verify installation. This is mentioned in the documentation as the way to // Verify installation. This is mentioned in the documentation as the way to
// ensure everything is properly installed. // ensure everything is properly installed.
if (all.size () == 0) if (all.size () == 0) {
out << "Configuration error: .taskrc contains no entries" out << "Configuration error: .taskrc contains no entries"
<< std::endl; << std::endl;
rc = 1;
}
else else
{ {
if (context.config.get ("data.location") == "") if (context.config.get ("data.location") == "")
@@ -551,12 +569,14 @@ std::string handleVersion ()
<< std::endl; << std::endl;
} }
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleDelete () int handleDelete (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
context.disallowModification (); context.disallowModification ();
@@ -645,19 +665,23 @@ std::string handleDelete ()
<< std::endl; << std::endl;
} }
} }
else else {
out << "Task not deleted." << std::endl; out << "Task not deleted." << std::endl;
rc = 1;
}
} }
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleStart () int handleStart (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
context.disallowModification (); context.disallowModification ();
@@ -699,18 +723,21 @@ std::string handleStart ()
<< task->get ("description") << task->get ("description")
<< "' already started." << "' already started."
<< std::endl; << std::endl;
rc = 1;
} }
} }
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleStop () int handleStop (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
context.disallowModification (); context.disallowModification ();
@@ -746,18 +773,21 @@ std::string handleStop ()
<< task->get ("description") << task->get ("description")
<< "' not started." << "' not started."
<< std::endl; << std::endl;
rc = 1;
} }
} }
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleDone () int handleDone (std::string &outs)
{ {
int rc = 0;
int count = 0; int count = 0;
std::stringstream out; std::stringstream out;
@@ -826,6 +856,7 @@ std::string handleDone ()
<< task->get ("description") << task->get ("description")
<< "' is not pending" << "' is not pending"
<< std::endl; << std::endl;
rc = 1;
} }
context.tdb.commit (); context.tdb.commit ();
@@ -839,11 +870,12 @@ std::string handleDone ()
<< " as done" << " as done"
<< std::endl; << std::endl;
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleExport () int handleExport (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
@@ -882,11 +914,12 @@ std::string handleExport ()
} }
} }
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleModify () int handleModify (std::string &outs)
{ {
int count = 0; int count = 0;
std::stringstream out; std::stringstream out;
@@ -959,11 +992,12 @@ std::string handleModify ()
if (context.config.get ("echo.command", true)) if (context.config.get ("echo.command", true))
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl; out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleAppend () int handleAppend (std::string &outs)
{ {
int count = 0; int count = 0;
std::stringstream out; std::stringstream out;
@@ -1026,11 +1060,12 @@ std::string handleAppend ()
if (context.config.get ("echo.command", true)) if (context.config.get ("echo.command", true))
out << "Appended " << count << " task" << (count == 1 ? "" : "s") << std::endl; out << "Appended " << count << " task" << (count == 1 ? "" : "s") << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleDuplicate () int handleDuplicate (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
int count = 0; int count = 0;
@@ -1095,7 +1130,8 @@ std::string handleDuplicate ()
if (context.config.get ("echo.command", true)) if (context.config.get ("echo.command", true))
out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl; out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -1174,8 +1210,9 @@ void handleShell ()
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleColor () int handleColor (std::string &outs)
{ {
int rc = 0;
std::stringstream out; std::stringstream out;
if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false))
{ {
@@ -1257,13 +1294,15 @@ std::string handleColor ()
out << "Color is currently turned off in your .taskrc file. " out << "Color is currently turned off in your .taskrc file. "
"To enable color, create the entry 'color=on'." "To enable color, create the entry 'color=on'."
<< std::endl; << std::endl;
rc = 1;
} }
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleAnnotate () int handleAnnotate (std::string &outs)
{ {
if (!context.task.has ("description")) if (!context.task.has ("description"))
throw std::string ("Cannot apply a blank annotation."); throw std::string ("Cannot apply a blank annotation.");
@@ -1307,7 +1346,8 @@ std::string handleAnnotate ()
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -52,7 +52,7 @@ static std::vector <std::string> customReports;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// This report will eventually become the one report that many others morph into // This report will eventually become the one report that many others morph into
// via the .taskrc file. // via the .taskrc file.
std::string handleCustomReport (const std::string& report) int handleCustomReport (const std::string& report, std::string &outs)
{ {
// Load report configuration. // Load report configuration.
std::string columnList = context.config.get ("report." + report + ".columns"); std::string columnList = context.config.get ("report." + report + ".columns");
@@ -94,21 +94,24 @@ std::string handleCustomReport (const std::string& report)
labelList, labelList,
sortList, sortList,
filterList, filterList,
tasks); tasks,
outs);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// This report will eventually become the one report that many others morph into // This report will eventually become the one report that many others morph into
// via the .taskrc file. // via the .taskrc file.
std::string runCustomReport ( int runCustomReport (
const std::string& report, const std::string& report,
const std::string& columnList, const std::string& columnList,
const std::string& labelList, const std::string& labelList,
const std::string& sortList, const std::string& sortList,
const std::string& filterList, const std::string& filterList,
std::vector <Task>& tasks) std::vector <Task>& tasks,
std::string &outs)
{ {
int rc = 0;
// Load report configuration. // Load report configuration.
std::vector <std::string> columns; std::vector <std::string> columns;
split (columns, columnList, ','); split (columns, columnList, ',');
@@ -547,11 +550,14 @@ std::string runCustomReport (
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " task" : " tasks") << (table.rowCount () == 1 ? " task" : " tasks")
<< std::endl; << std::endl;
else else {
out << "No matches." out << "No matches."
<< std::endl; << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -599,7 +599,7 @@ ARE_THESE_REALLY_HARMFUL:
// Introducing the Silver Bullet. This feature is the catch-all fixative for // Introducing the Silver Bullet. This feature is the catch-all fixative for
// various other ills. This is like opening up the hood and going in with a // various other ills. This is like opening up the hood and going in with a
// wrench. To be used sparingly. // wrench. To be used sparingly.
std::string handleEdit () int handleEdit (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
@@ -643,7 +643,8 @@ std::string handleEdit ()
context.tdb.commit (); context.tdb.commit ();
context.tdb.unlock (); context.tdb.unlock ();
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -666,6 +666,7 @@ static std::string importTask_1_6_0 (const std::vector <std::string>& lines)
static std::string importTaskCmdLine (const std::vector <std::string>& lines) static std::string importTaskCmdLine (const std::vector <std::string>& lines)
{ {
std::vector <std::string> failed; std::vector <std::string> failed;
std::string unused;
std::vector <std::string>::const_iterator it; std::vector <std::string>::const_iterator it;
for (it = lines.begin (); it != lines.end (); ++it) for (it = lines.begin (); it != lines.end (); ++it)
@@ -680,7 +681,7 @@ static std::string importTaskCmdLine (const std::vector <std::string>& lines)
context.task.clear (); context.task.clear ();
context.cmd.command = ""; context.cmd.command = "";
context.parse (); context.parse ();
handleAdd (); (void)handleAdd (unused);
context.clearMessages (); context.clearMessages ();
} }
@@ -1144,7 +1145,7 @@ static std::string importCSV (const std::vector <std::string>& lines)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleImport () int handleImport (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
@@ -1212,7 +1213,8 @@ std::string handleImport ()
else else
throw std::string ("You must specify a file to import."); throw std::string ("You must specify a file to import.");
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View File

@@ -55,25 +55,25 @@ int getDueState (const std::string&);
bool nag (Task&); bool nag (Task&);
// command.cpp // command.cpp
std::string handleAdd (); int handleAdd (std::string &);
std::string handleAppend (); int handleAppend (std::string &);
std::string handleExport (); int handleExport (std::string &);
std::string handleDone (); int handleDone (std::string &);
std::string handleModify (); int handleModify (std::string &);
std::string handleProjects (); int handleProjects (std::string &);
std::string handleCompletionProjects (); int handleCompletionProjects (std::string &);
std::string handleTags (); int handleTags (std::string &);
std::string handleCompletionTags (); int handleCompletionTags (std::string &);
std::string handleCompletionCommands (); int handleCompletionCommands (std::string &);
std::string handleCompletionIDs (); int handleCompletionIDs (std::string &);
std::string handleCompletionConfig (); int handleCompletionConfig (std::string &);
std::string handleVersion (); int handleVersion (std::string &);
std::string handleDelete (); int handleDelete (std::string &);
std::string handleStart (); int handleStart (std::string &);
std::string handleStop (); int handleStop (std::string &);
std::string handleColor (); int handleColor (std::string &);
std::string handleAnnotate (); int handleAnnotate (std::string &);
std::string handleDuplicate (); int handleDuplicate (std::string &);
void handleUndo (); void handleUndo ();
#ifdef FEATURE_SHELL #ifdef FEATURE_SHELL
void handleShell (); void handleShell ();
@@ -85,27 +85,28 @@ int deltaAttributes (Task&);
int deltaSubstitutions (Task&); int deltaSubstitutions (Task&);
// edit.cpp // edit.cpp
std::string handleEdit (); int handleEdit (std::string &);
// report.cpp // report.cpp
std::string shortUsage (); int shortUsage (std::string &);
std::string longUsage (); int longUsage (std::string &);
std::string handleInfo (); int handleInfo (std::string &);
std::string handleReportSummary (); int handleReportSummary (std::string &);
std::string handleReportNext (); int handleReportNext (std::string &);
std::string handleReportHistory (); int handleReportHistory (std::string &);
std::string handleReportGHistory (); int handleReportGHistory (std::string &);
std::string handleReportCalendar (); int handleReportCalendar (std::string &);
std::string handleReportStats (); int handleReportStats (std::string &);
std::string handleReportTimesheet (); int handleReportTimesheet (std::string &);
std::string getFullDescription (Task&); std::string getFullDescription (Task&);
std::string getDueDate (Task&); std::string getDueDate (Task&);
// custom.cpp // custom.cpp
std::string handleCustomReport (const std::string&); int handleCustomReport (const std::string&, std::string &);
std::string runCustomReport (const std::string&, const std::string&, int runCustomReport (const std::string&, const std::string&,
const std::string&, const std::string&, const std::string&, const std::string&,
const std::string&, std::vector <Task>&); const std::string&, std::vector <Task>&,
std::string&);
void validReportColumns (const std::vector <std::string>&); void validReportColumns (const std::vector <std::string>&);
void validSortColumns (const std::vector <std::string>&, const std::vector <std::string>&); void validSortColumns (const std::vector <std::string>&, const std::vector <std::string>&);
@@ -118,7 +119,7 @@ std::string colorizeFootnote (const std::string&);
std::string colorizeDebug (const std::string&); std::string colorizeDebug (const std::string&);
// import.cpp // import.cpp
std::string handleImport (); int handleImport (std::string&);
// list template // list template
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@@ -50,7 +50,7 @@
extern Context context; extern Context context;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string shortUsage () int shortUsage (std::string &outs)
{ {
Table table; Table table;
@@ -209,14 +209,19 @@ std::string shortUsage ()
<< std::endl << std::endl
<< std::endl; << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string longUsage () int longUsage (std::string &outs)
{ {
std::string shortUsageString;
std::stringstream out; std::stringstream out;
out << shortUsage ()
(void)shortUsage(shortUsageString);
out << shortUsageString
<< "ID is the numeric identifier displayed by the 'task list' command. " << "ID is the numeric identifier displayed by the 'task list' command. "
<< "You can specify multiple IDs for task commands, and multiple tasks " << "You can specify multiple IDs for task commands, and multiple tasks "
<< "will be affected. To specify multiple IDs make sure you use one " << "will be affected. To specify multiple IDs make sure you use one "
@@ -276,13 +281,15 @@ std::string longUsage ()
<< " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~" << "\n" << " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~" << "\n"
<< std::endl; << std::endl;
return out.str (); outs = out.str();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Display all information for the given task. // Display all information for the given task.
std::string handleInfo () int handleInfo (std::string &outs)
{ {
int rc = 0;
// Get all the tasks. // Get all the tasks.
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
@@ -497,18 +504,22 @@ std::string handleInfo ()
<< std::endl; << std::endl;
} }
if (! tasks.size ()) if (! tasks.size ()) {
out << "No matches." << std::endl; out << "No matches." << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Project Remaining Avg Age Complete 0% 100% // Project Remaining Avg Age Complete 0% 100%
// A 12 13d 55% XXXXXXXXXXXXX----------- // A 12 13d 55% XXXXXXXXXXXXX-----------
// B 109 3d 12h 10% XXX--------------------- // B 109 3d 12h 10% XXX---------------------
std::string handleReportSummary () int handleReportSummary (std::string &outs)
{ {
int rc = 0;
// Scan the pending tasks. // Scan the pending tasks.
std::vector <Task> tasks; std::vector <Task> tasks;
context.tdb.lock (context.config.get ("locking", true)); context.tdb.lock (context.config.get ("locking", true));
@@ -648,10 +659,13 @@ std::string handleReportSummary ()
<< table.rowCount () << table.rowCount ()
<< (table.rowCount () == 1 ? " project" : " projects") << (table.rowCount () == 1 ? " project" : " projects")
<< std::endl; << std::endl;
else else {
out << "No projects." << std::endl; out << "No projects." << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -673,7 +687,7 @@ std::string handleReportSummary ()
// //
// Make the "three" tasks a configurable number // Make the "three" tasks a configurable number
// //
std::string handleReportNext () int handleReportNext (std::string &outs)
{ {
// Load report configuration. // Load report configuration.
std::string columnList = context.config.get ("report.next.columns"); std::string columnList = context.config.get ("report.next.columns");
@@ -718,7 +732,8 @@ std::string handleReportNext ()
labelList, labelList,
sortList, sortList,
filterList, filterList,
tasks); tasks,
outs);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -744,8 +759,9 @@ time_t monthlyEpoch (const std::string& date)
return 0; return 0;
} }
std::string handleReportHistory () int handleReportHistory (std::string &outs)
{ {
int rc = 0;
std::map <time_t, int> groups; // Represents any month with data std::map <time_t, int> groups; // Represents any month with data
std::map <time_t, int> addedGroup; // Additions by month std::map <time_t, int> addedGroup; // Additions by month
std::map <time_t, int> completedGroup; // Completions by month std::map <time_t, int> completedGroup; // Completions by month
@@ -891,15 +907,19 @@ std::string handleReportHistory ()
out << optionalBlankLine () out << optionalBlankLine ()
<< table.render () << table.render ()
<< std::endl; << std::endl;
else else {
out << "No tasks." << std::endl; out << "No tasks." << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleReportGHistory () int handleReportGHistory (std::string &outs)
{ {
int rc = 0;
std::map <time_t, int> groups; // Represents any month with data std::map <time_t, int> groups; // Represents any month with data
std::map <time_t, int> addedGroup; // Additions by month std::map <time_t, int> addedGroup; // Additions by month
std::map <time_t, int> completedGroup; // Completions by month std::map <time_t, int> completedGroup; // Completions by month
@@ -1087,14 +1107,17 @@ std::string handleReportGHistory ()
else else
out << "Legend: + added, X completed, - deleted" << std::endl; out << "Legend: + added, X completed, - deleted" << std::endl;
} }
else else {
out << "No tasks." << std::endl; out << "No tasks." << std::endl;
rc = 1;
}
return out.str (); outs = out.str ();
return rc;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleReportTimesheet () int handleReportTimesheet (std::string &outs)
{ {
// Scan the pending tasks. // Scan the pending tasks.
std::vector <Task> tasks; std::vector <Task> tasks;
@@ -1264,7 +1287,8 @@ std::string handleReportTimesheet ()
end -= 7 * 86400; end -= 7 * 86400;
} }
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@@ -1432,7 +1456,7 @@ std::string renderMonths (
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleReportCalendar () int handleReportCalendar (std::string &outs)
{ {
// Each month requires 28 text columns width. See how many will actually // Each month requires 28 text columns width. See how many will actually
// fit. But if a preference is specified, and it fits, use it. // fit. But if a preference is specified, and it fits, use it.
@@ -1604,11 +1628,12 @@ std::string handleReportCalendar ()
<< optionalBlankLine () << optionalBlankLine ()
<< std::endl; << std::endl;
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
std::string handleReportStats () int handleReportStats (std::string &outs)
{ {
std::stringstream out; std::stringstream out;
@@ -1836,7 +1861,8 @@ std::string handleReportStats ()
<< table.render () << table.render ()
<< optionalBlankLine (); << optionalBlankLine ();
return out.str (); outs = out.str ();
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

62
src/tests/feature.exit.t Executable file
View File

@@ -0,0 +1,62 @@
#! /usr/bin/perl
################################################################################
## task - a command line task list manager.
##
## Copyright 2006 - 2009, Paul Beckingham.
## All rights reserved.
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 2 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
## FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, write to the
##
## Free Software Foundation, Inc.,
## 51 Franklin Street, Fifth Floor,
## Boston, MA
## 02110-1301
## USA
##
################################################################################
use strict;
use warnings;
use Test::More tests => 7;
# Create the rc file.
if (open my $fh, '>', 'exit.rc')
{
print $fh "data.location=.\n",
"confirmation=no\n";
close $fh;
ok (-r 'exit.rc', 'Created exit.rc');
}
qx{../task rc:exit.rc add foo};
my $exit_good = system ('../task rc:exit.rc ls foo 2>&1 >>/dev/null');
is ($exit_good, 0, 'task returns 0 on success');
my $exit_bad = system ('../task rc:exit.rc ls bar 2>&1 >>/dev/null');
isnt ($exit_bad, 0, 'task returns non-zero on failure');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'undo.data';
ok (!-r 'undo.data', 'Removed undo.data');
unlink 'exit.rc';
ok (!-r 'exit.rc', 'Removed exit.rc');
exit 0;