Compare commits

...

56 Commits

Author SHA1 Message Date
Federico Hernandez
a6c7236ff3 Release date for 1.8.5 2009-12-06 01:49:05 +01:00
Paul Beckingham
204d287b20 Feature - #341 man pages
- 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).

Signed-off-by: Federico Hernandez <ultrafredde@gmail.com>
2009-12-05 22:49:30 +01:00
Paul Beckingham
b3e3c36d50 Documentation Update
- Added missing ChangeLog entry for #310.
2009-12-05 12:46:29 -05:00
Paul Beckingham
e717345f20 Feature - #310 'task add' with external editor
- Simplified and make clearer and error message that complained about
  things that were beyond user control (thanks to John Florian).
(cherry picked from commit a2152628251c6d8c9bc840b8f36851f4ce680c99)

Signed-off-by: Paul Beckingham <paul@beckingham.net>
2009-12-05 12:23:32 -05:00
Federico Hernandez
3b354b6d47 Bug Fix - #332 output field "recur_ind" not valid?
- changed man page to include the correct fields
2009-12-02 21:41:20 +01:00
Paul Beckingham
2c0da35225 Bug Fix - #319 Removing tag from many tasks, incorrect change summary
- Fixed bug 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).
2009-12-02 00:14:17 -05:00
Paul Beckingham
13955bc6ae Unit Tests - text.t
- Added more unit tests to cover the new split_minimal function.
2009-12-01 23:41:44 -05:00
Paul Beckingham
1d80a2ebdc Unit Tests - default.t
- The fix to bug #322 means the way default commands are specified
  is now a little different.  If the command "task rc:x" is run, the
  default command no longer needs to also include "rc:x".
2009-12-01 23:29:01 -05:00
Paul Beckingham
d4910f65eb Bug Fix - #332 output field "recur_ind" not valid?
- 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).
- Added ChangeLog entry for the #333 fix.
2009-12-01 22:12:59 -05:00
Federico Hernandez
2b44d513e8 Added unit tests for bug fix #333 2009-12-02 00:28:34 +01:00
Federico Hernandez
7f11f1b560 Bug Fix - #333 duplicate command should display the ID of the created task
- Added missing "Created task" output to duplicate command (thanks to
  Cory Donnelly).
2009-12-01 23:00:40 +01:00
Paul Beckingham
b246fae889 Unit Tests - fixing broken tests
- The split tests are all broken after a recent change.  They need
  to be extended to accommodate the new split_minimal functions.
2009-11-29 22:41:55 -05:00
Federico Hernandez
8c5508de4b Merge branch '1.8.5' of tasktools.org:task into 1.8.5 2009-11-30 00:16:40 +01:00
Federico Hernandez
b3db2245fa Updated OS that task runs on 2009-11-30 00:16:10 +01:00
Paul Beckingham
00b246ce8a Bug Fix - shell mode
- Removed redundant messages when exiting shell mode.
2009-11-29 16:17:26 -05:00
Paul Beckingham
b94706c56e Bug Fix - #322 rc: override for shell command does not propagate
- Fixed bug #322 which failed to propagate rc overrides to shell commands.
- Context now properly records overrides to file and variables.
- The text.cpp:split (...) functions can now skip trivial split results.
2009-11-29 14:23:22 -05:00
Paul Beckingham
8d784da0ae Bug Fix = #317, sorting
- 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.
2009-11-29 08:21:33 -05:00
Paul Beckingham
b5f65850f8 Bug Fix - #327 Deleting due date on recurring task wraps to 1969
- Task now prevents removal of either a due date or a recurrence
  from a recurring task.
2009-11-28 09:53:15 -05:00
Paul Beckingham
b7726bce21 Bug Fix - #329 task shell convert all characters to lowercase
- Fixed bug that inadvertently converted the entire command line to
  lower case in the shell, rather than just the command, for testing
  against the "quit" string (thanks to Juergen Daubert).
2009-11-26 09:41:47 -05:00
Paul Beckingham
d44e9363f0 Enhancement - better confirmation
- Added feature to allow the user to quit when asked to confirm multiple
  changes.  Now task asks "Proceed with change? (Yes/no/all/quit)".
2009-11-21 17:39:50 -05:00
Paul Beckingham
549e700bc8 Bug Fix - timesheet
- The timesheet report was being sorted as though the 'end' date was
  not being considered a date, but simply a string.
2009-11-18 20:28:25 -05:00
Paul Beckingham
b2fc4969b9 Documentation Update
- Minor doc edits.
2009-11-18 20:27:54 -05:00
Federico Hernandez
331b08055a Bumped version number to 1.8.5 2009-11-18 10:22:33 +01:00
Federico Hernandez
847a8b6d49 Added SHA1 of taged release commit 2009-11-18 09:56:14 +01:00
Federico Hernandez
12c4983936 Release date for 1.8.4 2009-11-17 12:00:36 +01:00
Paul Beckingham
39d9f235de HACK - case-insensitive file system problem again. 2009-11-16 23:42:32 -05:00
Paul Beckingham
7aa0c3698a HACK - case-insensitive file system problem again. 2009-11-16 23:42:00 -05:00
Paul Beckingham
bc40ab63b3 Bug Fix - #312 Changing one task changes another
- Added a warning 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).
2009-11-16 23:24:47 -05:00
Paul Beckingham
6e673d2834 Bug Fix - #313 Edit command fails when data.location includes spaces
- Applied patch from Cory Donnelly.
2009-11-16 22:10:47 -05:00
Federico Hernandez
30c6dd0047 Added Joe to AUTHORS file 2009-11-13 23:32:40 +01:00
Paul Beckingham
64bc2a165a Bug fix - hang on cygwin when task updated.
- Fixed bug that caused a hang on cygwin, when a task with multiple
  annotations was edited (thanks to Joe Pulliam).
2009-11-09 22:35:51 -05:00
Paul Beckingham
5b96dbbce8 Bug Fix - wait date editing
- The "wait" date was not being properly formatted, as are all the other
  dates, in the "edit" command.  The result is that an epoch integer date
  was rendered, instead of something readable and in the preferred format.
2009-11-09 18:19:04 -05:00
Federico Hernandez
77dd930574 Fixed bug in regexp matching of whitespace between month and year. 2009-11-05 21:36:46 +01:00
Federico Hernandez
f6842941f3 Bumped version number to 1.8.4 2009-10-22 22:40:18 +02:00
Federico Hernandez
e2e0851a69 Merge branch '1.8.3' 2009-10-22 22:27:21 +02:00
Federico Hernandez
1299fe468b Added sha1 for 1.8.3 to Changelog 2009-10-22 22:09:53 +02:00
Federico Hernandez
de50b2902c Merge branch '1.8.3' 2009-10-22 22:02:32 +02:00
Federico Hernandez
bcdcbeeea0 Preparing release
- Updating dist documentation
- Release date in man pages
2009-10-21 22:53:26 +02:00
Pietro Cerutti
469cafa053 Make run_all more portable.
- Changed shebang for portability
- Fix date command for FreeBSD

Signed-off-by: Federico Hernandez <ultrafredde@gmail.com>
2009-10-21 22:20:32 +02:00
Pietro Cerutti
fdb359c180 Fix confirm() to handle EOF
Signed-off-by: Federico Hernandez <ultrafredde@gmail.com>
2009-10-21 22:19:37 +02:00
Paul Beckingham
b3d40b2554 Portability - Haiku R1/alpha1
- Added necessary include files and edits in order to build task on
  Haiku R1/alpha1.
2009-09-27 17:02:20 -04:00
Federico Hernandez
fba076a0d0 Bumped version number to 1.8.3 2009-09-07 16:13:37 +02:00
Federico Hernandez
09d7940dd3 Revert "Bumping version number to 1.8.3"
This reverts commit 00031dc1ab.
2009-09-07 16:04:24 +02:00
Federico Hernandez
00031dc1ab Bumping version number to 1.8.3 2009-09-07 15:52:51 +02:00
Paul Beckingham
3ef844de5f Packaging
- Adding updated package files for OSX, from the 1.8.2 release.
2009-09-07 09:51:20 -04:00
Federico Hernandez
bb45d91ddb Added sha1 of 1.8.2 to ChangLog 2009-09-07 15:41:51 +02:00
Federico Hernandez
f243f0ed44 Release date in man pages 2009-09-07 10:18:44 +02:00
Federico Hernandez
b305cc0a60 Release date in ChangeLog 2009-09-07 09:57:09 +02:00
Paul Beckingham
53609b2837 Bug Fix - #288
- Fixed bug which failed to propagate rc overrides from the command
  line to the default command (thanks to Zach Frazier).
2009-09-06 16:27:28 -04:00
Federico Hernandez
bdaa0f89d9 Platform update 2009-09-02 12:50:43 +02:00
Federico Hernandez
2fd8d8aa83 Bug Fix #291
- Fixed bug #291 which generated a false warning about an unrecognized
  variable when enabling default.projects in .taskrc (thanks to Thomas@BIC)
2009-09-02 10:43:04 +02:00
Federico Hernandez
e69fb81b2b Documentation update in Changelog for #289 2009-09-01 21:12:49 +02:00
Federico Hernandez
d2aa0f31b0 Bug Fix - #289
- Fixed bug #289 which imported task from todo.sh
      without valid uuids (thanks to Ben Jackson).
2009-09-01 21:08:13 +02:00
Paul Beckingham
05f67db429 Bug Fix - #287
- Fixed bug #287 which caused color control codes to be written to
  the shadow file (thanks to Richard Querin).
2009-08-29 13:55:59 -04:00
Paul Beckingham
62be3f8acb Feature #282
- Applied large patch to make task return meaningful exit codes.
- Added unit tests to prove this.
- Thanks to Pietro Cerutti.
2009-08-29 09:14:26 -04:00
Federico Hernandez
cc5c99c0a1 Pushing version number to 1.8.2 2009-08-21 01:09:41 +02:00
46 changed files with 764 additions and 399 deletions

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -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:

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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 ();

View File

@@ -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;

View File

@@ -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.";
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -44,6 +44,7 @@ public:
private:
bool needConfirmation;
bool allConfirmed;
bool quit;
};
#endif

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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

View File

@@ -57,7 +57,7 @@ public:
void setEntry ();
status getStatus ();
status getStatus () const;
void setStatus (status);
int getTagCount ();

View File

@@ -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";

View File

@@ -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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -52,7 +52,7 @@ static std::vector <std::string> customReports;
////////////////////////////////////////////////////////////////////////////////
// This report will eventually become the one report that many others morph into
// 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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -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
///////////////////////////////////////////////////////////////////////////////

View File

@@ -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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -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))

View File

@@ -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
////////////////////////////////////////////////////////////////////////////////

View File

@@ -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
View 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;

View File

@@ -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');

View File

@@ -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};

View File

@@ -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');

View File

@@ -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;

View File

@@ -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
View File

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

View File

@@ -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"`
;;

View File

@@ -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");

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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&);

View File

@@ -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 ();
}

View File

@@ -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);