Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6c7236ff3 | ||
|
|
204d287b20 | ||
|
|
b3e3c36d50 | ||
|
|
e717345f20 | ||
|
|
3b354b6d47 | ||
|
|
2c0da35225 | ||
|
|
13955bc6ae | ||
|
|
1d80a2ebdc | ||
|
|
d4910f65eb | ||
|
|
2b44d513e8 | ||
|
|
7f11f1b560 | ||
|
|
b246fae889 | ||
|
|
8c5508de4b | ||
|
|
b3db2245fa | ||
|
|
00b246ce8a | ||
|
|
b94706c56e | ||
|
|
8d784da0ae | ||
|
|
b5f65850f8 | ||
|
|
b7726bce21 | ||
|
|
d44e9363f0 | ||
|
|
549e700bc8 | ||
|
|
b2fc4969b9 | ||
|
|
331b08055a | ||
|
|
847a8b6d49 | ||
|
|
12c4983936 | ||
|
|
39d9f235de | ||
|
|
7aa0c3698a | ||
|
|
bc40ab63b3 | ||
|
|
6e673d2834 | ||
|
|
30c6dd0047 | ||
|
|
64bc2a165a | ||
|
|
5b96dbbce8 | ||
|
|
77dd930574 | ||
|
|
f6842941f3 | ||
|
|
e2e0851a69 | ||
|
|
1299fe468b | ||
|
|
de50b2902c | ||
|
|
bcdcbeeea0 | ||
|
|
469cafa053 | ||
|
|
fdb359c180 | ||
|
|
b3d40b2554 | ||
|
|
fba076a0d0 | ||
|
|
09d7940dd3 | ||
|
|
00031dc1ab | ||
|
|
3ef844de5f | ||
|
|
bb45d91ddb | ||
|
|
f243f0ed44 | ||
|
|
b305cc0a60 | ||
|
|
53609b2837 | ||
|
|
bdaa0f89d9 | ||
|
|
2fd8d8aa83 | ||
|
|
e69fb81b2b | ||
|
|
d2aa0f31b0 | ||
|
|
05f67db429 | ||
|
|
62be3f8acb | ||
|
|
cc5c99c0a1 |
4
AUTHORS
4
AUTHORS
@@ -18,6 +18,7 @@ The following submitted code, packages or analysis, and deserve special thanks:
|
||||
Johan Friis
|
||||
Steven de Brouwer
|
||||
Pietro Cerutti
|
||||
Cory Donnelly
|
||||
|
||||
Thanks to the following, who submitted detailed bug reports and excellent suggestions:
|
||||
Eugene Kramer
|
||||
@@ -38,4 +39,7 @@ Thanks to the following, who submitted detailed bug reports and excellent sugges
|
||||
Mike Adonay
|
||||
Thomas@BIC
|
||||
Ian Mortimer
|
||||
Zach Frazier
|
||||
Joe Pulliam
|
||||
Juergen Daubert
|
||||
|
||||
|
||||
63
ChangeLog
63
ChangeLog
@@ -1,7 +1,66 @@
|
||||
|
||||
------ current release ---------------------------
|
||||
|
||||
1.8.1 (8/20/2009)
|
||||
1.8.5 (12/05/2009)
|
||||
+ Added feature to allow the user to quit when asked to confirm multiple
|
||||
changes. Now task asks "Proceed with change? (Yes/no/all/quit)".
|
||||
+ Added feature #341 that makes explicit references to the task and taskrc
|
||||
man pages, both in the auto-generated .taskrc file and the version command
|
||||
output (thanks to Cory Donnelly).
|
||||
+ Added feature - #310 that simplified and make clearer an error message
|
||||
that complained about things that were beyond user control (thanks to
|
||||
John Florian).
|
||||
+ Fixed bug that was causing the 'completed' report to sort incorrectly.
|
||||
+ Fixed bug #321 where all shell input was converted to lower case (thanks
|
||||
to Juergen Daubert).
|
||||
+ Fixed bug #327 that allowed the removal of a due date from a recurring
|
||||
task.
|
||||
+ Fixed bug #317 which colored tasks in the 'completed' report according
|
||||
to due dates, which are no longer relevant to a completed task (thanks
|
||||
to Cory Donnelly).
|
||||
+ Fixed bug that was causing the 'completed' report to sort incorrectly.
|
||||
+ Fixed bug #322 which failed to propagate rc overrides to shell commands.
|
||||
+ Fixed redundant messages when exiting shell mode.
|
||||
+ Fixed bug #333 which failed to display the ID of a duplicated task (thanks
|
||||
to Cory Donnelly).
|
||||
+ Fixed bug #332 where task complained that the 'recur_ind' custom report
|
||||
column was invalid. It was misnamed in the documentation, which should
|
||||
have read 'recurrence_indicator'. Also, the 'tag_indicator' column was
|
||||
not mentioned anywhere (thanks to T. Charles Yun).
|
||||
+ Fixed bug #319 that caused task to not properly detect the removal of a
|
||||
tag when obtaining confirmation from the user fora bulk modification
|
||||
(thanks to Cory Donnelly).
|
||||
|
||||
------ old releases ------------------------------
|
||||
|
||||
1.8.4 (11/17/2009) 12c4983936d27317df100f05da8244139dd06a3f
|
||||
+ Fixed bug that caused wait: dates to not be properly rendered in a
|
||||
readable and preferred format with the "edit" command.
|
||||
+ Fixed bug that caused a hang on cygwin, when a task with multiple
|
||||
annotations was edited (thanks to Joe Pulliam).
|
||||
+ Fixed bug #314 where the edit command fails when data.location includes
|
||||
directories containing spaces (thanks to Cory Donnelly).
|
||||
+ Added a warning (issue #312) when modifying recurring tasks, that all
|
||||
instances of that task may be modified. When task confirms a bulk edit
|
||||
the recurrence is again indicated (thanks to Cory Donnelly).
|
||||
|
||||
1.8.3 (10/21/2009) bcdcbeeea0d92f21c3565aebfaf6332b959f4025
|
||||
+ Added support for Haiku R1/alpha1
|
||||
|
||||
1.8.2 (9/7/2009) f243f0ed443ecd7dde779de8a6525222591024db
|
||||
+ 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).
|
||||
+ Fixed bug #287 that causes color control codes to be written to shadow
|
||||
files (thanks to Richard Querin).
|
||||
+ Fixed bug #289 which imported task from todo.sh without valid uuids
|
||||
(thanks to Ben Jackson).
|
||||
+ Fixed bug #291 which generated a false warning about an unrecognized
|
||||
variable when enabling default.projects in .taskrc (thanks to Thomas@BIC).
|
||||
+ Fixed bug #288 which failed to propagate rc file overrides on the command
|
||||
line to the default command (thanks to Zach Frazier).
|
||||
|
||||
1.8.1 (8/20/2009) 35792e7874d2bb664abb1a0a67960b7fe7e0fccf
|
||||
+ Fixed bug #231 that broke the build on OpenBSD 32-bit due to a time_t
|
||||
and int collision (thanks to Pietro Cerutti).
|
||||
+ Fixed bug #241 that prevented bash's tab-completion of projects in Fedora
|
||||
@@ -34,8 +93,6 @@
|
||||
+ Fixed bug that failed to suppress color control code in the header and footnote
|
||||
when redirecting output to a file (thanks to John Florian).
|
||||
|
||||
------ old releases ------------------------------
|
||||
|
||||
1.8.0 (7/21/2009) 14977ef317bd004dae2f2c313e806af9f2a2140c
|
||||
+ Added zsh tab completion script (thanks to P.C. Shyamshankar).
|
||||
+ Fixed bug that cause the _forcecolor configuration variable to be
|
||||
|
||||
28
NEWS
28
NEWS
@@ -9,26 +9,26 @@ New Features in task 1.8
|
||||
- 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
|
||||
- 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
|
||||
list here.
|
||||
|
||||
Task has been built and tested on the following configurations:
|
||||
|
||||
- OS X 10.5 Leopard
|
||||
- OS X 10.4 Tiger
|
||||
- Fedora Core 11 Leonidas
|
||||
- Fedora Core 10 Cambridge
|
||||
- Ubuntu 9.04 Jaunty Jackalope
|
||||
- Ubuntu 8.10 Intrepid Ibex
|
||||
- Ubuntu 8.04 Hardy Heron
|
||||
- Slackware 12.2
|
||||
- Arch Linux
|
||||
- Solaris 10
|
||||
- Solaris 8
|
||||
- OpenBSD 4.5
|
||||
- FreeBSD
|
||||
- Cygwin 1.5.25-14
|
||||
* OS X 10.6 Snow Leopard and 10.5 Leopard
|
||||
* Fedora 12 Constantine and 11 Leonidas
|
||||
* Ubuntu 9.10 Karmic Koala and 9.04 Jaunty Jackalope
|
||||
* Slackware 12.2
|
||||
* Arch Linux
|
||||
* Gentoo Linux
|
||||
* SliTaz Linux
|
||||
* CRUX Linux
|
||||
* Solaris 10 and 8
|
||||
* OpenBSD 4.5
|
||||
* FreeBSD
|
||||
* Cygwin 1.5
|
||||
* Haiku R1/alpha1
|
||||
|
||||
While Task has undergone testing, bugs are sure to remain. If you encounter a
|
||||
bug, please enter a new issue at:
|
||||
|
||||
4
README
4
README
@@ -12,8 +12,8 @@ At the site you'll find a wiki, discussion forums, downloads, news and more.
|
||||
|
||||
|
||||
Your contributions are especially welcome. Whether it comes in the form of
|
||||
code patches, ideas, discussion, bug reports or just encouragement, your input
|
||||
is needed.
|
||||
code patches, ideas, discussion, bug reports, encouragement or criticism, your
|
||||
input is needed.
|
||||
|
||||
Please send your support questions and code patches to:
|
||||
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT(task, 1.8.1, support@taskwarrior.org)
|
||||
AC_INIT(task, 1.8.5, support@taskwarrior.org)
|
||||
|
||||
CFLAGS="${CFLAGS=}"
|
||||
CXXFLAGS="${CXXFLAGS=}"
|
||||
|
||||
# this macro is used to get the arguments supplied
|
||||
# to the configure script (./configure --enable-debug)
|
||||
# Check if we have enable debug support.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH task-tutorial 5 2009-08-14 "task 1.8.1" "User Manuals"
|
||||
.TH task-tutorial 5 2009-11-18 "task 1.8.5" "User Manuals"
|
||||
|
||||
.SH NAME
|
||||
task-tutorial \- A tutorial for the task(1) command line todo manager.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH task 1 2009-08-14 "task 1.8.1" "User Manuals"
|
||||
.TH task 1 2009-11-18 "task 1.8.5" "User Manuals"
|
||||
|
||||
.SH NAME
|
||||
task \- A command line todo manager.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.TH taskrc 5 2009-08-14 "task 1.8.1" "User Manuals"
|
||||
.TH taskrc 5 2009-11-18 "task 1.8.5" "User Manuals"
|
||||
|
||||
.SH NAME
|
||||
taskrc \- Configuration file for the task(1) command
|
||||
@@ -384,8 +384,9 @@ The description for report X when running the "task help" command.
|
||||
.TP
|
||||
.B report.X.columns
|
||||
The columns that will be used when generating the report X. Valid columns are:
|
||||
id, uuid, project, priority, entry, start, due, recur, recur_ind, age, age_compact,
|
||||
active, tags, description, description_only. The IDs are separated by commas.
|
||||
id, uuid, project, priority, entry, start, due, recur, recur_indicator, age,
|
||||
age_compact, active, tags, tag_indicator, description, description_only.
|
||||
The IDs are separated by commas.
|
||||
|
||||
.TP
|
||||
.B report.X.labels
|
||||
|
||||
@@ -1 +1 @@
|
||||
<pkg-contents spec="1.12"><f n="task" o="paul" g="staff" p="33261" pt="/Users/paul/task.git/package-config/osx/binary/task" m="false" t="file"/></pkg-contents>
|
||||
<pkg-contents spec="1.12"><f n="task" o="root" g="wheel" p="33261" pt="/Users/paul/task.git/package-config/osx/binary/task" m="false" t="file"><mod>group</mod><mod>owner</mod></f></pkg-contents>
|
||||
@@ -1 +1 @@
|
||||
<pkgref spec="1.12" uuid="C71026FD-E252-42CD-89C3-2F6F087AAF17"><config><identifier>com.beckingham.task180.task.pkg</identifier><version>1.8.0</version><description></description><post-install type="none"/><requireAuthorization/><installFrom mod="true">/Users/paul/task.git/package-config/osx/binary/task</installFrom><installTo mod="true" relocatable="true">/usr/local/bin</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>parent</mod><mod>locationType</mod><mod>relocatable</mod><mod>version</mod><mod>installTo.path</mod><mod>installTo</mod></config><contents><file-list>01task-contents.xml</file-list><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents></pkgref>
|
||||
<pkgref spec="1.12" uuid="C71026FD-E252-42CD-89C3-2F6F087AAF17"><config><identifier>com.beckingham.task182.task.pkg</identifier><version>1.8.2</version><description></description><post-install type="none"/><requireAuthorization/><installFrom mod="true">/Users/paul/task.git/package-config/osx/binary/task</installFrom><installTo mod="true" relocatable="true">/usr/local/bin</installTo><flags><followSymbolicLinks/></flags><packageStore type="internal"></packageStore><mod>installTo</mod><mod>locationType</mod><mod>relocatable</mod><mod>identifier</mod><mod>parent</mod><mod>version</mod><mod>installTo.path</mod></config><contents><file-list>01task-contents.xml</file-list><filter>/CVS$</filter><filter>/\.svn$</filter><filter>/\.cvsignore$</filter><filter>/\.cvspass$</filter><filter>/\.DS_Store$</filter></contents></pkgref>
|
||||
@@ -1 +1 @@
|
||||
<pkmkdoc spec="1.12"><properties><title>Task 1.8.0</title><build>/Users/paul/Desktop/task-1.8.0.pkg</build><organization>com.beckingham</organization><userSees ui="easy"/><min-target os="3"/><domain anywhere="true" system="true"/></properties><distribution><versions min-spec="1.000000"/><scripts></scripts></distribution><contents><choice title="task" id="choice0" starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="com.beckingham.task180.task.pkg"/><choice-reqs><requirement id="tosv" operator="ge" value="'10.5.0'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"/></choice-reqs></choice></contents><resources bg-scale="proportional" bg-align="center"><locale lang="en"><resource mod="true" type="license">/Users/paul/task.git/package-config/osx/binary/COPYING.txt</resource><resource mod="true" type="readme">/Users/paul/task.git/package-config/osx/binary/README.txt</resource></locale></resources><flags/><item type="file">01task.xml</item><mod>properties.title</mod><mod>properties.anywhereDomain</mod><mod>properties.systemDomain</mod></pkmkdoc>
|
||||
<pkmkdoc spec="1.12"><properties><title>Task 1.8.2</title><build>/Users/paul/Desktop/task-1.8.2.pkg</build><organization>com.beckingham</organization><userSees ui="both"/><min-target os="3"/><domain anywhere="true" system="true"/></properties><distribution><versions min-spec="1.000000"/><scripts></scripts></distribution><contents><choice title="task" id="choice0" starts_selected="true" starts_enabled="true" starts_hidden="false"><pkgref id="com.beckingham.task182.task.pkg"/><choice-reqs><requirement id="tosv" operator="ge" value="'10.6.0'" selected="no" enabled="no" hidden="unchanged" startSelected="unchanged" startEnabled="unchanged" startHidden="unchanged"/></choice-reqs></choice></contents><resources bg-scale="proportional" bg-align="center"><locale lang="en"><resource mod="true" type="license">/Users/paul/task.git/package-config/osx/binary/COPYING.txt</resource><resource mod="true" type="readme">/Users/paul/task.git/package-config/osx/binary/README.txt</resource></locale></resources><flags/><item type="file">01task.xml</item><mod>properties.title</mod><mod>properties.customizeOption</mod><mod>properties.anywhereDomain</mod><mod>properties.systemDomain</mod></pkmkdoc>
|
||||
@@ -100,7 +100,7 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data)
|
||||
// Create a sample .taskrc file.
|
||||
std::stringstream contents;
|
||||
contents << "# Task program configuration file.\n"
|
||||
<< "# For more documentation, see http://taskwarrior.org\n"
|
||||
<< "# For more documentation, see http://taskwarrior.org or try 'man task' and 'man taskrc'\n"
|
||||
<< "\n"
|
||||
<< "# Files\n"
|
||||
<< "data.location=" << data << "\n"
|
||||
@@ -150,8 +150,8 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data)
|
||||
<< "#default.priority=M # Unless otherwise specified\n"
|
||||
<< "default.command=list # Unless otherwise specified\n"
|
||||
<< "\n"
|
||||
<< "# Fields: id,uuid,project,priority,entry,start,due,recur,recur_ind,age,\n"
|
||||
<< "# age_compact,active,tags,description,description_only\n"
|
||||
<< "# Fields: id,uuid,project,priority,entry,start,due,recur,recurrence_indicator,age,\n"
|
||||
<< "# age_compact,active,tags,tag_indicator,description,description_only\n"
|
||||
<< "# Description: This report is ...\n"
|
||||
<< "# Sort: due+,priority-,project+\n"
|
||||
<< "# Filter: pro:x pri:H +bug limit:10\n"
|
||||
|
||||
115
src/Context.cpp
115
src/Context.cpp
@@ -30,6 +30,7 @@
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "Context.h"
|
||||
#include "Timer.h"
|
||||
#include "text.h"
|
||||
@@ -49,6 +50,8 @@ Context::Context ()
|
||||
, tdb ()
|
||||
, stringtable ()
|
||||
, program ("")
|
||||
, file_override ("")
|
||||
, var_overrides ("")
|
||||
, cmd ()
|
||||
, inShadow (false)
|
||||
{
|
||||
@@ -64,6 +67,7 @@ void Context::initialize (int argc, char** argv)
|
||||
{
|
||||
// Capture the args.
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
program = argv[i];
|
||||
@@ -74,6 +78,7 @@ void Context::initialize (int argc, char** argv)
|
||||
}
|
||||
else
|
||||
args.push_back (argv[i]);
|
||||
}
|
||||
|
||||
initialize ();
|
||||
}
|
||||
@@ -126,23 +131,26 @@ void Context::initialize ()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int Context::run ()
|
||||
{
|
||||
int rc;
|
||||
Timer t ("Context::run");
|
||||
|
||||
std::string output;
|
||||
try
|
||||
{
|
||||
parse (); // Parse command line.
|
||||
output = dispatch (); // Dispatch to command handlers.
|
||||
parse (); // Parse command line.
|
||||
rc = dispatch (output); // Dispatch to command handlers.
|
||||
}
|
||||
|
||||
catch (const std::string& error)
|
||||
{
|
||||
footnote (error);
|
||||
rc = 2;
|
||||
}
|
||||
|
||||
catch (...)
|
||||
{
|
||||
footnote (stringtable.get (100, "Unknown error."));
|
||||
rc = 3;
|
||||
}
|
||||
|
||||
// Dump all debug messages.
|
||||
@@ -170,63 +178,63 @@ int Context::run ()
|
||||
else
|
||||
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");
|
||||
|
||||
// TODO Just look at this thing. It cries out for a dispatch table.
|
||||
std::string out;
|
||||
if (cmd.command == "projects") { out = handleProjects (); }
|
||||
else if (cmd.command == "tags") { out = handleTags (); }
|
||||
else if (cmd.command == "colors") { out = handleColor (); }
|
||||
else if (cmd.command == "version") { out = handleVersion (); }
|
||||
else if (cmd.command == "help") { out = longUsage (); }
|
||||
else if (cmd.command == "stats") { out = handleReportStats (); }
|
||||
else if (cmd.command == "info") { out = handleInfo (); }
|
||||
else if (cmd.command == "history") { out = handleReportHistory (); }
|
||||
else if (cmd.command == "ghistory") { out = handleReportGHistory (); }
|
||||
else if (cmd.command == "summary") { out = handleReportSummary (); }
|
||||
else if (cmd.command == "calendar") { out = handleReportCalendar (); }
|
||||
else if (cmd.command == "timesheet") { out = handleReportTimesheet (); }
|
||||
else if (cmd.command == "add") { out = handleAdd (); }
|
||||
else if (cmd.command == "append") { out = handleAppend (); }
|
||||
else if (cmd.command == "annotate") { out = handleAnnotate (); }
|
||||
else if (cmd.command == "done") { out = handleDone (); }
|
||||
else if (cmd.command == "delete") { out = handleDelete (); }
|
||||
else if (cmd.command == "start") { out = handleStart (); }
|
||||
else if (cmd.command == "stop") { out = handleStop (); }
|
||||
else if (cmd.command == "export") { out = handleExport (); }
|
||||
else if (cmd.command == "import") { out = handleImport (); }
|
||||
else if (cmd.command == "duplicate") { out = handleDuplicate (); }
|
||||
else if (cmd.command == "edit") { out = handleEdit (); }
|
||||
if (cmd.command == "projects") { rc = handleProjects (out); }
|
||||
else if (cmd.command == "tags") { rc = handleTags (out); }
|
||||
else if (cmd.command == "colors") { rc = handleColor (out); }
|
||||
else if (cmd.command == "version") { rc = handleVersion (out); }
|
||||
else if (cmd.command == "help") { rc = longUsage (out); }
|
||||
else if (cmd.command == "stats") { rc = handleReportStats (out); }
|
||||
else if (cmd.command == "info") { rc = handleInfo (out); }
|
||||
else if (cmd.command == "history") { rc = handleReportHistory (out); }
|
||||
else if (cmd.command == "ghistory") { rc = handleReportGHistory (out); }
|
||||
else if (cmd.command == "summary") { rc = handleReportSummary (out); }
|
||||
else if (cmd.command == "calendar") { rc = handleReportCalendar (out); }
|
||||
else if (cmd.command == "timesheet") { rc = handleReportTimesheet (out); }
|
||||
else if (cmd.command == "add") { rc = handleAdd (out); }
|
||||
else if (cmd.command == "append") { rc = handleAppend (out); }
|
||||
else if (cmd.command == "annotate") { rc = handleAnnotate (out); }
|
||||
else if (cmd.command == "done") { rc = handleDone (out); }
|
||||
else if (cmd.command == "delete") { rc = handleDelete (out); }
|
||||
else if (cmd.command == "start") { rc = handleStart (out); }
|
||||
else if (cmd.command == "stop") { rc = handleStop (out); }
|
||||
else if (cmd.command == "export") { rc = handleExport (out); }
|
||||
else if (cmd.command == "import") { rc = handleImport (out); }
|
||||
else if (cmd.command == "duplicate") { rc = handleDuplicate (out); }
|
||||
else if (cmd.command == "edit") { rc = handleEdit (out); }
|
||||
#ifdef FEATURE_SHELL
|
||||
else if (cmd.command == "shell") { handleShell (); }
|
||||
else if (cmd.command == "shell") { handleShell ( ); }
|
||||
#endif
|
||||
else if (cmd.command == "undo") { handleUndo (); }
|
||||
else if (cmd.command == "_projects") { out = handleCompletionProjects (); }
|
||||
else if (cmd.command == "_tags") { out = handleCompletionTags (); }
|
||||
else if (cmd.command == "_commands") { out = handleCompletionCommands (); }
|
||||
else if (cmd.command == "_ids") { out = handleCompletionIDs (); }
|
||||
else if (cmd.command == "_config") { out = handleCompletionConfig (); }
|
||||
else if (cmd.command == "undo") { handleUndo ( ); }
|
||||
else if (cmd.command == "_projects") { rc = handleCompletionProjects (out); }
|
||||
else if (cmd.command == "_tags") { rc = handleCompletionTags (out); }
|
||||
else if (cmd.command == "_commands") { rc = handleCompletionCommands (out); }
|
||||
else if (cmd.command == "_ids") { rc = handleCompletionIDs (out); }
|
||||
else if (cmd.command == "_config") { rc = handleCompletionConfig (out); }
|
||||
else if (cmd.command == "" &&
|
||||
sequence.size ()) { out = handleModify (); }
|
||||
sequence.size ()) { rc = handleModify (out); }
|
||||
|
||||
// Command that display IDs and therefore need TDB::gc first.
|
||||
else if (cmd.command == "next") { if (!inShadow) tdb.gc (); out = handleReportNext (); }
|
||||
else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); out = handleCustomReport (cmd.command); }
|
||||
else if (cmd.command == "next") { if (!inShadow) tdb.gc (); rc = handleReportNext (out); }
|
||||
else if (cmd.validCustom (cmd.command)) { if (!inShadow) tdb.gc (); rc = handleCustomReport (cmd.command, out); }
|
||||
|
||||
// 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),
|
||||
if (cmd.isWriteCommand () && !inShadow)
|
||||
shadow ();
|
||||
|
||||
return out;
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -252,8 +260,6 @@ void Context::shadow ()
|
||||
|
||||
std::string oldCurses = config.get ("curses");
|
||||
std::string oldColor = config.get ("color");
|
||||
config.set ("curses", "off");
|
||||
config.set ("color", "off");
|
||||
|
||||
clear ();
|
||||
|
||||
@@ -264,8 +270,12 @@ void Context::shadow ()
|
||||
split (args, command, ' ');
|
||||
|
||||
initialize ();
|
||||
config.set ("curses", "off");
|
||||
config.set ("color", "off");
|
||||
|
||||
parse ();
|
||||
std::string result = dispatch ();
|
||||
std::string result;
|
||||
(void)dispatch (result);
|
||||
std::ofstream out (shadowFile.c_str ());
|
||||
if (out.good ())
|
||||
{
|
||||
@@ -343,13 +353,14 @@ void Context::loadCorrectConfigFile ()
|
||||
std::string rc = home + "/.taskrc";
|
||||
std::string data = home + "/.task";
|
||||
|
||||
// Is there an override for rc?
|
||||
// Is there an file_override for rc:?
|
||||
foreach (arg, args)
|
||||
{
|
||||
if (*arg == "--")
|
||||
break;
|
||||
else if (arg->substr (0, 3) == "rc:")
|
||||
{
|
||||
file_override = *arg;
|
||||
rc = arg->substr (3, std::string::npos);
|
||||
|
||||
home = rc;
|
||||
@@ -373,7 +384,7 @@ void Context::loadCorrectConfigFile ()
|
||||
if (config.get ("data.location") != "")
|
||||
data = config.get ("data.location");
|
||||
|
||||
// Is there an override for data?
|
||||
// Are there any var_overrides for data.location?
|
||||
foreach (arg, args)
|
||||
{
|
||||
if (*arg == "--")
|
||||
@@ -433,6 +444,7 @@ void Context::loadCorrectConfigFile ()
|
||||
n.getUntilEOS (value))
|
||||
{
|
||||
config.set (name, value);
|
||||
var_overrides += " " + *arg;
|
||||
footnote (std::string ("Configuration override ") + // TODO i18n
|
||||
arg->substr (3, std::string::npos));
|
||||
}
|
||||
@@ -649,15 +661,22 @@ void Context::parse (
|
||||
// then invoke the default command.
|
||||
if (parseCmd.command == "" && parseArgs.size () == 0)
|
||||
{
|
||||
// Apply overrides, if any.
|
||||
std::string defaultCommand = config.get ("default.command");
|
||||
if (defaultCommand != "")
|
||||
{
|
||||
// Add on the overrides.
|
||||
defaultCommand += " " + file_override + " " + var_overrides;
|
||||
|
||||
// Stuff the command line.
|
||||
parseArgs.clear ();
|
||||
split (parseArgs, defaultCommand, ' ');
|
||||
args.clear ();
|
||||
split (args, defaultCommand, ' ');
|
||||
header ("[task " + defaultCommand + "]");
|
||||
|
||||
// Reinitialize the context and recurse.
|
||||
file_override = "";
|
||||
var_overrides = "";
|
||||
footnotes.clear ();
|
||||
initialize ();
|
||||
parse (args, cmd, task, sequence, subst, filter);
|
||||
}
|
||||
@@ -682,6 +701,8 @@ void Context::clear ()
|
||||
// stringtable.clear ();
|
||||
program = "";
|
||||
args.clear ();
|
||||
file_override = "";
|
||||
var_overrides = "";
|
||||
cmd.command = "";
|
||||
tagAdditions.clear ();
|
||||
tagRemovals.clear ();
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
void initialize (); // for reinitializing
|
||||
int run (); // task classic
|
||||
int interactive (); // task interactive (not implemented)
|
||||
std::string dispatch (); // command handler dispatch
|
||||
int dispatch (std::string&); // command handler dispatch
|
||||
void shadow (); // shadow file update
|
||||
|
||||
int getWidth (); // determine terminal width
|
||||
@@ -83,6 +83,8 @@ public:
|
||||
StringTable stringtable;
|
||||
std::string program;
|
||||
std::vector <std::string> args;
|
||||
std::string file_override;
|
||||
std::string var_overrides;
|
||||
Cmd cmd;
|
||||
std::map <std::string, std::string> aliases;
|
||||
std::vector <std::string> tagAdditions;
|
||||
|
||||
29
src/Date.cpp
29
src/Date.cpp
@@ -29,6 +29,7 @@
|
||||
#include <time.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include "Date.h"
|
||||
#include "text.h"
|
||||
#include "util.h"
|
||||
@@ -82,14 +83,14 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
// Single or double digit.
|
||||
case 'm':
|
||||
if (i >= mdy.length () ||
|
||||
! ::isdigit (mdy[i]))
|
||||
! isdigit (mdy[i]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
|
||||
if (i + 1 < mdy.length () &&
|
||||
(mdy[i + 0] == '0' || mdy[i + 0] == '1') &&
|
||||
::isdigit (mdy[i + 1]))
|
||||
isdigit (mdy[i + 1]))
|
||||
{
|
||||
month = ::atoi (mdy.substr (i, 2).c_str ());
|
||||
i += 2;
|
||||
@@ -103,14 +104,14 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
|
||||
case 'd':
|
||||
if (i >= mdy.length () ||
|
||||
! ::isdigit (mdy[i]))
|
||||
! isdigit (mdy[i]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
|
||||
if (i + 1 < mdy.length () &&
|
||||
(mdy[i + 0] == '0' || mdy[i + 0] == '1' || mdy[i + 0] == '2' || mdy[i + 0] == '3') &&
|
||||
::isdigit (mdy[i + 1]))
|
||||
isdigit (mdy[i + 1]))
|
||||
{
|
||||
day = ::atoi (mdy.substr (i, 2).c_str ());
|
||||
i += 2;
|
||||
@@ -125,8 +126,8 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
// Double digit.
|
||||
case 'y':
|
||||
if (i + 1 >= mdy.length () ||
|
||||
! ::isdigit (mdy[i + 0]) ||
|
||||
! ::isdigit (mdy[i + 1]))
|
||||
! isdigit (mdy[i + 0]) ||
|
||||
! isdigit (mdy[i + 1]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
@@ -137,8 +138,8 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
|
||||
case 'M':
|
||||
if (i + 1 >= mdy.length () ||
|
||||
! ::isdigit (mdy[i + 0]) ||
|
||||
! ::isdigit (mdy[i + 1]))
|
||||
! isdigit (mdy[i + 0]) ||
|
||||
! isdigit (mdy[i + 1]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
@@ -149,8 +150,8 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
|
||||
case 'D':
|
||||
if (i + 1 >= mdy.length () ||
|
||||
! ::isdigit (mdy[i + 0]) ||
|
||||
! ::isdigit (mdy[i + 1]))
|
||||
! isdigit (mdy[i + 0]) ||
|
||||
! isdigit (mdy[i + 1]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
@@ -162,10 +163,10 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */)
|
||||
// Quadruple digit.
|
||||
case 'Y':
|
||||
if (i + 3 >= mdy.length () ||
|
||||
! ::isdigit (mdy[i + 0]) ||
|
||||
! ::isdigit (mdy[i + 1]) ||
|
||||
! ::isdigit (mdy[i + 2]) ||
|
||||
! ::isdigit (mdy[i + 3]))
|
||||
! isdigit (mdy[i + 0]) ||
|
||||
! isdigit (mdy[i + 1]) ||
|
||||
! isdigit (mdy[i + 2]) ||
|
||||
! isdigit (mdy[i + 3]))
|
||||
{
|
||||
throw std::string ("\"") + mdy + "\" is not a valid date.";
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ bool Nibbler::getInt (int& result)
|
||||
++i;
|
||||
}
|
||||
|
||||
while (i < mInput.length () && ::isdigit (mInput[i]))
|
||||
while (i < mInput.length () && isdigit (mInput[i]))
|
||||
++i;
|
||||
|
||||
if (i > mCursor)
|
||||
@@ -260,7 +260,7 @@ bool Nibbler::getInt (int& result)
|
||||
bool Nibbler::getUnsignedInt (int& result)
|
||||
{
|
||||
std::string::size_type i = mCursor;
|
||||
while (i < mInput.length () && ::isdigit (mInput[i]))
|
||||
while (i < mInput.length () && isdigit (mInput[i]))
|
||||
++i;
|
||||
|
||||
if (i > mCursor)
|
||||
|
||||
@@ -37,6 +37,7 @@ extern Context context;
|
||||
Permission::Permission ()
|
||||
: needConfirmation (false)
|
||||
, allConfirmed (false)
|
||||
, quit (false)
|
||||
{
|
||||
// Turning confirmations off is the same as entering "all".
|
||||
if (context.config.get ("confirmation", true) == false)
|
||||
@@ -46,6 +47,9 @@ Permission::Permission ()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool Permission::confirmed (const Task& task, const std::string& question)
|
||||
{
|
||||
if (quit)
|
||||
return false;
|
||||
|
||||
if (!needConfirmation)
|
||||
return true;
|
||||
|
||||
@@ -57,16 +61,25 @@ bool Permission::confirmed (const Task& task, const std::string& question)
|
||||
<< task.id
|
||||
<< " \""
|
||||
<< task.get ("description")
|
||||
<< "\""
|
||||
<< std::endl;
|
||||
<< "\"";
|
||||
|
||||
int answer = confirm3 (question);
|
||||
if (task.getStatus () == Task::recurring ||
|
||||
task.has ("parent"))
|
||||
{
|
||||
std::cout << " (Recurring)";
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
|
||||
int answer = confirm4 (question);
|
||||
if (answer == 2)
|
||||
allConfirmed = true;
|
||||
|
||||
if (answer > 0)
|
||||
if (answer == 1 || answer == 2)
|
||||
return true;
|
||||
|
||||
if (answer == 3)
|
||||
quit = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
private:
|
||||
bool needConfirmation;
|
||||
bool allConfirmed;
|
||||
bool quit;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -153,7 +153,7 @@ bool Sequence::validId (const std::string& input) const
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < input.length (); ++i)
|
||||
if (!::isdigit (input[i]))
|
||||
if (!isdigit (input[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -876,7 +876,7 @@ void Table::sort (std::vector <int>& order)
|
||||
if (gap > 1)
|
||||
{
|
||||
gap = (int) ((float)gap / 1.3);
|
||||
if (gap == 10 or gap == 9)
|
||||
if (gap == 10 || gap == 9)
|
||||
gap = 11;
|
||||
}
|
||||
|
||||
|
||||
14
src/Task.cpp
14
src/Task.cpp
@@ -134,7 +134,7 @@ void Task::setEntry ()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Task::status Task::getStatus ()
|
||||
Task::status Task::getStatus () const
|
||||
{
|
||||
return textToStatus (get ("status")); // No i18n
|
||||
}
|
||||
@@ -441,10 +441,14 @@ void Task::addAnnotation (const std::string& description)
|
||||
void Task::removeAnnotations ()
|
||||
{
|
||||
// Erase old annotations.
|
||||
Record::iterator i;
|
||||
for (i = this->begin (); i != this->end (); ++i)
|
||||
Record::iterator i = this->begin ();
|
||||
while (i != this->end ())
|
||||
{
|
||||
if (i->first.substr (0, 11) == "annotation_") // No i18n
|
||||
this->erase (i);
|
||||
this->erase (i++);
|
||||
else
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -523,7 +527,7 @@ void Task::validate () const
|
||||
if (!has ("uuid") ||
|
||||
!has ("entry") ||
|
||||
!has ("description"))
|
||||
throw std::string ("A task must have a uuid, entry date and description in order to be valid."); // TODO i18n
|
||||
throw std::string ("A task must have a description in order to be valid."); // TODO i18n
|
||||
|
||||
if (get ("description") == "") // No i18n
|
||||
throw std::string ("Cannot add a task that is blank, or contains <CR> or <LF> characters."); // TODO i18n
|
||||
|
||||
@@ -57,7 +57,7 @@ public:
|
||||
|
||||
void setEntry ();
|
||||
|
||||
status getStatus ();
|
||||
status getStatus () const;
|
||||
void setStatus (status);
|
||||
|
||||
int getTagCount ();
|
||||
|
||||
@@ -52,7 +52,7 @@ Timer::~Timer ()
|
||||
<< mDescription
|
||||
<< " "
|
||||
<< std::setprecision (6)
|
||||
<< std::fixed
|
||||
// << std::fixed
|
||||
<< ((end.tv_sec - mStart.tv_sec) + ((end.tv_usec - mStart.tv_usec )
|
||||
/ 1000000.0))
|
||||
<< " sec";
|
||||
|
||||
197
src/command.cpp
197
src/command.cpp
@@ -49,7 +49,7 @@
|
||||
extern Context context;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleAdd ()
|
||||
int handleAdd (std::string &outs)
|
||||
{
|
||||
std::stringstream out;
|
||||
|
||||
@@ -110,12 +110,14 @@ std::string handleAdd ()
|
||||
context.tdb.commit ();
|
||||
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;
|
||||
|
||||
context.filter.push_back (Att ("status", "pending"));
|
||||
@@ -195,15 +197,18 @@ std::string handleProjects ()
|
||||
<< " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
else {
|
||||
out << "No projects."
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleCompletionProjects ()
|
||||
int handleCompletionProjects (std::string &outs)
|
||||
{
|
||||
std::vector <Task> tasks;
|
||||
context.tdb.lock (context.config.get ("locking", true));
|
||||
@@ -228,12 +233,14 @@ std::string handleCompletionProjects ()
|
||||
if (project->first.length ())
|
||||
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;
|
||||
|
||||
context.filter.push_back (Att ("status", "pending"));
|
||||
@@ -290,15 +297,19 @@ std::string handleTags ()
|
||||
<< " (" << quantity << (quantity == 1 ? " task" : " tasks") << ")"
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
else {
|
||||
out << "No tags."
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleCompletionTags ()
|
||||
int handleCompletionTags (std::string &outs)
|
||||
{
|
||||
std::vector <Task> tasks;
|
||||
context.tdb.lock (context.config.get ("locking", true));
|
||||
@@ -328,11 +339,12 @@ std::string handleCompletionTags ()
|
||||
foreach (tag, unique)
|
||||
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;
|
||||
context.cmd.allCommands (commands);
|
||||
@@ -342,11 +354,12 @@ std::string handleCompletionCommands ()
|
||||
foreach (command, commands)
|
||||
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;
|
||||
context.config.all (configs);
|
||||
@@ -356,11 +369,12 @@ std::string handleCompletionConfig ()
|
||||
foreach (config, configs)
|
||||
out << *config << std::endl;
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleCompletionIDs ()
|
||||
int handleCompletionIDs (std::string &outs)
|
||||
{
|
||||
std::vector <Task> tasks;
|
||||
context.tdb.lock (context.config.get ("locking", true));
|
||||
@@ -381,7 +395,8 @@ std::string handleCompletionIDs ()
|
||||
foreach (id, ids)
|
||||
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;
|
||||
|
||||
// Create a table for the disclaimer.
|
||||
@@ -420,7 +436,7 @@ std::string handleVersion ()
|
||||
link.addCell (link.addRow (), 0,
|
||||
"See http://taskwarrior.org for the latest releases, online documentation "
|
||||
"and lively discussion. New releases containing fixes and enhancements "
|
||||
"are made frequently.");
|
||||
"are made frequently. Don't forget the man pages 'man task' and 'man taskrc'.");
|
||||
|
||||
std::vector <std::string> all;
|
||||
context.config.all (all);
|
||||
@@ -483,8 +499,8 @@ std::string handleVersion ()
|
||||
" blanklines bulk color color.active color.due color.overdue color.pri.H "
|
||||
"color.pri.L color.pri.M color.pri.none color.recurring color.tagged "
|
||||
"color.footnote color.header color.debug confirmation curses data.location "
|
||||
"dateformat debug default.command default.priority defaultwidth due locale "
|
||||
"displayweeknumber echo.command locking monthsperline nag next project "
|
||||
"dateformat debug default.command default.priority default.project defaultwidth "
|
||||
"due locale displayweeknumber echo.command locking monthsperline nag next project "
|
||||
"shadow.command shadow.file shadow.notify weekstart editor import.synonym.id "
|
||||
"import.synonym.uuid longversion complete.all.projects complete.all.tags "
|
||||
#ifdef FEATURE_SHELL
|
||||
@@ -524,7 +540,7 @@ std::string handleVersion ()
|
||||
if (unrecognized.size ())
|
||||
{
|
||||
out << "Your .taskrc file contains these unrecognized variables:"
|
||||
<< std::endl;
|
||||
<< std::endl;
|
||||
|
||||
foreach (i, unrecognized)
|
||||
out << " " << *i << std::endl;
|
||||
@@ -535,9 +551,11 @@ std::string handleVersion ()
|
||||
// Verify installation. This is mentioned in the documentation as the way to
|
||||
// ensure everything is properly installed.
|
||||
|
||||
if (all.size () == 0)
|
||||
if (all.size () == 0) {
|
||||
out << "Configuration error: .taskrc contains no entries"
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (context.config.get ("data.location") == "")
|
||||
@@ -551,12 +569,14 @@ std::string handleVersion ()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleDelete ()
|
||||
int handleDelete (std::string &outs)
|
||||
{
|
||||
int rc = 0;
|
||||
std::stringstream out;
|
||||
|
||||
context.disallowModification ();
|
||||
@@ -645,19 +665,23 @@ std::string handleDelete ()
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
out << "Task not deleted." << std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
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;
|
||||
|
||||
context.disallowModification ();
|
||||
@@ -699,18 +723,21 @@ std::string handleStart ()
|
||||
<< task->get ("description")
|
||||
<< "' already started."
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
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;
|
||||
|
||||
context.disallowModification ();
|
||||
@@ -746,18 +773,21 @@ std::string handleStop ()
|
||||
<< task->get ("description")
|
||||
<< "' not started."
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
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;
|
||||
std::stringstream out;
|
||||
|
||||
@@ -799,7 +829,7 @@ std::string handleDone ()
|
||||
|
||||
if (taskDiff (before, *task))
|
||||
{
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Are you sure?"))
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*task);
|
||||
|
||||
@@ -826,6 +856,7 @@ std::string handleDone ()
|
||||
<< task->get ("description")
|
||||
<< "' is not pending"
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
@@ -839,11 +870,12 @@ std::string handleDone ()
|
||||
<< " as done"
|
||||
<< std::endl;
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleExport ()
|
||||
int handleExport (std::string &outs)
|
||||
{
|
||||
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;
|
||||
std::stringstream out;
|
||||
@@ -917,6 +950,15 @@ std::string handleModify ()
|
||||
!task->has ("recur"))
|
||||
throw std::string ("You cannot specify an until date for a non-recurring task.");
|
||||
|
||||
if (task->has ("due") &&
|
||||
!context.task.has ("due") &&
|
||||
context.task.has ("recur"))
|
||||
throw std::string ("You cannot remove the due date from a recurring task.");
|
||||
|
||||
if (task->has ("recur") &&
|
||||
!context.task.has ("recur"))
|
||||
throw std::string ("You cannot remove the recurrence from a recurring task.");
|
||||
|
||||
// Make all changes.
|
||||
foreach (other, all)
|
||||
{
|
||||
@@ -925,6 +967,13 @@ std::string handleModify ()
|
||||
task->get ("parent") == other->get ("parent")) || // Sibling
|
||||
other->get ("uuid") == task->get ("parent")) // Parent
|
||||
{
|
||||
if (task->has ("parent"))
|
||||
std::cout << "Task "
|
||||
<< task->id
|
||||
<< " is a recurring task, and all other instances of this"
|
||||
<< " task may be modified."
|
||||
<< std::endl;
|
||||
|
||||
Task before (*other);
|
||||
|
||||
// A non-zero value forces a file write.
|
||||
@@ -943,7 +992,7 @@ std::string handleModify ()
|
||||
|
||||
if (taskDiff (before, *other))
|
||||
{
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Are you sure?"))
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*other);
|
||||
++count;
|
||||
@@ -959,11 +1008,12 @@ std::string handleModify ()
|
||||
if (context.config.get ("echo.command", true))
|
||||
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;
|
||||
std::stringstream out;
|
||||
@@ -1002,7 +1052,7 @@ std::string handleAppend ()
|
||||
|
||||
if (taskDiff (before, *other))
|
||||
{
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Are you sure?"))
|
||||
if (changes && permission.confirmed (before, taskDifferences (before, *other) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*other);
|
||||
|
||||
@@ -1026,11 +1076,12 @@ std::string handleAppend ()
|
||||
if (context.config.get ("echo.command", true))
|
||||
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;
|
||||
int count = 0;
|
||||
@@ -1089,13 +1140,23 @@ std::string handleDuplicate ()
|
||||
++count;
|
||||
}
|
||||
|
||||
if (context.config.get ("echo.command", true))
|
||||
{
|
||||
out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||
#ifdef FEATURE_NEW_ID
|
||||
// All this, just for an id number.
|
||||
std::vector <Task> all;
|
||||
Filter none;
|
||||
context.tdb.loadPending (all, none);
|
||||
out << "Created task " << context.tdb.nextId () << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
context.tdb.commit ();
|
||||
context.tdb.unlock ();
|
||||
|
||||
if (context.config.get ("echo.command", true))
|
||||
out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl;
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1117,13 +1178,9 @@ void handleShell ()
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
|
||||
// Preserve any special override arguments, and reapply them for each
|
||||
// shell command.
|
||||
std::vector <std::string> special;
|
||||
foreach (arg, context.args)
|
||||
if (arg->substr (0, 3) == "rc." ||
|
||||
arg->substr (0, 3) == "rc:")
|
||||
special.push_back (*arg);
|
||||
// Make a copy because context.clear will delete them.
|
||||
std::string permanentOverrides = " " + context.file_override
|
||||
+ " " + context.var_overrides;
|
||||
|
||||
std::string quit = "quit"; // TODO i18n
|
||||
std::string command;
|
||||
@@ -1135,11 +1192,13 @@ void handleShell ()
|
||||
|
||||
command = "";
|
||||
std::getline (std::cin, command);
|
||||
command = lowerCase (trim (command));
|
||||
std::string decoratedCommand = trim (command + permanentOverrides);
|
||||
|
||||
if (command.length () > 0 &&
|
||||
command.length () <= quit.length () &&
|
||||
command == quit.substr (0, command.length ()))
|
||||
// When looking for the 'quit' command, use 'command', not
|
||||
// 'decoratedCommand'.
|
||||
if (command.length () > 0 &&
|
||||
command.length () <= quit.length () &&
|
||||
lowerCase (command) == quit.substr (0, command.length ()))
|
||||
{
|
||||
keepGoing = false;
|
||||
}
|
||||
@@ -1150,8 +1209,7 @@ void handleShell ()
|
||||
context.clear ();
|
||||
|
||||
std::vector <std::string> args;
|
||||
split (args, command, ' ');
|
||||
foreach (arg, special) context.args.push_back (*arg);
|
||||
split (args, decoratedCommand, ' ');
|
||||
foreach (arg, args) context.args.push_back (*arg);
|
||||
|
||||
context.initialize ();
|
||||
@@ -1170,12 +1228,16 @@ void handleShell ()
|
||||
}
|
||||
}
|
||||
while (keepGoing && !std::cin.eof ());
|
||||
|
||||
// No need to repeat any overrides after the shell quits.
|
||||
context.clearMessages ();
|
||||
}
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleColor ()
|
||||
int handleColor (std::string &outs)
|
||||
{
|
||||
int rc = 0;
|
||||
std::stringstream out;
|
||||
if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false))
|
||||
{
|
||||
@@ -1257,13 +1319,15 @@ std::string handleColor ()
|
||||
out << "Color is currently turned off in your .taskrc file. "
|
||||
"To enable color, create the entry 'color=on'."
|
||||
<< 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"))
|
||||
throw std::string ("Cannot apply a blank annotation.");
|
||||
@@ -1289,7 +1353,7 @@ std::string handleAnnotate ()
|
||||
|
||||
if (taskDiff (before, *task))
|
||||
{
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Are you sure?"))
|
||||
if (permission.confirmed (before, taskDifferences (before, *task) + "Proceed with change?"))
|
||||
{
|
||||
context.tdb.update (*task);
|
||||
|
||||
@@ -1307,7 +1371,8 @@ std::string handleAnnotate ()
|
||||
context.tdb.commit ();
|
||||
context.tdb.unlock ();
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -52,7 +52,7 @@ static std::vector <std::string> customReports;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This report will eventually become the one report that many others morph into
|
||||
// via the .taskrc file.
|
||||
std::string handleCustomReport (const std::string& report)
|
||||
int handleCustomReport (const std::string& report, std::string &outs)
|
||||
{
|
||||
// Load report configuration.
|
||||
std::string columnList = context.config.get ("report." + report + ".columns");
|
||||
@@ -94,21 +94,24 @@ std::string handleCustomReport (const std::string& report)
|
||||
labelList,
|
||||
sortList,
|
||||
filterList,
|
||||
tasks);
|
||||
tasks,
|
||||
outs);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// This report will eventually become the one report that many others morph into
|
||||
// via the .taskrc file.
|
||||
|
||||
std::string runCustomReport (
|
||||
int runCustomReport (
|
||||
const std::string& report,
|
||||
const std::string& columnList,
|
||||
const std::string& labelList,
|
||||
const std::string& sortList,
|
||||
const std::string& filterList,
|
||||
std::vector <Task>& tasks)
|
||||
std::vector <Task>& tasks,
|
||||
std::string &outs)
|
||||
{
|
||||
int rc = 0;
|
||||
// Load report configuration.
|
||||
std::vector <std::string> columns;
|
||||
split (columns, columnList, ',');
|
||||
@@ -458,6 +461,7 @@ std::string runCustomReport (
|
||||
std::string column = sortColumn->substr (0, sortColumn->length () - 1);
|
||||
char direction = (*sortColumn)[sortColumn->length () - 1];
|
||||
|
||||
// TODO This code should really be using Att::type.
|
||||
if (column == "id")
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
@@ -471,7 +475,7 @@ std::string runCustomReport (
|
||||
Table::descendingPriority));
|
||||
|
||||
else if (column == "entry" || column == "start" || column == "due" ||
|
||||
column == "wait")
|
||||
column == "wait" || column == "until" || column == "end")
|
||||
table.sortOn (columnIndex[column],
|
||||
(direction == '+' ?
|
||||
Table::ascendingDate :
|
||||
@@ -547,11 +551,14 @@ std::string runCustomReport (
|
||||
<< table.rowCount ()
|
||||
<< (table.rowCount () == 1 ? " task" : " tasks")
|
||||
<< std::endl;
|
||||
else
|
||||
else {
|
||||
out << "No matches."
|
||||
<< std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -150,7 +150,7 @@ static std::string formatTask (Task task)
|
||||
<< " Due: " << formatDate (task, "due") << std::endl
|
||||
<< " Until: " << formatDate (task, "until") << std::endl
|
||||
<< " Recur: " << task.get ("recur") << std::endl
|
||||
<< " Wait until: " << task.get ("wait") << std::endl
|
||||
<< " Wait until: " << formatDate (task, "wait") << std::endl
|
||||
<< " Parent: " << task.get ("parent") << std::endl
|
||||
<< " Foreground color: " << task.get ("fg") << std::endl
|
||||
<< " Background color: " << task.get ("bg") << std::endl
|
||||
@@ -543,7 +543,7 @@ void editFile (Task& task)
|
||||
|
||||
// Complete the command line.
|
||||
editor += " ";
|
||||
editor += file.str ();
|
||||
editor += "\"" + file.str () + "\"";
|
||||
|
||||
ARE_THESE_REALLY_HARMFUL:
|
||||
// Launch the editor.
|
||||
@@ -599,7 +599,7 @@ ARE_THESE_REALLY_HARMFUL:
|
||||
// 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
|
||||
// wrench. To be used sparingly.
|
||||
std::string handleEdit ()
|
||||
int handleEdit (std::string &outs)
|
||||
{
|
||||
std::stringstream out;
|
||||
|
||||
@@ -643,7 +643,8 @@ std::string handleEdit ()
|
||||
context.tdb.commit ();
|
||||
context.tdb.unlock ();
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -106,16 +106,16 @@ static fileType determineFileType (const std::vector <std::string>& lines)
|
||||
{
|
||||
if ( lines[i][0] == 'x' &&
|
||||
lines[i][1] == ' ' &&
|
||||
::isdigit (lines[i][2]) &&
|
||||
::isdigit (lines[i][3]) &&
|
||||
::isdigit (lines[i][4]) &&
|
||||
::isdigit (lines[i][5]) &&
|
||||
isdigit (lines[i][2]) &&
|
||||
isdigit (lines[i][3]) &&
|
||||
isdigit (lines[i][4]) &&
|
||||
isdigit (lines[i][5]) &&
|
||||
lines[i][6] == '-' &&
|
||||
::isdigit (lines[i][7]) &&
|
||||
::isdigit (lines[i][8]) &&
|
||||
isdigit (lines[i][7]) &&
|
||||
isdigit (lines[i][8]) &&
|
||||
lines[i][9] == '-' &&
|
||||
::isdigit (lines[i][10]) &&
|
||||
::isdigit (lines[i][11]))
|
||||
isdigit (lines[i][10]) &&
|
||||
isdigit (lines[i][11]))
|
||||
return todo_sh_2_0;
|
||||
}
|
||||
|
||||
@@ -126,13 +126,13 @@ static fileType determineFileType (const std::vector <std::string>& lines)
|
||||
// +project
|
||||
if (words[w].length () > 1 &&
|
||||
words[w][0] == '+' &&
|
||||
::isalnum (words[w][1]))
|
||||
isalnum (words[w][1]))
|
||||
return todo_sh_2_0;
|
||||
|
||||
// @context
|
||||
if (words[w].length () > 1 &&
|
||||
words[w][0] == '@' &&
|
||||
::isalnum (words[w][1]))
|
||||
isalnum (words[w][1]))
|
||||
return todo_sh_2_0;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
std::vector <std::string> failed;
|
||||
std::string unused;
|
||||
|
||||
std::vector <std::string>::const_iterator 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.cmd.command = "";
|
||||
context.parse ();
|
||||
handleAdd ();
|
||||
(void)handleAdd (unused);
|
||||
context.clearMessages ();
|
||||
}
|
||||
|
||||
@@ -789,6 +790,8 @@ static std::string importTodoSh_2_0 (const std::vector <std::string>& lines)
|
||||
context.parse ();
|
||||
decorateTask (context.task);
|
||||
|
||||
context.task.set ("uuid", uuid ());
|
||||
|
||||
if (isPending)
|
||||
{
|
||||
context.task.setStatus (Task::pending);
|
||||
@@ -1144,7 +1147,7 @@ static std::string importCSV (const std::vector <std::string>& lines)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleImport ()
|
||||
int handleImport (std::string &outs)
|
||||
{
|
||||
std::stringstream out;
|
||||
|
||||
@@ -1212,7 +1215,8 @@ std::string handleImport ()
|
||||
else
|
||||
throw std::string ("You must specify a file to import.");
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
71
src/main.h
71
src/main.h
@@ -55,25 +55,25 @@ int getDueState (const std::string&);
|
||||
bool nag (Task&);
|
||||
|
||||
// command.cpp
|
||||
std::string handleAdd ();
|
||||
std::string handleAppend ();
|
||||
std::string handleExport ();
|
||||
std::string handleDone ();
|
||||
std::string handleModify ();
|
||||
std::string handleProjects ();
|
||||
std::string handleCompletionProjects ();
|
||||
std::string handleTags ();
|
||||
std::string handleCompletionTags ();
|
||||
std::string handleCompletionCommands ();
|
||||
std::string handleCompletionIDs ();
|
||||
std::string handleCompletionConfig ();
|
||||
std::string handleVersion ();
|
||||
std::string handleDelete ();
|
||||
std::string handleStart ();
|
||||
std::string handleStop ();
|
||||
std::string handleColor ();
|
||||
std::string handleAnnotate ();
|
||||
std::string handleDuplicate ();
|
||||
int handleAdd (std::string &);
|
||||
int handleAppend (std::string &);
|
||||
int handleExport (std::string &);
|
||||
int handleDone (std::string &);
|
||||
int handleModify (std::string &);
|
||||
int handleProjects (std::string &);
|
||||
int handleCompletionProjects (std::string &);
|
||||
int handleTags (std::string &);
|
||||
int handleCompletionTags (std::string &);
|
||||
int handleCompletionCommands (std::string &);
|
||||
int handleCompletionIDs (std::string &);
|
||||
int handleCompletionConfig (std::string &);
|
||||
int handleVersion (std::string &);
|
||||
int handleDelete (std::string &);
|
||||
int handleStart (std::string &);
|
||||
int handleStop (std::string &);
|
||||
int handleColor (std::string &);
|
||||
int handleAnnotate (std::string &);
|
||||
int handleDuplicate (std::string &);
|
||||
void handleUndo ();
|
||||
#ifdef FEATURE_SHELL
|
||||
void handleShell ();
|
||||
@@ -85,27 +85,28 @@ int deltaAttributes (Task&);
|
||||
int deltaSubstitutions (Task&);
|
||||
|
||||
// edit.cpp
|
||||
std::string handleEdit ();
|
||||
int handleEdit (std::string &);
|
||||
|
||||
// report.cpp
|
||||
std::string shortUsage ();
|
||||
std::string longUsage ();
|
||||
std::string handleInfo ();
|
||||
std::string handleReportSummary ();
|
||||
std::string handleReportNext ();
|
||||
std::string handleReportHistory ();
|
||||
std::string handleReportGHistory ();
|
||||
std::string handleReportCalendar ();
|
||||
std::string handleReportStats ();
|
||||
std::string handleReportTimesheet ();
|
||||
int shortUsage (std::string &);
|
||||
int longUsage (std::string &);
|
||||
int handleInfo (std::string &);
|
||||
int handleReportSummary (std::string &);
|
||||
int handleReportNext (std::string &);
|
||||
int handleReportHistory (std::string &);
|
||||
int handleReportGHistory (std::string &);
|
||||
int handleReportCalendar (std::string &);
|
||||
int handleReportStats (std::string &);
|
||||
int handleReportTimesheet (std::string &);
|
||||
std::string getFullDescription (Task&);
|
||||
std::string getDueDate (Task&);
|
||||
|
||||
// custom.cpp
|
||||
std::string handleCustomReport (const std::string&);
|
||||
std::string runCustomReport (const std::string&, const std::string&,
|
||||
const std::string&, const std::string&,
|
||||
const std::string&, std::vector <Task>&);
|
||||
int handleCustomReport (const std::string&, std::string &);
|
||||
int runCustomReport (const std::string&, const std::string&,
|
||||
const std::string&, const std::string&,
|
||||
const std::string&, std::vector <Task>&,
|
||||
std::string&);
|
||||
void validReportColumns (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&);
|
||||
|
||||
// import.cpp
|
||||
std::string handleImport ();
|
||||
int handleImport (std::string&);
|
||||
|
||||
// list template
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
extern Context context;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string shortUsage ()
|
||||
int shortUsage (std::string &outs)
|
||||
{
|
||||
Table table;
|
||||
|
||||
@@ -209,14 +209,19 @@ std::string shortUsage ()
|
||||
<< 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;
|
||||
out << shortUsage ()
|
||||
|
||||
(void)shortUsage(shortUsageString);
|
||||
|
||||
out << shortUsageString
|
||||
<< "ID is the numeric identifier displayed by the 'task list' command. "
|
||||
<< "You can specify multiple IDs for task commands, and multiple tasks "
|
||||
<< "will be affected. To specify multiple IDs make sure you use one "
|
||||
@@ -276,13 +281,15 @@ std::string longUsage ()
|
||||
<< " $ ! ' \" ( ) ; \\ ` * ? { } [ ] < > | & % # ~" << "\n"
|
||||
<< std::endl;
|
||||
|
||||
return out.str ();
|
||||
outs = out.str();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Display all information for the given task.
|
||||
std::string handleInfo ()
|
||||
int handleInfo (std::string &outs)
|
||||
{
|
||||
int rc = 0;
|
||||
// Get all the tasks.
|
||||
std::vector <Task> tasks;
|
||||
context.tdb.lock (context.config.get ("locking", true));
|
||||
@@ -497,18 +504,22 @@ std::string handleInfo ()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
if (! tasks.size ())
|
||||
if (! tasks.size ()) {
|
||||
out << "No matches." << std::endl;
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return rc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Project Remaining Avg Age Complete 0% 100%
|
||||
// A 12 13d 55% XXXXXXXXXXXXX-----------
|
||||
// B 109 3d 12h 10% XXX---------------------
|
||||
std::string handleReportSummary ()
|
||||
int handleReportSummary (std::string &outs)
|
||||
{
|
||||
int rc = 0;
|
||||
// Scan the pending tasks.
|
||||
std::vector <Task> tasks;
|
||||
context.tdb.lock (context.config.get ("locking", true));
|
||||
@@ -648,10 +659,13 @@ std::string handleReportSummary ()
|
||||
<< table.rowCount ()
|
||||
<< (table.rowCount () == 1 ? " project" : " projects")
|
||||
<< std::endl;
|
||||
else
|
||||
else {
|
||||
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
|
||||
//
|
||||
std::string handleReportNext ()
|
||||
int handleReportNext (std::string &outs)
|
||||
{
|
||||
// Load report configuration.
|
||||
std::string columnList = context.config.get ("report.next.columns");
|
||||
@@ -718,7 +732,8 @@ std::string handleReportNext ()
|
||||
labelList,
|
||||
sortList,
|
||||
filterList,
|
||||
tasks);
|
||||
tasks,
|
||||
outs);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -744,8 +759,9 @@ time_t monthlyEpoch (const std::string& date)
|
||||
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> addedGroup; // Additions by month
|
||||
std::map <time_t, int> completedGroup; // Completions by month
|
||||
@@ -891,15 +907,19 @@ std::string handleReportHistory ()
|
||||
out << optionalBlankLine ()
|
||||
<< table.render ()
|
||||
<< std::endl;
|
||||
else
|
||||
else {
|
||||
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> addedGroup; // Additions by month
|
||||
std::map <time_t, int> completedGroup; // Completions by month
|
||||
@@ -1087,14 +1107,17 @@ std::string handleReportGHistory ()
|
||||
else
|
||||
out << "Legend: + added, X completed, - deleted" << std::endl;
|
||||
}
|
||||
else
|
||||
else {
|
||||
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.
|
||||
std::vector <Task> tasks;
|
||||
@@ -1264,7 +1287,8 @@ std::string handleReportTimesheet ()
|
||||
end -= 7 * 86400;
|
||||
}
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1376,9 +1400,9 @@ std::string renderMonths (
|
||||
row = 0;
|
||||
|
||||
// Loop through days in month and add to table.
|
||||
for (int d = 1; d <= daysInMonth.at (mpl); ++d)
|
||||
for (int d = 1; d <= daysInMonth[mpl]; ++d)
|
||||
{
|
||||
Date temp (months.at (mpl), d, years.at (mpl));
|
||||
Date temp (months[mpl], d, years[mpl]);
|
||||
int dow = temp.dayOfWeek ();
|
||||
int woy = temp.weekOfYear (weekStart);
|
||||
|
||||
@@ -1396,9 +1420,9 @@ std::string renderMonths (
|
||||
table.addCell (row, thisCol, d);
|
||||
|
||||
if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) &&
|
||||
today.day () == d &&
|
||||
today.month () == months.at (mpl) &&
|
||||
today.year () == years.at (mpl))
|
||||
today.day () == d &&
|
||||
today.month () == months[mpl] &&
|
||||
today.year () == years[mpl])
|
||||
table.setCellFg (row, thisCol, Text::cyan);
|
||||
|
||||
foreach (task, all)
|
||||
@@ -1410,8 +1434,8 @@ std::string renderMonths (
|
||||
|
||||
if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) &&
|
||||
due.day () == d &&
|
||||
due.month () == months.at (mpl) &&
|
||||
due.year () == years.at (mpl))
|
||||
due.month () == months[mpl] &&
|
||||
due.year () == years[mpl])
|
||||
{
|
||||
table.setCellFg (row, thisCol, Text::black);
|
||||
table.setCellBg (row, thisCol, due < today ? Text::on_red : Text::on_yellow);
|
||||
@@ -1423,7 +1447,7 @@ std::string renderMonths (
|
||||
int eow = 6;
|
||||
if (weekStart == 1)
|
||||
eow = 0;
|
||||
if (dow == eow && d < daysInMonth.at (mpl))
|
||||
if (dow == eow && d < daysInMonth[mpl])
|
||||
row++;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
// fit. But if a preference is specified, and it fits, use it.
|
||||
@@ -1604,11 +1628,12 @@ std::string handleReportCalendar ()
|
||||
<< optionalBlankLine ()
|
||||
<< std::endl;
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
std::string handleReportStats ()
|
||||
int handleReportStats (std::string &outs)
|
||||
{
|
||||
std::stringstream out;
|
||||
|
||||
@@ -1836,7 +1861,8 @@ std::string handleReportStats ()
|
||||
<< table.render ()
|
||||
<< optionalBlankLine ();
|
||||
|
||||
return out.str ();
|
||||
outs = out.str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -91,6 +91,8 @@ void autoColorize (
|
||||
// Note: fg, bg already contain colors specifically assigned via command.
|
||||
// Note: These rules form a hierarchy - the last rule is King.
|
||||
|
||||
Task::status status = task.getStatus ();
|
||||
|
||||
// Colorization of the tagged.
|
||||
if (gsFg["color.tagged"] != Text::nocolor ||
|
||||
gsBg["color.tagged"] != Text::nocolor)
|
||||
@@ -146,9 +148,11 @@ void autoColorize (
|
||||
}
|
||||
}
|
||||
|
||||
// Colorization of the active.
|
||||
if (gsFg["color.active"] != Text::nocolor ||
|
||||
gsBg["color.active"] != Text::nocolor)
|
||||
// Colorization of the active, if not completed/deleted.
|
||||
if ((gsFg["color.active"] != Text::nocolor ||
|
||||
gsBg["color.active"] != Text::nocolor) &&
|
||||
status != Task::completed &&
|
||||
status != Task::deleted)
|
||||
{
|
||||
if (task.has ("start"))
|
||||
{
|
||||
@@ -202,7 +206,9 @@ void autoColorize (
|
||||
}
|
||||
|
||||
// Colorization of the due and overdue.
|
||||
if (task.has ("due"))
|
||||
if (task.has ("due") &&
|
||||
status != Task::completed &&
|
||||
status != Task::deleted)
|
||||
{
|
||||
std::string due = task.get ("due");
|
||||
switch (getDueState (due))
|
||||
|
||||
83
src/task.h
83
src/task.h
@@ -1,83 +0,0 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#ifndef INCLUDED_TASK
|
||||
#define INCLUDED_TASK
|
||||
|
||||
#include <string>
|
||||
#include "Record.h"
|
||||
#include "Subst.h"
|
||||
#include "Sequence.h"
|
||||
|
||||
class Task : public Record
|
||||
{
|
||||
public:
|
||||
Task (); // Default constructor
|
||||
Task (const Task&); // Copy constructor
|
||||
Task& operator= (const Task&); // Assignment operator
|
||||
bool operator== (const Task&); // Comparison operator
|
||||
Task (const std::string&); // Parse
|
||||
~Task (); // Destructor
|
||||
|
||||
void parse (const std::string&);
|
||||
std::string composeCSV () const;
|
||||
|
||||
// Status values.
|
||||
enum status {pending, completed, deleted, recurring, waiting};
|
||||
|
||||
// Public data.
|
||||
int id;
|
||||
|
||||
// Series of helper functions.
|
||||
static status textToStatus (const std::string&);
|
||||
static std::string statusToText (status);
|
||||
|
||||
void setEntry ();
|
||||
|
||||
status getStatus ();
|
||||
void setStatus (status);
|
||||
|
||||
int getTagCount ();
|
||||
bool hasTag (const std::string&);
|
||||
void addTag (const std::string&);
|
||||
void addTags (const std::vector <std::string>&);
|
||||
void getTags (std::vector<std::string>&) const;
|
||||
void removeTag (const std::string&);
|
||||
|
||||
void getAnnotations (std::vector <Att>&) const;
|
||||
void setAnnotations (const std::vector <Att>&);
|
||||
void addAnnotation (const std::string&);
|
||||
void removeAnnotations ();
|
||||
|
||||
void validate () const;
|
||||
|
||||
private:
|
||||
int determineVersion (const std::string&);
|
||||
void legacyParse (const std::string&);
|
||||
};
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -40,7 +40,7 @@ if (open my $fh, '>', 'basic.rc')
|
||||
|
||||
# Test the usage command.
|
||||
my $output = qx{../task rc:basic.rc};
|
||||
like ($output, qr/You must specify a command, or a task ID to modify/, 'missing command and ID');
|
||||
like ($output, qr/You must specify a command, or a task ID to modify/m, 'missing command and ID');
|
||||
|
||||
# Test the version command.
|
||||
$output = qx{../task rc:basic.rc version};
|
||||
|
||||
66
src/tests/bug.327.t
Executable file
66
src/tests/bug.327.t
Executable file
@@ -0,0 +1,66 @@
|
||||
#! /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 => 6;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'bug.rc')
|
||||
{
|
||||
print $fh "data.location=.\n",
|
||||
"confirmation=no\n";
|
||||
close $fh;
|
||||
ok (-r 'bug.rc', 'Created bug.rc');
|
||||
}
|
||||
|
||||
# Setup: Add a recurring task then remove the due date.
|
||||
qx{../task rc:bug.rc add foo recur:yearly due:eoy};
|
||||
qx{../task rc:bug.rc li};
|
||||
qx{../task rc:bug.rc 2 due:};
|
||||
|
||||
# Result: Somehow the due date is incremented and wraps around to 12/31/1969,
|
||||
# then keeps going back to today.
|
||||
my $output = qx{../task rc:bug.rc li};
|
||||
like ($output, qr/^1 task$/ms, 'Should only be one task');
|
||||
|
||||
# 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 'bug.rc';
|
||||
ok (!-r 'bug.rc', 'Removed bug.rc');
|
||||
|
||||
exit 0;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 15;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'bulk.rc')
|
||||
@@ -49,13 +49,13 @@ qx{../task rc:bulk.rc add t4 due:thursday};
|
||||
qx{../task rc:bulk.rc add t5 due:friday};
|
||||
qx{../task rc:bulk.rc add t6 due:saturday};
|
||||
|
||||
my $output = qx{yes|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
my $output = qx{echo "quit"|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
like ($output, qr/Modified 0 tasks/, '"quit" prevents any further modifications');
|
||||
|
||||
my $output = qx{echo "all"|../task rc:bulk.rc pro:p1 pri:M 4 5 6};
|
||||
unlike ($output, qr/Task 4 "t4"\n - No changes were made/, 'Task 4 modified');
|
||||
unlike ($output, qr/Task 5 "t5"\n - No changes were made/, 'Task 5 modified');
|
||||
unlike ($output, qr/Task 6 "t6"\n - No changes were made/, 'Task 6 modified');
|
||||
#diag ("---");
|
||||
#diag ($output);
|
||||
#diag ("---");
|
||||
|
||||
$output = qx{../task rc:bulk.rc info 4};
|
||||
like ($output, qr/Project\s+p1/, 'project applied to 4');
|
||||
|
||||
@@ -58,7 +58,7 @@ if ( $day <= 9)
|
||||
# task cal and task cal y
|
||||
my $output = qx{../task rc:cal.rc rc._forcecolor:on cal};
|
||||
like ($output, qr/\[36m$day/, 'Current day is highlighted');
|
||||
like ($output, qr/$month.* $year/, 'Current month and year are displayed');
|
||||
like ($output, qr/$month\w+?\s+?$year/, 'Current month and year are displayed');
|
||||
qx{../task rc:cal.rc add zero};
|
||||
unlike ($output, qr/\[41m\d+/, 'No overdue tasks are present');
|
||||
unlike ($output, qr/\[43m\d+/, 'No due tasks are present');
|
||||
@@ -67,9 +67,9 @@ like ($output, qr/Su Mo Tu/, 'Week starts on Sunday');
|
||||
$output = qx{../task rc:cal.rc rc.weekstart:Monday cal};
|
||||
like ($output, qr/Fr Sa Su/, 'Week starts on Monday');
|
||||
$output = qx{../task rc:cal.rc cal y};
|
||||
like ($output, qr/$month.* $year/, 'Current month and year are displayed');
|
||||
like ($output, qr/$prevmonth.* $nextyear/, 'Month and year one year ahead are displayed');
|
||||
unlike ($output, qr/$month.* $nextyear/, 'Current month and year ahead are not displayed');
|
||||
like ($output, qr/$month\w+?\s+?$year/, 'Current month and year are displayed');
|
||||
like ($output, qr/$prevmonth\w+?\s+?$nextyear/, 'Month and year one year ahead are displayed');
|
||||
unlike ($output, qr/$month\w+?\s+?$nextyear/, 'Current month and year ahead are not displayed');
|
||||
|
||||
# task cal due and task cal due y
|
||||
qx{../task rc:cal.rc add due:20190515 one};
|
||||
|
||||
@@ -49,10 +49,10 @@ if (open my $fh, '>', 'response.txt')
|
||||
|
||||
qx{../task rc:confirm.rc add foo} for 1 .. 10;
|
||||
|
||||
# Test the various forms of "yes".
|
||||
my $output = qx{echo "yes" | ../task rc:confirm.rc del 1};
|
||||
like ($output, qr/Permanently delete task 1 'foo'\? \(y\/n\)/, 'confirmation - yes works');
|
||||
unlike ($output, qr/Task not deleted\./, 'confirmation - yes works');
|
||||
# Test the various forms of "Yes".
|
||||
my $output = qx{echo "Yes" | ../task rc:confirm.rc del 1};
|
||||
like ($output, qr/Permanently delete task 1 'foo'\? \(y\/n\)/, 'confirmation - Yes works');
|
||||
unlike ($output, qr/Task not deleted\./, 'confirmation - Yes works');
|
||||
|
||||
$output = qx{echo "ye" | ../task rc:confirm.rc del 2};
|
||||
like ($output, qr/Permanently delete task 2 'foo'\? \(y\/n\)/, 'confirmation - ye works');
|
||||
|
||||
@@ -34,7 +34,7 @@ use Test::More tests => 17;
|
||||
if (open my $fh, '>', 'default.rc')
|
||||
{
|
||||
print $fh "data.location=.\n",
|
||||
"default.command=rc:default.rc list\n",
|
||||
"default.command=list\n",
|
||||
"default.project=PROJECT\n",
|
||||
"default.priority=M\n";
|
||||
close $fh;
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 12;
|
||||
use Test::More tests => 15;
|
||||
|
||||
# Create the rc file.
|
||||
if (open my $fh, '>', 'dup.rc')
|
||||
@@ -55,6 +55,12 @@ like ($output, qr/Description\s+FOO/, 'duplicate modified description');
|
||||
like ($output, qr/Priority\s+H/, 'duplicate added priority');
|
||||
like ($output, qr/Tags\s+tag/, 'duplicate added tag');
|
||||
|
||||
# Test the output of the duplicate command - returning id of duplicated task
|
||||
$output = qx{../task rc:dup.rc duplicate 1};
|
||||
like ($output, qr/Duplicated\s+1\s+'foo'/, 'duplicate output task id and description');
|
||||
like ($output, qr/Duplicated\s+1\s+task/, 'duplicate output number of tasks duplicated');
|
||||
like ($output, qr/Created\s+task\s+4/, 'duplicate output of new task id');
|
||||
|
||||
# Cleanup.
|
||||
unlink 'pending.data';
|
||||
ok (!-r 'pending.data', 'Removed pending.data');
|
||||
|
||||
62
src/tests/feature.exit.t
Executable file
62
src/tests/feature.exit.t
Executable 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;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#! /bin/bash
|
||||
#! /bin/sh
|
||||
|
||||
date > all.log
|
||||
|
||||
@@ -14,7 +14,7 @@ END=`tail -1 all.log`
|
||||
OS=`uname`
|
||||
|
||||
case $OS in
|
||||
Darwin)
|
||||
Darwin | FreeBSD)
|
||||
STARTEPOCH=`date -j -f "%a %b %d %T %Z %Y" "${START}" "+%s"`
|
||||
ENDEPOCH=`date -j -f "%a %b %d %T %Z %Y" "${END}" "+%s"`
|
||||
;;
|
||||
|
||||
@@ -34,7 +34,7 @@ Context context;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (109);
|
||||
UnitTest t (117);
|
||||
|
||||
// void wrapText (std::vector <std::string>& lines, const std::string& text, const int width)
|
||||
std::string text = "This is a test of the line wrapping code.";
|
||||
@@ -104,12 +104,19 @@ int main (int argc, char** argv)
|
||||
t.is (items.size (), (size_t) 1, "split 'a' '-' -> 1 item");
|
||||
t.is (items[0], "a", "split 'a' '-' -> 'a'");
|
||||
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 1, "split 'a' '-' -> 1 item");
|
||||
t.is (items[0], "a", "split 'a' '-' -> 'a'");
|
||||
|
||||
unsplit = "-";
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 2, "split '-' '-' -> '' ''");
|
||||
t.is (items[0], "", "split '-' '-' -> [0] ''");
|
||||
t.is (items[1], "", "split '-' '-' -> [1] ''");
|
||||
|
||||
split_minimal (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 1, "split '-' '-' -> '-'");
|
||||
|
||||
unsplit = "-a-bc-def";
|
||||
split (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 4, "split '-a-bc-def' '-' -> '' 'a' 'bc' 'def'");
|
||||
@@ -118,11 +125,20 @@ int main (int argc, char** argv)
|
||||
t.is (items[2], "bc", "split '-a-bc-def' '-' -> [2] 'bc'");
|
||||
t.is (items[3], "def", "split '-a-bc-def' '-' -> [3] 'def'");
|
||||
|
||||
split_minimal (items, unsplit, '-');
|
||||
t.is (items.size (), (size_t) 3, "split '-a-bc-def' '-' -> 'a' 'bc' 'def'");
|
||||
t.is (items[0], "a", "split '-a-bc-def' '-' -> [1] 'a'");
|
||||
t.is (items[1], "bc", "split '-a-bc-def' '-' -> [2] 'bc'");
|
||||
t.is (items[2], "def", "split '-a-bc-def' '-' -> [3] 'def'");
|
||||
|
||||
// void split (std::vector<std::string>& results, const std::string& input, const std::string& delimiter)
|
||||
unsplit = "";
|
||||
split (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 0, "split '' '--' -> 0 items");
|
||||
|
||||
split_minimal (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 0, "split '' '--' -> 0");
|
||||
|
||||
unsplit = "a";
|
||||
split (items, unsplit, "--");
|
||||
t.is (items.size (), (size_t) 1, "split 'a' '--' -> 1 item");
|
||||
|
||||
@@ -38,6 +38,7 @@ int main (int argc, char** argv)
|
||||
|
||||
// TODO bool confirm (const std::string&);
|
||||
// TODO int confirm3 (const std::string&);
|
||||
// TODO int confirm4 (const std::string&);
|
||||
// TODO void delay (float);
|
||||
// TODO std::string formatSeconds (time_t);
|
||||
// TODO std::string formatSecondsCompact (time_t);
|
||||
@@ -83,15 +84,15 @@ int main (int argc, char** argv)
|
||||
Task rightAgain (right);
|
||||
|
||||
std::string output = taskDifferences (left, right);
|
||||
t.ok (taskDiff (left, right), "Detected changes");
|
||||
t.ok (output.find ("zero was changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
||||
t.ok (output.find ("one was deleted") != std::string::npos, "Detected deletion one:1 ->");
|
||||
t.ok (output.find ("two") == std::string::npos, "Detected no change two:2 -> two:2");
|
||||
t.ok (output.find ("three was set to '3'") != std::string::npos, "Detected addition -> three:3");
|
||||
t.ok (taskDiff (left, right), "Detected changes");
|
||||
t.ok (output.find ("zero will be changed from '0' to '00'") != std::string::npos, "Detected change zero:0 -> zero:00");
|
||||
t.ok (output.find ("one will be deleted") != std::string::npos, "Detected deletion one:1 ->");
|
||||
t.ok (output.find ("two") == std::string::npos, "Detected no change two:2 -> two:2");
|
||||
t.ok (output.find ("three will be set to '3'") != std::string::npos, "Detected addition -> three:3");
|
||||
|
||||
t.notok (taskDiff (right, rightAgain), "No changes detected");
|
||||
t.notok (taskDiff (right, rightAgain), "No changes detected");
|
||||
output = taskDifferences (right, rightAgain);
|
||||
t.ok (output.find ("No changes were made") != std::string::npos, "No changes detected");
|
||||
t.ok (output.find ("No changes will be made") != std::string::npos, "No changes detected");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
66
src/text.cpp
66
src/text.cpp
@@ -72,6 +72,26 @@ void split (
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split_minimal (
|
||||
std::vector<std::string>& results,
|
||||
const std::string& input,
|
||||
const char delimiter)
|
||||
{
|
||||
results.clear ();
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type i;
|
||||
while ((i = input.find (delimiter, start)) != std::string::npos)
|
||||
{
|
||||
if (i != start)
|
||||
results.push_back (input.substr (start, i - start));
|
||||
start = i + 1;
|
||||
}
|
||||
|
||||
if (input.length ())
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split (
|
||||
std::vector<std::string>& results,
|
||||
@@ -93,6 +113,28 @@ void split (
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void split_minimal (
|
||||
std::vector<std::string>& results,
|
||||
const std::string& input,
|
||||
const std::string& delimiter)
|
||||
{
|
||||
results.clear ();
|
||||
std::string::size_type length = delimiter.length ();
|
||||
|
||||
std::string::size_type start = 0;
|
||||
std::string::size_type i;
|
||||
while ((i = input.find (delimiter, start)) != std::string::npos)
|
||||
{
|
||||
if (i != start)
|
||||
results.push_back (input.substr (start, i - start));
|
||||
start = i + length;
|
||||
}
|
||||
|
||||
if (input.length ())
|
||||
results.push_back (input.substr (start, std::string::npos));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void join (
|
||||
std::string& result,
|
||||
@@ -209,7 +251,7 @@ std::string commify (const std::string& data)
|
||||
int i;
|
||||
for (int i = 0; i < (int) data.length (); ++i)
|
||||
{
|
||||
if (::isdigit (data[i]))
|
||||
if (isdigit (data[i]))
|
||||
end = i;
|
||||
|
||||
if (data[i] == '.')
|
||||
@@ -227,11 +269,11 @@ std::string commify (const std::string& data)
|
||||
int consecutiveDigits = 0;
|
||||
for (; i >= 0; --i)
|
||||
{
|
||||
if (::isdigit (data[i]))
|
||||
if (isdigit (data[i]))
|
||||
{
|
||||
result += data[i];
|
||||
|
||||
if (++consecutiveDigits == 3 && i && ::isdigit (data[i - 1]))
|
||||
if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
|
||||
{
|
||||
result += ',';
|
||||
consecutiveDigits = 0;
|
||||
@@ -251,11 +293,11 @@ std::string commify (const std::string& data)
|
||||
int consecutiveDigits = 0;
|
||||
for (; i >= 0; --i)
|
||||
{
|
||||
if (::isdigit (data[i]))
|
||||
if (isdigit (data[i]))
|
||||
{
|
||||
result += data[i];
|
||||
|
||||
if (++consecutiveDigits == 3 && i && ::isdigit (data[i - 1]))
|
||||
if (++consecutiveDigits == 3 && i && isdigit (data[i - 1]))
|
||||
{
|
||||
result += ',';
|
||||
consecutiveDigits = 0;
|
||||
@@ -279,8 +321,8 @@ std::string lowerCase (const std::string& input)
|
||||
{
|
||||
std::string output = input;
|
||||
for (int i = 0; i < (int) input.length (); ++i)
|
||||
if (::isupper (input[i]))
|
||||
output[i] = ::tolower (input[i]);
|
||||
if (isupper (input[i]))
|
||||
output[i] = tolower (input[i]);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -290,8 +332,8 @@ std::string upperCase (const std::string& input)
|
||||
{
|
||||
std::string output = input;
|
||||
for (int i = 0; i < (int) input.length (); ++i)
|
||||
if (::islower (input[i]))
|
||||
output[i] = ::toupper (input[i]);
|
||||
if (islower (input[i]))
|
||||
output[i] = toupper (input[i]);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -302,7 +344,7 @@ std::string ucFirst (const std::string& input)
|
||||
std::string output = input;
|
||||
|
||||
if (output.length () > 0)
|
||||
output[0] = ::toupper (output[0]);
|
||||
output[0] = toupper (output[0]);
|
||||
|
||||
return output;
|
||||
}
|
||||
@@ -352,7 +394,7 @@ void guess (
|
||||
bool digitsOnly (const std::string& input)
|
||||
{
|
||||
for (size_t i = 0; i < input.length (); ++i)
|
||||
if (!::isdigit (input[i]))
|
||||
if (!isdigit (input[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -362,7 +404,7 @@ bool digitsOnly (const std::string& input)
|
||||
bool noSpaces (const std::string& input)
|
||||
{
|
||||
for (size_t i = 0; i < input.length (); ++i)
|
||||
if (::isspace (input[i]))
|
||||
if (isspace (input[i]))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -40,6 +40,8 @@ std::string unquoteText (const std::string&);
|
||||
void extractLine (std::string&, std::string&, int);
|
||||
void split (std::vector<std::string>&, const std::string&, const char);
|
||||
void split (std::vector<std::string>&, const std::string&, const std::string&);
|
||||
void split_minimal (std::vector<std::string>&, const std::string&, const char);
|
||||
void split_minimal (std::vector<std::string>&, const std::string&, const std::string&);
|
||||
void join (std::string&, const std::string&, const std::vector<std::string>&);
|
||||
std::string commify (const std::string&);
|
||||
std::string lowerCase (const std::string&);
|
||||
|
||||
53
src/util.cpp
53
src/util.cpp
@@ -64,7 +64,7 @@ bool confirm (const std::string& question)
|
||||
<< " ";
|
||||
|
||||
std::getline (std::cin, answer);
|
||||
answer = lowerCase (trim (answer));
|
||||
answer = std::cin.eof() ? "no" : lowerCase (trim (answer));
|
||||
}
|
||||
while (answer != "y" && // TODO i18n
|
||||
answer != "ye" && // TODO i18n
|
||||
@@ -82,7 +82,7 @@ bool confirm (const std::string& question)
|
||||
int confirm3 (const std::string& question)
|
||||
{
|
||||
std::vector <std::string> options;
|
||||
options.push_back ("yes");
|
||||
options.push_back ("Yes");
|
||||
options.push_back ("no");
|
||||
options.push_back ("all");
|
||||
|
||||
@@ -104,11 +104,49 @@ int confirm3 (const std::string& question)
|
||||
}
|
||||
while (matches.size () != 1);
|
||||
|
||||
if (matches[0] == "yes") return 1;
|
||||
if (matches[0] == "Yes") return 1;
|
||||
else if (matches[0] == "all") return 2;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 0 = no
|
||||
// 1 = yes
|
||||
// 2 = all
|
||||
// 3 = quit
|
||||
int confirm4 (const std::string& question)
|
||||
{
|
||||
std::vector <std::string> options;
|
||||
options.push_back ("Yes");
|
||||
options.push_back ("no");
|
||||
options.push_back ("all");
|
||||
options.push_back ("quit");
|
||||
|
||||
std::string answer;
|
||||
std::vector <std::string> matches;
|
||||
|
||||
do
|
||||
{
|
||||
std::cout << question
|
||||
<< " ("
|
||||
<< options[0] << "/"
|
||||
<< options[1] << "/"
|
||||
<< options[2] << "/"
|
||||
<< options[3]
|
||||
<< ") ";
|
||||
|
||||
std::getline (std::cin, answer);
|
||||
answer = trim (answer);
|
||||
autoComplete (answer, options, matches);
|
||||
}
|
||||
while (matches.size () != 1);
|
||||
|
||||
if (matches[0] == "Yes") return 1;
|
||||
else if (matches[0] == "all") return 2;
|
||||
else if (matches[0] == "quit") return 3;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void delay (float f)
|
||||
{
|
||||
@@ -500,22 +538,21 @@ std::string taskDifferences (const Task& before, const Task& after)
|
||||
foreach (name, beforeOnly)
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was deleted\n";
|
||||
<< " will be deleted\n";
|
||||
|
||||
foreach (name, afterOnly)
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was set to '"
|
||||
<< " will be set to '"
|
||||
<< renderAttribute (*name, after.get (*name))
|
||||
<< "'\n";
|
||||
|
||||
foreach (name, beforeAtts)
|
||||
if (*name != "uuid" &&
|
||||
after.get (*name) != "" &&
|
||||
before.get (*name) != after.get (*name))
|
||||
out << " - "
|
||||
<< *name
|
||||
<< " was changed from '"
|
||||
<< " will be changed from '"
|
||||
<< renderAttribute (*name, before.get (*name))
|
||||
<< "' to '"
|
||||
<< renderAttribute (*name, after.get (*name))
|
||||
@@ -523,7 +560,7 @@ std::string taskDifferences (const Task& before, const Task& after)
|
||||
|
||||
// Shouldn't just say nothing.
|
||||
if (out.str ().length () == 0)
|
||||
out << " - No changes were made\n";
|
||||
out << " - No changes will be made\n";
|
||||
|
||||
return out.str ();
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ for (typeof (c) *foreach_p = & (c); \
|
||||
// util.cpp
|
||||
bool confirm (const std::string&);
|
||||
int confirm3 (const std::string&);
|
||||
int confirm4 (const std::string&);
|
||||
void delay (float);
|
||||
std::string formatSeconds (time_t);
|
||||
std::string formatSecondsCompact (time_t);
|
||||
|
||||
Reference in New Issue
Block a user