Compare commits

..

68 Commits

Author SHA1 Message Date
Paul Beckingham
1b6faf57c9 Unit Tests - correction to T::compose tests
- Uses " instead of ' to quote annotations.
2009-04-24 00:09:59 -04:00
Paul Beckingham
76c66b8ab1 Documentation Update
- Added Ubuntu 9.04 (Jaunty Jackalope) to the docs.
2009-04-23 23:59:59 -04:00
Paul Beckingham
fe84ddcc98 Bug Fix - missing report labels in default .taskrc
- Added report labels to the .taskrc file that is created by default,
  when no .taskrc is found.  Thanks to P. C. Shyamshankar.
2009-04-23 23:54:21 -04:00
Paul Beckingham
a77d4662f8 New Version - 1.6.1 2009-04-22 19:50:06 -04:00
Paul Beckingham
60915cefd4 Documentation Update
- Added 1.6.0 rpm download link.
2009-04-22 19:39:56 -04:00
Paul Beckingham
e23243e195 Bug Fix - missing ///g from usage
- Added text describing the ///g substitution feature.
- Updated ChangeLog.
2009-04-13 20:44:35 -04:00
Paul Beckingham
06062a96eb Documentation Update - version 2009-04-12 23:41:05 -04:00
Paul Beckingham
7431f0cdd3 Bug Fix - use " not ' for annotation quoting
- Annotations were using ' to quote text, sanitizing ' -> " inside
  the text.  It would be better to quote with " and sanitize " -> '
  because ' is more common in the text (e.g. "Doesn't work") than ".
2009-04-12 22:53:20 -04:00
Paul Beckingham
22f0b1d9fb Documentation Update
- Added the global modifier of the substitution command to the
  command line grammar file.
2009-04-12 22:35:26 -04:00
Paul Beckingham
b8187e24ae Enhancement - file import
- Added support for configuration variables that override field
  mapping.
- Updated documentation.
2009-04-12 20:29:37 -04:00
Paul Beckingham
120593887b Code Cleanup - unit test warning
- Cleaned up Perl warning in unit test.
2009-04-12 02:17:12 -04:00
Paul Beckingham
01e5e773eb Enhancement - stats report
- Rearranged sequence of stats report to make more sense, with
  counts first and statistics (derived data) last.
2009-04-12 02:10:05 -04:00
Paul Beckingham
e0fd39db7b Enhancement - substitutions /from/to/g
- Added support for the "g" modifier to the substitution command,
  that replace every occurrence of "from" with "to", in the task
  description and any annotations.
2009-04-12 02:01:08 -04:00
Paul Beckingham
a39261f82d Unit tests - substitution
- Added unit tests to ensure that substitution works on the task
  description and annotations.
2009-04-12 01:03:52 -04:00
Paul Beckingham
a6b45af0a2 Enhancement - annotation substitution
- Annotations are now modifiable using the /from/to/ modification
  command.
2009-04-12 00:56:58 -04:00
Paul Beckingham
daea320564 Bug Fix - annual recurrence due-date creep
- Fixed bug where annual recurring tasks exhibit due-date creep
  (thanks to T. Charles Yun).
2009-04-12 00:05:21 -04:00
Paul Beckingham
1cbec205f1 Bug Fix - concatenation of colons in description
- When a task description contained a colon, the two words preceding
  the colon were concatenated.  For example, "aa bb:cc dd" gets
  concatenated to "aabb:cc dd".
- Added unit to test to prevent regression.
- Updated documentation.
2009-04-11 23:02:48 -04:00
Paul Beckingham
579232b7ea Documentation Update
- Cygwin doesn't support all color combinations, which include bold,
  underline and bright background colors.  Added a troubleshooting
  item that describes this problem, and suggests running "task colors"
  to determine just which colors are supported.
2009-04-11 22:39:37 -04:00
Paul Beckingham
cee8fda236 Documentation Update
- added Federico Hernandez to the AUTHORS file for his contribution of
  Red Hat RPM packages.
- Added RPM download links.
2009-04-11 22:18:12 -04:00
Paul Beckingham
4dda1f0c27 Documentation Update
- Added Ubuntu Jaunty Jackalope 9.04 (beta) to the list of supported
  platforms.
2009-04-05 12:26:11 -04:00
Paul Beckingham
0571412da0 Documentation Update
- Added Ubuntu 9.04 (Jaunty Jackalope) to the list of supported systems
  (thanks to Federico Hernandez).
2009-03-30 19:51:13 -04:00
Paul Beckingham
b4f031e4a7 New report columns
- Added support for tag_indicator column.
- Added support for recurrence_indicator column.
2009-03-30 00:29:28 -04:00
Paul Beckingham
5b1d64960d Documentation Update
- Added faq question/answer.
- Clarified file format upgrade via backup warning.
2009-03-29 23:50:00 -04:00
Paul Beckingham
a5fef2cc6b Autoconf
- Removed double check for ncurses, causing the library to be listed
  twice on the link line.
2009-03-29 22:17:14 -04:00
Paul Beckingham
8ab3c1cc3c Bug Fix
- Fixed bug whereby if no columns labels were specified, it was
  considered a column count mismatch.
- Fixed unit tests to use m/d/Y not M/D/Y dateformat.
2009-03-29 21:27:48 -04:00
Paul Beckingham
2700713c03 Merge branch 'import' into 1.6.0
Conflicts:
	ChangeLog
	html/task.html
2009-03-29 18:51:27 -04:00
Paul Beckingham
567bdd98a4 Code Cleanup
- Removed temporary file import samples
2009-03-29 17:43:21 -04:00
Paul Beckingham
25425614b1 File Import
- Implemented all remaining import functionality.
2009-03-29 17:42:11 -04:00
Paul Beckingham
3b65051e9e Unit Tests - labels
- Added unit tests to verify that custom report labels are properly used.
2009-03-29 15:41:09 -04:00
Paul Beckingham
5f4563af2f File Import
- Implemented import from task 1.4.3, 1.5.0, 1.6.0, plain text,
  todo.sh and task command line files.
- Implemented unit tests to cover the above.
2009-03-29 15:34:35 -04:00
Paul Beckingham
7e2bd166fa Code Cleanup
- Guaranteed the correct Config::get call via cast.  There is some
  doubt as to the correct call being made otherwise.  This is not a
  very likely fix, but does eliminate one possibility.
2009-03-29 11:08:32 -04:00
Paul Beckingham
41b60f88d3 Custom Report Labels
- Added the ability to override the labels of custom reports (thanks
  to T. Charles Yun).
2009-03-27 23:51:20 -04:00
Paul Beckingham
93ec320555 Updated FAQ 2009-03-27 22:32:40 -04:00
Paul Beckingham
c1291dc587 Updated Documentation
- Added description of import process.
2009-03-27 22:02:13 -04:00
Paul Beckingham
57deb83b25 Documentation Update
- Added a faq.html file, although it is not so much a set of frequently
  asked questions, but of repeatedly asked questions.  Questions from
  the mail bag, if they apply to a general audience might be shown
  here.
2009-03-27 21:25:34 -04:00
Paul Beckingham
e4f5d6579c File Import
- Updated documentation.
- Added another recognized format - task command line.
2009-03-26 00:52:51 -04:00
Paul Beckingham
99dc72f26f File Import
- Added format identifier code for task 1.4.3, task 1.5.0, todo.sh
  2.0 and CSV.
- Implemented import for type text.
- Implemented util.cpp:slurp function.
- Gathered sample input files for import testing, and later, unit
  tests.
2009-03-26 00:41:15 -04:00
Paul Beckingham
406e648d58 Code Cleanup
- The new 'echo.command' configuration variable was incorrectly
  flagged as obsolete by report.cpp:handleVersion.
2009-03-25 17:49:22 -04:00
Paul Beckingham
db7b2dd9fe File Import
- Implemented scaffolding for new import command.
2009-03-25 02:05:50 -04:00
Paul Beckingham
c31ec6b6a6 Unit Tests - all file formats
- Added unit tests to verify correct parsing of file formats 1, 2
  and 3.
2009-03-25 01:29:59 -04:00
Paul Beckingham
1a656f0f60 Recurring Tasks - upgrades and bug fixes
- Improved (fixed) logical consistency checks that prevent the
  addition of a recurrence frequency without a due date.
- Improved (fixed) logical consistency checks that prevent the
  addition of an until date with a recurrence frequency.
- When a recurring task is modified, all sibling instances, as well
  as the parent task now get modified.
- When a recurring task is appended, all sibling instances, as well
  as the parent task now get modified.
- Updated documentation.
- It is now possible to upgrade a regular task to a recurring task,
  which is triggered by the "recur" attribute.
2009-03-25 01:08:13 -04:00
Paul Beckingham
5ec0d569a9 Enhancement - annotations
- Added annotated description to the active, overdue and next reports.
2009-03-24 23:15:55 -04:00
Paul Beckingham
3979c3283e Enhancement - annotations
- Added support for "annotate" command to annotate existing tasks.
- Bumped file format to version 3, due to the annotations.
- Added unit tests to verify that annotations work.
- Changed 'description' column everywhere to include annotations.
- Added 'description_only' column to exclude the annotations.
- Fixed bug in Table.cpp that calculated the width of multi-line
  columns by using the cell length, instead of the length of the
  longest individual line.
- Updated documentation with new feature.
- Updated documentation with new column.
- Enhanced t.t unit tests to cover format 43
2009-03-24 01:57:12 -04:00
Paul Beckingham
ca795ea281 Bug Fix - confirmation was broken
- Fixed inverted condition that broke confirmation.
2009-03-23 21:55:02 -04:00
Paul Beckingham
d10e9be500 Bug fix - assumed "yes" value, instead of Bool
- Fixed bug where only a value of "yes" would enable confirmation,
  instead of any Boolean true value.
2009-03-22 23:50:42 -04:00
Paul Beckingham
f790df24c5 Enhancement - added echo of id, description
- Added an echo of the ID and description of the task for the start,
  stop, do, undo, delete and undelete commands.  Thanks to Bruce
  Dillahunty.
- Updated documentation.
- Added "echo.command=no" to delete.t, undo.t because the default
  value is "yes", which breaks tests.
- Fixed syntax errors in utf8.t
- Corrected expected number of tests in recur.t
2009-03-22 23:34:17 -04:00
Paul Beckingham
ca933d7f39 Unit Tests - duration
- Corrected unit tests that were mistakenly using 'week' instead of
  'weekly'.
2009-03-20 20:02:59 -04:00
Paul Beckingham
827bc6204b Unit Tests - autocomplete
- Implemented unit test to verify the functionality of the
  util.cpp:autoComplete function.
2009-03-20 20:01:44 -04:00
Paul Beckingham
4537d5048e Unit Tests - confirmation
- Implemented unit tests to very that "confirmation=yes" works.
- Implemented unit tests to very that \n causes a re-prompt.
- Updated docs with regard to this fix, thanks to Bruce Dillahunty.
2009-03-20 18:12:11 -04:00
Paul Beckingham
74ea5b4ef6 Bug Fix - confirmation not processing newline
- Fixed bug where util.cpp:confirm was eating newlines, and not
  rewriting the prompt.  Consequently, after confirm asked the
  question, and the user hit <Enter>, nothing was displayed but
  the newline.  Now uses std::getline.
2009-03-20 16:56:25 -04:00
Paul Beckingham
cc2220b406 Unit Tests - text
- Added unit tests to verify correct functioning of the text utility
  code.
2009-03-19 20:48:02 -04:00
Paul Beckingham
7389ce617a i18n - utf8 support
- Added more unit tests to verify that utf8 projects and tags are
  supported.
2009-03-19 19:46:41 -04:00
Paul Beckingham
165001acac i18n - unit tests
- Added more utf8.t unit tests.
- Removed text.cpp:extractParagraphs, which is not used.
2009-03-19 19:14:01 -04:00
Paul Beckingham
3d3d788961 Recurring Tasks - new "weekdays" frequency
- Added support for "weekdays" as a recurrence frequency, which skips
  Saturday and Sunday, but is otherwise a daily recurrence.  Thanks
  to Chris Pride.
2009-03-18 23:29:25 -04:00
Paul Beckingham
3c196230dd Autoconf
- Added the "-Wall and -pedantic" compiler flags for the non-debug
  build.  The should have been there anyway.
2009-03-18 22:54:59 -04:00
Paul Beckingham
9a350a7dcd Documentation Update
- Updated docs that referenced 1.4.1, failed to include new 1.5.0
  and 1.6.0 commands.
2009-03-17 23:54:56 -04:00
Paul Beckingham
92579e5531 Unit Test - oldest/newest
- Added unit tests to verify oldest and newest reports both work when
  less than 10 tasks are added, and have the correct sort order.
2009-03-17 23:36:00 -04:00
Paul Beckingham
40a538a769 Unit Tests - utf8.t
- Implemented unit tests (more of a demo) that add a bunch of UTF-8
  encoded tasks, as a starting point to updating task to be aware of
  encodings, Unicode etc.
2009-03-17 22:45:53 -04:00
Paul Beckingham
b6e4bc966f Merge branch 'master' of git@github.com:pbeckingham/task into 1.6.0 2009-03-16 22:27:40 -04:00
Paul Beckingham
03815967d2 Documentation Update - task.html
- Added reference to github to allow direct download, clone, fork etc.
2009-03-16 22:26:27 -04:00
Andy Lester
7049bf19d9 Ignore backup files, too
Signed-off-by: Paul Beckingham <paul@beckingham.net>
2009-03-17 10:19:07 +08:00
Paul Beckingham
c69c3bb090 New Command - append
- New "append" command concatenates additional text onto an existing
  task description.
- Added unit tests to append command.
2009-03-16 08:29:09 -04:00
Paul Beckingham
7238d1f1c9 Began 1.6.0 Development 2009-03-16 08:27:24 -04:00
Paul Beckingham
9f82c55c5b Merge branch '1.5.0'
Conflicts:
	html/task.html
2009-03-15 22:26:50 -04:00
Paul Beckingham
857f813a24 - Added the 1.4.3 debian package from Richard Querin. 2008-11-11 08:54:55 -05:00
Paul Beckingham
5498986e15 - Fixed typo. 2008-11-11 00:27:14 -05:00
Paul Beckingham
eb827603c3 - Final tidying up of 1.4.3 loose ends. 2008-11-11 00:10:14 -05:00
Paul Beckingham
b548342acc - Modified docs to include "shadow.notify". 2008-11-02 23:21:41 -05:00
64 changed files with 4560 additions and 447 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ Makefile
configure
config.log
www.xls
*~

View File

@@ -8,11 +8,13 @@ Contributing Authors:
Stefan Dorn
Michael Greb
Benjamin Tegarden
Chris Pride
Richard Querin
Federico Hernandez
With thanks to:
Eugene Kramer
Srijith K
Richard Querin
Bruce Israel
Thomas Engel
Nishiishii
@@ -25,4 +27,7 @@ With thanks to:
Russell Friesenhahn
Paolo Marsi
Eric Farris
Bruce Dillahunty
Askme Too
P.C. Shyamshankar

280
ChangeLog
View File

@@ -1,7 +1,55 @@
------ current release ---------------------------
1.5.0 (3/15/2009)
1.6.1 (4/22/2009)
+ Fixed bug that caused new, first-time .taskrc files to be written without
including the custom report labels (thanks to P.C. Shyamshankar).
------ old releases ------------------------------
1.6.0 (4/12/2009)
+ Added support for new "append" command that adds more description text to
an existing task.
+ Added support for the "weekdays" recurrence, which means a task can recur
five times a week, and not on weekends (thanks to Chris Pride).
+ UTF8 text is now supported in task project names, tags and descriptions.
+ Fixed bug that caused the y/n confirmation on task deletion to ignore the
Enter key and fail to re-prompt (thanks to Bruce Dillahunty).
+ When the "echo.command" configuration variable is set to "yes", it causes
commands that modify tasks to display which task was affected (thanks to
Bruce Dillahunty).
+ A task can now be annotated with the command "task <id> annotate ...", and
a timestamped annotation will appear in reports.
+ A 'description_only' column is now available for use in custom reports,
and it excludes annotations.
+ A task can now be upgraded to a recurring task by adding a recurrence
frequency, a due date, and an optional until date.
+ When a recurring task is modified, all other instances of the recurring
task are also modified.
+ Custom reports now support user-specified column labels (thanks to T.
Charles Yun).
+ Task can now import tasks from a variety of data formats, including task
export files from versions 1.4.3 and earlier, versions 1.5.0 and later,
todo.sh 2.x, CSV, plain text and task command line. See online docs for
full details.
+ Export was including 'id' in the column header even though it was not
included in the data.
+ The task file format has changed slightly. Please back up your task
data files before upgrading to 1.6.0.
+ Added new column 'recurrence_indicator' that displays an 'R' if the task
is a recurring task. This column can be added to any custom report.
+ Added new column 'tag_indicator' that displays a '+' if the task
has any tags. This column can be added to any custom report.
+ Fixed bug where sometimes a task description was concatenated oddly if
there was a colon somewhere in the description.
+ Fixed bug that caused recurring annual tasks to exhibit a creeping due
date, because of an assumption of 365 days per year, which failed to
consider leap years (thanks to T. Charles Yun).
+ Annotations can now be modified with the substitution commands /from/to/.
+ Substitutions can now be made global with /from/to/g and all occurrences
of "from" will be replaced with "to".
1.5.0 (3/15/2009) 87be68e2e83d7bb628be1e5679b16a49a26d3549
+ Removed deprecated TUTORIAL file.
+ Removed "showage" configuration variable.
+ "task stop" can now remove the start time from a started task.
@@ -43,17 +91,15 @@
+ Fixed bug that prevented the summary report from properly reporting
recently completed tasks.
------ old releases ------------------------------
1.4.3 (11/1/2008) 8639e9260646c8c9224e0fc47e5d2443b46eecfc
+ Fixed misleading task count at bottom on "info" report.
+ Added support for a shadow file that contains a plain text task report,
with the "shadow.file" and "shadow.command" configuration variables
with the "shadow.file" and "shadow.command" configuration variables.
The shadow file is automatically updated whenever the task database
changes. Useful for integrating with "Samurize"
changes. Useful for integrating with "Samurize".
+ Task now displays a message whenever a shadow file is updated, if the
"shadow.notify" configuration variable is set "on"
+ Bug: adding a task with a \n, \r or \f in it now fails properly
"shadow.notify" configuration variable is set "on".
+ Bug: adding a task with a \n, \r or \f in it now fails properly.
+ Removed "usage" command, and support for "command.logging" configuration
variable.
+ Added documentation for Shadow files.
@@ -61,111 +107,111 @@
1.4.2 (9/18/2008) e7304e86ce9bb80978c7055fd2a9e999619a6fb8
+ "task undo" can now retract a "task done" command, provided no reports
have been run (and therefore TDB::gc run)
have been run (and therefore TDB::gc run).
+ Task now correctly sorts on entire strings, instead of just the first
character (thanks to Andy Lester)
character (thanks to Andy Lester).
+ Task now uses dashes (-----) to column underlines when color is disabled
(thanks to Vincent Fleuranceau)
(thanks to Vincent Fleuranceau).
+ Task now allows mixed case attribute names (pri:, PRI:, Pri: ...) and
commands (add, ADD, Add ...) (thanks to Vincent Fleuranceau)
commands (add, ADD, Add ...) (thanks to Vincent Fleuranceau).
+ Task now supports a default project and priority for new tasks, via
the new "default.project" and "default.priority" configuration variables
(thanks to Vincent Fleuranceau)
+ Task supports improved word-wrapping to the terminal width
(thanks to Vincent Fleuranceau).
+ Task supports improved word-wrapping to the terminal width.
+ Task now supports "default.command" configuration variable (for example
it could contain "list due:tomorrow") that is the command that is run
whenever task is invoked with no arguments.
+ Task supports modifying the existing description of a task, with the
following syntax: task <id> "new description ...".
+ Bug: Now properly supports relative dates in filters (task list due:eom,
task list due:tomorrow, task list due:23rd ...)
task list due:tomorrow, task list due:23rd ...).
+ Bug: Source now properly includes <string.h> in order to build clean
using gcc 4.3 (thanks to H. İbrahim Güngör)
using gcc 4.3 (thanks to H. İbrahim Güngör).
1.4.1 (7/18/2008) e080c3168c6064628ab85b21bd859d9875a3a9a7
+ Bug: Descriptions can not be altered with "task 123 New description"
+ Tweak: For "task calendar" month names are now centered over the month
+ Removed TUTORIAL file contents in favor of online version
+ Provided Mac .pkg binary
+ Bug: Descriptions can not be altered with "task 123 New description".
+ Tweak: For "task calendar" month names are now centered over the month.
+ Removed TUTORIAL file contents in favor of online version.
+ Provided Mac .pkg binary.
1.4.0 (7/10/2008) 60b7d15a1d22e064acf0974c5d7eabbb57dd8071
+ New recurring tasks feature
+ New recurring tasks feature.
+ "task undelete" can now undelete erroneously deleted tasks, provided no
reports have been run (and therefore TDB::gc run)
+ Added averages to the "task history" report
+ Added ability to override ~/.taskrc with rc:<file>
+ Added bar chart history report "task ghistory"
+ Added task filtering on all reports
+ Automatically shuts off color, curses when output is not a tty
+ Supports relative due: dates (tomorrow, wednesday, 23rd, eom ...)
+ Supports the ~ character in .taskrc data.location
reports have been run (and therefore TDB::gc run).
+ Added averages to the "task history" report.
+ Added ability to override ~/.taskrc with rc:<file>.
+ Added bar chart history report "task ghistory".
+ Added task filtering on all reports.
+ Automatically shuts off color, curses when output is not a tty.
+ Supports relative due: dates (tomorrow, wednesday, 23rd, eom ...).
+ Supports the ~ character in .taskrc data.location.
+ Allows colons on the description, provided what is to the left of the colon
is not a standard attribute name
+ Bug: Fixed where Esc[0m sequences were being emitted for no good reason
+ Bug: Fixed underlined table headers when color is turned off
+ Bug: Adding a blank priority resulted in an assigned garbage value
+ Bug: Fixed parsing of date "07/08/2008" when using dateformat "m/d/Y"
is not a standard attribute name.
+ Bug: Fixed where Esc[0m sequences were being emitted for no good reason.
+ Bug: Fixed underlined table headers when color is turned off.
+ Bug: Adding a blank priority resulted in an assigned garbage value.
+ Bug: Fixed parsing of date "07/08/2008" when using dateformat "m/d/Y".
1.3.1 (6/21/2008) 3a6de7d9402f2609a773a73b16eff97b14a32869
+ New configuration variable, "defaultwidth" that determines the width
of tables when ncurses support is not available
of tables when ncurses support is not available.
+ Bug: "showage" configuration variable should apply to all reports, not
just the ones based on "list"
just the ones based on "list".
+ Bug: Fixed segmentation faults on Ubuntu when the "dateformat"
configuration variables was missing. This was a code bug, and should
have affected more platforms
have affected more platforms.
+ Bug: Task now will recreate a missing ~/.taskrc file, OR a missing
~/.task directory
~/.task directory.
1.3.0 (6/18/2008) 6673e408a223af98c38779c20b08524042c0edfa
+ "task calendar" now displays multiple months per line, adjustable by the
"monthsperline" configuration variable. Feature added by Damian Glenny
+ "task export" can now filter tasks like the reports
+ Factored out code to filter tasks
"monthsperline" configuration variable. Feature added by Damian Glenny.
+ "task export" can now filter tasks like the reports.
+ Factored out code to filter tasks.
+ Displays shorter message when a command is entered incorrectly, and the
full usage for "task help"
+ "task oldest" shows the oldest tasks
+ "task newest" shows the newest tasks
full usage for "task help".
+ "task oldest" shows the oldest tasks.
+ "task newest" shows the newest tasks.
+ Bug: Segmentation fault when no "dateformat" configuration variable
specified
specified.
+ Bug: Fixed bug whereby if you have more than one task with a due date, 7
days gets added to the entry date of task 2..n
+ Bug: Fixed bug whereby "1 wks" was being improperly pluralized
days gets added to the entry date of task 2..n.
+ Bug: Fixed bug whereby "1 wks" was being improperly pluralized.
1.2.0 (6/13/2008) c393d47cdfe7e197a31e94f4bb764474fa05ad8d
+ Bug: "dateformat" configuration variable used to display dates, but
not parse them
not parse them.
+ "task list x" now performs a caseless comparison between "x" and the
description
+ Task sub projects supported
description.
+ Task sub projects supported.
+ "showage" confguration determines whether "Age" column appears on the
"list" and "next" reports
+ Improved TUTORIAL
"list" and "next" reports.
+ Improved TUTORIAL.
1.1.0 (6/7/2008) 73286e86628725b346db2a25fbcd4bd68efb9b3a
+ "blanklines" configuration to stop displaying unnecessary white
space and thus work better on small-screen devices
+ "dateformat" configuration now determines how dates are formatted
+ Better formatting of "task tags" output
+ http://www.beckingham.net/task.html home page set up
+ Added tags to the "task long" report
space and thus work better on small-screen devices.
+ "dateformat" configuration now determines how dates are formatted.
+ Better formatting of "task tags" output.
+ http://www.beckingham.net/task.html home page set up.
+ Added tags to the "task long" report.
1.0.1 (6/4/2008) d216d401217027d93581808fc8944ab7d6b85fb0
+ Bug: UUID generator not properly terminating string.
+ Bug: srandom/srand not called prior to UUID generation.
1.0.0 (6/3/2008) f3de5c07118c597091a05c7d7fe8bdeae95474c1
+ New movie made, uploaded
+ Bug: assertion fails on mobile for t v
+ Bug: configure.ac does not properly determine ncurses availability
+ Bug: Cannot seem to use the percent character in a task description
+ Bug: New installation "task stats" reports newest task 12/31/1969
+ Bug: New installation task projects displays header but no data - should short-circuit
+ Bug: incorrect color specification in sample .taskrc file
+ Bug: when run without arguments, task dumps core on Solaris 10
+ "task calendar" now reports all months with due pending tasks
+ Added rules for colorization by tag, project and keyword
+ Added legend to "task calendar"
+ New movie made, uploaded.
+ Bug: assertion fails on mobile for t v.
+ Bug: configure.ac does not properly determine ncurses availability.
+ Bug: Cannot seem to use the percent character in a task description.
+ Bug: New installation "task stats" reports newest task 12/31/1969.
+ Bug: New installation task projects displays header but no data - should short-circuit.
+ Bug: incorrect color specification in sample .taskrc file.
+ Bug: when run without arguments, task dumps core on Solaris 10.
+ "task calendar" now reports all months with due pending tasks.
+ Added rules for colorization by tag, project and keyword.
+ Added legend to "task calendar".
0.9.9 (5/27/2008) 2ecf50032226c91b406f247417a063dc17c8e324
+ Autoconf/automake behaving properly.
@@ -180,8 +226,8 @@
+ Completed documentation.
0.9.7 (5/24/2008) 25dc4150947a3e612c8118838d04b3bbe68441f7
+ Migrated old compiler flags into Makefile.am
+ Added ncurses endwin function check to configure.ac
+ Migrated old compiler flags into Makefile.am.
+ Added ncurses endwin function check to configure.ac.
+ Set up structure for AUTHORS file.
+ Set up NEWS file, with pleas for feedback.
+ Added welcome message to README.
@@ -192,31 +238,31 @@
+ Removed unnecessary SAMPLE_taskrc, and assorted references.
+ Cleaned up ChangeLog.
+ Minor mods to standard docs.
+ Bumped version to 0.9.7
+ Changed some autoconf details
+ Corrected comment in T.cpp
+ Bumped version to 0.9.7.
+ Changed some autoconf details.
+ Corrected comment in T.cpp.
+ Made unit tests compile and run again.
+ Removed tests from distibution.
0.9.6 (5/13/208)
+ Corrected wrong include file in Table.cpp
+ Corrected wrong include file in Table.cpp.
+ Replaced color management code.
+ Improved color rules code.
0.9.5 (5/12/2008)
+ Replaced Table storage with Grid.
+ Added Grid.cpp to configure.ac
+ Added Makefile to src/.gitignore
+ Added Grid.cpp to configure.ac.
+ Added Makefile to src/.gitignore.
+ Makefile should not be part of the repository.
+ Added Grid.cpp
+ Added Grid::Cell::operator==
+ Added Grid.cpp.
+ Added Grid::Cell::operator==.
+ ChangeLog file begun.
+ Bumped version to 0.9.5 for next release.
0.9.4 (4/26/2008)
+ Integrated new Grid object into build - not yet integrated into Table.
+ More .gitignore tweaks.
+ Added .gitignore
+ Added .gitignore.
+ Added more missing files.
+ Added all source code.
+ Generic OSS files added.
@@ -237,29 +283,29 @@
+ Consolidated header files, removed unnecessary ones.
0.9.0 (3/23/2008)
+ flat source directory
+ autoconf complete
+ "task next"
+ "task stats"
+ "task export"
+ Rules-based colorization
+ flat source directory.
+ autoconf complete.
+ "task next".
+ "task stats".
+ "task export".
+ Rules-based colorization.
0.8.1 (1/28/2008) - 0.8.16 (3/13/2008)
+ autoconf conversion (many builds)
+ autoconf conversion (many builds).
0.8.0 Polish (1/25/2008)
+ Code cleanup, reorganization
+ "task overdue"
+ Add "age" column to list and long
+ Use 'conf' for build, version tracking
+ Add "/from/to/" description editing
+ Code cleanup, reorganization.
+ "task overdue".
+ Add "age" column to list and long.
+ Use 'conf' for build, version tracking.
+ Add "/from/to/" description editing.
0.7.0 Multi-user, File handling, atomicity (1/8/2008)
+ Clean, publishable API reimplementation
+ File locking
+ retain deleted tasks
+ "task info ID" report showing all metadata
+ File format v2, including UUID
+ Clean, publishable API reimplementation.
+ File locking.
+ retain deleted tasks.
+ "task info ID" report showing all metadata.
+ File format v2, including UUID.
[Development hiatus while planning for T, TDB API, new features and the future
of the project. Seeded to two testers for feedback, suggestions. Development
@@ -267,40 +313,40 @@ deliberately stopped to allow extended use of task, allowing command logging and
regular usage to determine which features were needed or unnecessary.]
0.6.0 Reports (12/27/2006)
+ "task history"
+ "task summary"
+ "task calendar"
+ due support
+ Table sorting
+ "task history".
+ "task summary".
+ "task calendar".
+ due support.
+ Table sorting.
0.5.0 Multi-user support (12/10/2006)
+ Command logging
+ "task usage" report
+ Command logging.
+ "task usage" report.
0.4.0 Destructive / modification commands (12/3/2006)
+ "task delete" complete
+ "task id ..." complete
+ "task list ..." synonym for "task find ..."
+ "task delete" complete.
+ "task id ..." complete.
+ "task list ..." synonym for "task find ...".
0.3.0 Work in progress support (12/3/2006)
+ "task start" complete
+ "task done" complete
+ completed.data support
+ "task start" complete.
+ "task done" complete.
+ completed.data support.
0.2.0 Neutral commands (12/2/2006)
+ "task find" complete
+ "task projects" complete
+ "task tags" complete
+ "task find" complete.
+ "task projects" complete.
+ "task tags" complete.
0.1.0 Constructive commands (12/1/2006)
+ "task add" complete
+ completed.data support
+ ~/.taskrc support
+ "task add" complete.
+ completed.data support.
+ ~/.taskrc support.
0.0.1 Basic infrastructure (11/29/2006)
+ Command line parsing
+ API layer
+ Usage
+ Command line parsing.
+ API layer.
+ Usage.
------ start -----------------------------------

5
NEWS
View File

@@ -1,4 +1,4 @@
Welcome to Task 1.5.0.
Welcome to Task 1.6.1.
Task has been built and tested on the following configurations:
@@ -9,7 +9,8 @@ Task has been built and tested on the following configurations:
- Fedora Core 10
- Ubuntu 7 Feisty Fawn
- Ubuntu 8 Hardy Heron
- Ubunto 8.10 Intrepid Ibex
- Ubuntu 8.10 Intrepid Ibex
- Ubuntu 9.04 Jaunty Jackalope
- Solaris 10
- Cygwin 1.5.25-14

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.61)
AC_INIT(task, 1.5.0, bugs@beckingham.net)
AC_INIT(task, 1.6.1, bugs@beckingham.net)
CFLAGS="${CFLAGS=}"
CXXFLAGS="${CXXFLAGS=}"
@@ -18,7 +18,7 @@ if test "$enable_debug" = "yes"; then
CXXFLAGS="$CFLAGS -Wall -pedantic -ggdb3 -DDEBUG"
AC_MSG_RESULT(yes)
else
CXXFLAGS="$CFLAGS -O3"
CXXFLAGS="$CFLAGS -Wall -pedantic -O3"
AC_MSG_RESULT(no)
fi
@@ -45,7 +45,6 @@ AC_SUBST(CFLAGS)
# Checks for libraries.
AC_CHECK_LIB(ncurses,initscr)
AC_CHECK_LIB(ncurses,endwin)
# Checks for header files.
AC_HEADER_STDC

View File

@@ -36,5 +36,5 @@ word ::=
file ::=
id ::= digit+ ;
digit ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
substitution ::= "/" word+ "/" word* "/" ;
substitution ::= "/" word+ "/" word* "/" "g"? ;

View File

@@ -97,6 +97,30 @@ Car 2 2 wks 25% XXXXXXXXX</code></pre>
indicating that percentage.
</p>
<strong>% task &lt;id&gt; append ...</strong>
<p>
Appends the additional description to an existing task.
</p>
<strong>% task annotate &lt;id&gt; additional note...</strong>
<p>
Allows an annotation to be attached to an existing task. Each
annotation has a time stamp, and when displayed, the annotations
are shown under the task description. For example:
</p>
<pre><code>% task add Go to the supermarket
% task annotate 1 need milk
% task ls
ID Project Pri Due Active Age Description
1 Go to the supermarket
3/23/2009 need milk</code></pre>
<p>
The date of the annotation uses the "dateformat" configuration
variable.
</p>
<strong>% task delete &lt;id&gt;</strong>
<p>
There are two ways of getting rid of tasks - mark them as done, or
@@ -120,6 +144,13 @@ Car 2 2 wks 25% XXXXXXXXX</code></pre>
This is how a task is marked as done.
</p>
<strong>% task undo &lt;id&gt;</strong>
<p>
If a task was recently marked as done, and no report has been run, it
may be possible to cancel the completed status of the task as though
"task done ..." was never run.
</p>
<strong>% task list ...</strong>
<p>
The list report will show the active status, and age of the task in
@@ -258,7 +289,7 @@ ID Project Pri Description
set via the "newest" configuration variable.
</p>
<strong>% task /from/to/</strong>
<strong>% task &lt;id&gt; /from/to/</strong>
<p>
If a task has been entered with a typo, it can be easily corrected
by this command. For example:
@@ -278,7 +309,20 @@ ID Project Pri Description
...</code></pre>
<p>
This command makes single corrections to a task description.
This command makes a single correction to the first occurrence of
"from" in a task description.
</p>
<p>
If a task is annotated, the annotation can also be modified using
this command.
</p>
<strong>% task &lt;id&gt; /from/to/g</strong>
<p>
The "g" modifier to the substitution command causes every occurrence
of "from" to be replaced with "to", in both the description and any
annotations.
</p>
<strong>% task tags</strong>

View File

@@ -58,6 +58,13 @@
confirmation before deleting a task.
</dd>
<dt>echo.command</dt>
<dd>
May be "yes" or "no", and causes task to display the ID and
description of any task when you run the start, stop, do, undo,
delete and undelete commands. The default value is "yes".
</dd>
<dt>nag</dt>
<dd>
This may be a string of text, or blank. It is used as a prompt
@@ -330,6 +337,28 @@ ID Project Pri Description
</p>
</dd>
<dt>import.synonym.id</dt>
<dt>import.synonym.uuid</dt>
<dt>import.synonym.status</dt>
<dt>import.synonym.tags</dt>
<dt>import.synonym.entry</dt>
<dt>import.synonym.start</dt>
<dt>import.synonym.due</dt>
<dt>import.synonym.recur</dt>
<dt>import.synonym.end</dt>
<dt>import.synonym.project</dt>
<dt>import.synonym.priority</dt>
<dt>import.synonym.fg</dt>
<dt>import.synonym.bg</dt>
<dt>import.synonym.description</dt>
<dd>
If any of these configuration variables are found, they influence
data import by specifying a single additional field name synonym.
If a data import is failing because certain column names are not
being recognized, then this is how the field mapping can be
controlled.
</dd>
<p>
Note that the command:
</p>
@@ -340,6 +369,8 @@ ID Project Pri Description
will display the configuration variables found in the .taskrc file,
and will warn you of any variables that are not recognized.
</p>
</div>
<br />

View File

@@ -88,6 +88,18 @@ report.mine.sort=priority-,project+</pre></code>
in a report:
</p>
<p>
It is also possible to override the default columns names, if
the following line is added to your .taskrc file:
</p>
<pre><code>report.mine.labels=ID,Project,Priority,Description of task</code></pre>
<p>
Note that there must be the same number of labels as there are
columns to label, and they must appear in the same sequence.
</p>
<ul>
<li>id
<li>uuid
@@ -100,7 +112,10 @@ report.mine.sort=priority-,project+</pre></code>
<li>active
<li>tags
<li>recur
<li>description_only
<li>description
<li>tag_indicator
<li>recurrence_indicator
</ul>
<p>

175
html/faq.html Normal file
View File

@@ -0,0 +1,175 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Frequently Asked Questions</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" />
</head>
<body>
<div id="container">
<table>
<tr>
<td>
<div id="toolbar">
<a href="task.html">Home</a>
<a href="setup.html">Setup</a>
<a href="30second.html">30-second Tutorial</a>
<a href="simple.html">Simple</a>
<a href="advanced.html">Advanced</a>
<a href="shell.html">Shell</a>
<a href="config.html">Configuration</a>
<a href="color.html">Colors</a>
<a href="usage.html">Usage</a>
<a href="recur.html">Recurrence</a>
<a href="date.html">Date Handling</a>
<a href="troubleshooting.html">Troubleshooting</a>
<a href="versions.html">Old Versions</a>
<a href="links.html">Task on the Web</a>
</div>
<div id="content">
<br />
<br />
<br />
<h2 class="title">Frequently Asked Questions</h2>
<div class="content">
<p>
(Actually, that's a misnomer. These are really Repeatedly Asked
Questions.)
</p>
<p>
<b>
Q: When I redirect the output of task to a file, I lose all
the colors. How do I fix this?
</b>
<br />
A: Task knows (or thinks it knows) when the output is not going
directly to a terminal, and strips out all the color control
characters. Prevent this with the following entry in your
.taskrc file:
<pre><code>_forcecolor=on</code></pre>
</p>
<hr>
<p>
<b>
Q: How do I backup my task data files? Where are they?
</b>
<br />
A: Task writes all pending tasks to the file ~/.task/pending.data
and all completed and deleted tasks to ~/.task/completed.data.
They are text files, so they can just be copied to another
location for safekeeping. Don't forget there is also the
~/.taskrc file that contains your task configuration data.
</p>
<hr>
<p>
<b>
Q: How can I separate my work tasks from my home tasks?
Specifically, can I keep them completely separate?
</b>
<br />
A: You can do this by creating an alternate .taskrc file,
then using shell aliases. Here is are example Bash
commands to achieve this:
<pre><code>% cp ~/.taskrc ~/.taskrc_home
% (now edit .taskrc_home to change the value of data.location)
% alias wtask="task"
% alias htask="task rc:~/.taskrc_home"</code></pre>
This gives you two commands, 'wtask' and 'htask' that
operate using two different sets of task data files.
</p>
<hr>
<p>
<b>
Q: Can I revert to a previous version of task? How?
</b>
<br />
A: Yes, you can revert to a previous version of task,
simply by downloading an
<a href="versions.html">older version</a> and
installing it. If you find a bug in task, then this
may be the only way to work around the bug, until a
new release is made.
</p>
<p>
Note that it is possible that the task file format will
change. For example, the format changed between versions
1.5.0 and 1.6.0. Task will automatically upgrade the file
but if you need to revert to a previous version of task,
there is the file format to consider. This is yet another
good reason to back up your task data files!
</p>
<hr>
<!--
<p>
<b>
Q:
</b>
<br />
A:
</p>
<hr>
-->
</div>
<br />
<br />
<div class="content">
<p>
Copyright 2006-2009, P. Beckingham. All rights reserved.
</p>
</div>
</div>
</td>
<td align="right" valign="top" width="200px">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<script type="text/javascript"><!--
google_ad_client = "pub-9709799404235424";
/* Task Main */
google_ad_slot = "8660617875";
google_ad_width = 120;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4737637-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
</body>
</html>

158
html/import.html Normal file
View File

@@ -0,0 +1,158 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Data Import</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="task.css" type="text/css" />
</head>
<body>
<div id="container">
<table>
<tr>
<td>
<div id="toolbar">
<a href="task.html">Home</a>
<a href="setup.html">Setup</a>
<a href="30second.html">30-second Tutorial</a>
<a href="simple.html">Simple</a>
<a href="advanced.html">Advanced</a>
<a href="shell.html">Shell</a>
<a href="config.html">Configuration</a>
<a href="color.html">Colors</a>
<a href="usage.html">Usage</a>
<a href="recur.html">Recurrence</a>
<a href="date.html">Date Handling</a>
<a href="troubleshooting.html">Troubleshooting</a>
<a href="versions.html">Old Versions</a>
<a href="links.html">Task on the Web</a>
</div>
<div id="content">
<br />
<br />
<br />
<h2 class="title">Data Import</h2>
<div class="content">
<p>
Tasks can be imported from files with this command:
<pre><code>% task import file</code></pre>
A variety of different file types are recognized by task, namely:
<ul>
<li>Tasks exported from task prior to version 1.5.0.
<li>Tasks exported from task version 1.5.0 and later. The file
format changed with 1.5.0.
<li>todo.sh files.
<li>CSV files with a variety of recognized column names.
<li>Plain text files, with one task listed per line.
<li>Task command line format.
</ul>
</p>
<p>
Task makes a good effort to determine which of these formats a
file is. It does this by reading the file, and looking for
familiar patterns. For example, the easiest files to recognize
are those exported from task itself, because they all have a
header line that comes in only three variations. Other formats
are a little harder to identify, but they all have their own
identifying characteristics.
</p>
<p>
The most complex import is when a CSV file is recognized.
Task needs a field header line in order to map columns to task
data items. For example, the if the following file is
imported:
</p>
<pre><code>number,status,task
1,pending,task one
2,pending,task two</code></pre>
<p>
Task will map the "number" field to task's "id" field, etc,
based on name. Task has a list of synonyms that it uses to
map fields, but you can specify your own override with any of
the following configuration variables:
</p>
<ul>
<li>import.synonym.id
<li>import.synonym.uuid
<li>import.synonym.status
<li>import.synonym.tags
<li>import.synonym.entry
<li>import.synonym.start
<li>import.synonym.due
<li>import.synonym.recur
<li>import.synonym.end
<li>import.synonym.project
<li>import.synonym.priority
<li>import.synonym.fg
<li>import.synonym.bg
<li>import.synonym.description
</ul>
<p>
Please note that it is wise to backup your task data files
before an import.
</p>
</div>
<br />
<br />
<div class="content">
<p>
Copyright 2006-2009, P. Beckingham. All rights reserved.
</p>
</div>
</div>
</td>
<td align="right" valign="top" width="200px">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<script type="text/javascript"><!--
google_ad_client = "pub-9709799404235424";
/* Task Main */
google_ad_slot = "8660617875";
google_ad_width = 120;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4737637-1");
pageTracker._initData();
pageTracker._trackPageview();
</script>
</body>
</html>

View File

@@ -101,6 +101,13 @@ Permanently delete task? (y/n) y
This is a recurring task. Do you want to delete all pending
recurrences of this same task? (y/n) y</code></pre>
<h4>Modification</h4>
<p>
When a recurring task is modified, all the other recurring task instances will
be modified. For example, if you raise the priority of one of the recurring
task instances, all will be modified.
</p>
<h4>Recurrence Periods</h4>
<p>
In the above examples, the recurrence period was specified as "monthly" and
@@ -116,6 +123,13 @@ recurrences of this same task? (y/n) y</code></pre>
<td class="table_d">daily, day, 1d, 2d ...</td>
<td class="table_d">Every day, or a number of days</td>
</tr>
<tr>
<td class="table_d">weekdays</td>
<td class="table_d">
Monday, Tuesday, Wednesday, Thursday and Friday,
skipping weekend days
</td>
</tr>
<tr>
<td class="table_d">weekly, 1w, 2w ...</td>
<td class="table_d">Every week, or a number of weeks</td>

View File

@@ -40,10 +40,10 @@
</p>
<pre><code>% ls
task-1.4.1.tar.gz
% gunzip task-1.4.1.tar.gz
% tar xf task-1.4.1.tar
% cd task-1.4.1
task-1.6.0.tar.gz
% gunzip task-1.6.0.tar.gz
% tar xf task-1.6.0.tar
% cd task-1.6.0
% ./configure
...
% make

View File

@@ -53,15 +53,18 @@
<p>
To use a shadow file, edit your .taskrc configuration file,
and add two entries as shown:
and add three entries as shown:
</p>
<pre><code>shadow.file=/path/to/file
shadow.command=list pri:H</code></pre>
shadow.command=list pri:H
shadow.notify=on</code></pre>
<p>
In this example the shadow file contains a report equivalent
to running "task list pri:H".
to running "task list pri:H". Note that the third entry
causes a message to be displayed whenever task updates the
shadow file. It is optional.
</p>
<p>

View File

@@ -58,6 +58,8 @@
<li><a href="filter.html">Filters</a>
<li><a href="shadow.html">Shadow Files</a>
<li><a href="custom.html">Custom Reports</a>
<li><a href="import.html">Data Import</a>
<li><a href="faq.html">Frequently Asked Questions</a>
</ul>
<p>
@@ -75,74 +77,47 @@
<div class="content">
<table>
<tr>
<td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.tar.gz">task-1.5.0.tar.gz</a></td>
</tr>
<!--
<tr>
<td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.6.0.tar.gz">task-1.6.0.tar.gz</a></td>
</tr>
<tr>
<td>Mac OS X 10.5 (Leopard) Intel-only:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.pkg">task-1.5.0.pkg</a></td>
<td><a href="http://www.beckingham.net/task-1.6.0.pkg">task-1.6.0.pkg</a></td>
</tr>
<tr>
<td>
Debian package:
(Thanks to <a href="http://blog.rfquerin.org">Richard Querin</a>):
Debian:
(Thanks to <a href="http://blog.rfquerin.org">Richard&nbsp;Querin</a>):
</td>
<td><a href="http://www.beckingham.net/task_1.5.0-1_i386.deb">task_1.5.0-1_i386.deb</a></td>
<td><a href="http://www.beckingham.net/task_1.6.0-1_i386.deb">task_1.6.0-1_i386.deb</a></td>
</tr>
<tr>
<td>
Red Hat:
(Thanks to <a href="http://www.ultrafredde.com">Federico&nbsp;Hernandez</a>):
</td>
<td><a href="http://www.beckingham.net/task-1.6.0-1.FC10.i386.rpm">task-1.6.0-1.FC10.i386.rpm</a></td>
</tr>
-->
<tr>
<td>Git - get the whole source and history:</td>
<td><a href="http://github.com/pbeckingham/task">http://github.com/pbeckingham/task</a></td>
</tr>
</table>
<h4>New in version 1.5.0 (3/15/2009)</h4>
<h4>New in version 1.6.1 (4/22/2009)</h4>
<ul>
<li>Removed deprecated TUTORIAL file.
<li>Removed support for the "showage" configuration variable.
<li>"task stop" can remove the start time from a started task.
<li>"task ghistory" now displays a differently aligned graph, allowing
easier comparison by month of tasks added versus completed and deleted.
<li>"task version" command now reports unrecognized configuration variables,
which may be spelling mistakes or deprecated variables.
<li>"configure --enable-debug" now supported to suppress compiler optimization
to allow debugging.
<li>Allow lower case priorities, and automatically upper case them.
<li>Added support for "due" configuration variable which defines the number
of days in the future when a task is considered due.
<li>Added support for custom reports, comprised of a set of column names and
sort order, with optional filtering in the configuration file. This
means user-defined reports can be written, and the reports currently
in the configuration file can be renamed. Several of task's built in
reports have been converted to user-defined reports.
<li>New online documentation for custom reports.
<li>New algorithm for determining when the "nag" message is displayed.
<li>Fixed bug where task hangs with a certain combination of recurring tasks
and shadow files.
<li>Fixed bug with the task sort algorithm, which led to an unstable sequence
when there were only a handful of tasks.
<li>Performance enhanced by eliminating unnecessary sorting.
<li>Task now has a large (and growing) test suite and bug regression tests
to help ensure higher quality releases.
<li>Fixed bug that caused large performance hit during table rendering.
<li>Fixed bug that concatenated a modified description without spaces.
<li>Added new column 'recur' that displays the recurrence period of any
recurring tasks. This column can be added to any custom report.
<li>Added support for "color.recurring" configuration variable which
specifies the color of recurring tasks.
<li>Added support for "locking" configuration variable that controls whether
file locking is used.
<li>Task export feature now includes recurrence information, removes nested
quotes, and limits output to pending tasks.
<li>Task no longer includes deleted tasks in the summary report (thanks to
Benjamin Tegarden).
<li>Fixed bug that prevented the summary report from properly reporting
recently completed tasks.
<li>Fixed bug that caused new, first-time .taskrc files to be written without
including the custom report labels (thanks to P.C. Shyamshankar).
</ul>
<p>
(Find out <a href="versions.html">what was new in prior versions</a>)
</p>
<!--
<h2>Task 1.5.0 Beta</h2>
<h2>Task 1.6.1 Beta</h2>
<p>
The next version of task is in beta. This means it is approaching the
end of the current development and testing cycle, and feedback from
@@ -162,7 +137,7 @@
<table>
<tr>
<td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.5.0beta.tar.gz">task-1.5.0beta.tar.gz</a></td>
<td><a href="http://www.beckingham.net/task-1.6.1beta.tar.gz">task-1.6.1beta.tar.gz</a></td>
</tr>
</table>
-->
@@ -181,6 +156,7 @@
<li>Ubuntu 7 Feisty Fawn
<li>Ubuntu 8 Hardy Heron
<li>Ubuntu 8.10 Intrepid Ibex
<li>Ubuntu 9.04 Jaunty Jackalope
<li>Solaris 10
<li>Cygwin 1.5.25-14
</ul>

View File

@@ -83,6 +83,24 @@
</p>
</div>
<br />
<h2 class="title">Do colors work under Cygwin?</h2>
<div class="content">
<p>
They do, but only in a limited way. You can use regular
foreground colors (black, red, green ...) and you can
regular background colors (on_black, on_red, on_green ...),
but underline and bold are not supported.
</p>
<p>
If you run the command:
<code><pre>% task colors</pre></code>
Task will display all the colors it can use, and you will
see which ones you can use.
</p>
</div>
<br />
<br />
<div class="content">

View File

@@ -36,6 +36,8 @@
<div class="content">
<pre><code>Usage: task
task add [tags] [attrs] desc...
task append [tags] [attrs] desc...
task annotate ID desc...
task completed [tags] [attrs] desc...
task ID [tags] [attrs] [desc...]
task ID /from/to/

View File

@@ -36,6 +36,153 @@
<br />
<div class="content">
<h4>New in version 1.6.0 (4/13/2009)</h4>
<p>
<table>
<tr>
<td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.6.0.tar.gz">task-1.6.0.tar.gz</a></td>
</tr>
<!--
<tr>
<td>Mac OS X 10.5 (Leopard) Intel-only:</td>
<td><a href="http://www.beckingham.net/task-1.6.0.pkg">task-1.6.0.pkg</a></td>
</tr>
<tr>
<td>
Debian:
(Thanks to <a href="http://blog.rfquerin.org">Richard&nbsp;Querin</a>):
</td>
<td><a href="http://www.beckingham.net/task_1.6.0-1_i386.deb">task_1.6.0-1_i386.deb</a></td>
</tr>
-->
<tr>
<td>
Red Hat:
(Thanks to <a href="http://www.ultrafredde.com">Federico&nbsp;Hernandez</a>):
</td>
<td><a href="http://www.beckingham.net/task-1.6.0-1.FC10.i386.rpm">task-1.6.0-1.FC10.i386.rpm</a></td>
</tr>
<tr>
<td>Git - get the whole source and history:</td>
<td><a href="http://github.com/pbeckingham/task">http://github.com/pbeckingham/task</a></td>
</tr>
</table>
<ul>
<li>Added support for new "append" command that adds more description text to
an existing task.
<li>Added support for the "weekdays" recurrence, which means a task can recur
five times a week, and not on weekends (thanks to Chris Pride).
<li>UTF8 text is now supported in task project names, tags and descriptions.
<li>Fixed bug that caused the y/n confirmation on task deletion to ignore the
Enter key and fail to re-prompt (thanks to Bruce Dillahunty).
<li>When the "echo.command" configuration variable is set to "yes", it causes
commands that modify tasks to display which task was affected (thanks to
Bruce Dillahunty).
<li>A task can now be annotated with the command "task <id> annotate ...", and
a timestamped annotation will appear in reports.
<li>A 'description_only' column is now available for use in custom reports,
and it excludes annotations.
<li>A task can now be upgraded to a recurring task by adding a recurrence
frequency, a due date, and an optional until date.
<li>When a recurring task is modified, all other instances of the recurring
task are also modified.
<li>Custom reports now support user-specified column labels (thanks to T.
Charles Yun).
<li>Task can now import tasks from a variety of data formats, including task
export files from versions 1.4.3 and earlier, versions 1.5.0 and later,
todo.sh 2.x, CSV, plain text and task command line. See online docs for
full details.
<li>Export was including 'id' in the column header even though it was not
included in the data.
<li>The task file format has changed slightly. Please back up your task
data files before upgrading to 1.6.0.
<li>Added new column 'recurrence_indicator' that displays an 'R' if the task
is a recurring task. This column can be added to any custom report.
<li>Added new column 'tag_indicator' that displays a '+' if the task
has any tags. This column can be added to any custom report.
<li>Fixed bug where sometimes a task description was concatenated oddly if
there was a colon somewhere in the description.
<li>Fixed bug that caused recurring annual tasks to exhibit a creeping due
date, because of an assumption of 365 days per year, which failed to
consider leap years (thanks to T. Charles Yun).
<li>Annotations can now be modified with the substitution commands /from/to/.
<li>Substitutions can now be made global with /from/to/g and all occurrences
of "from" will be replaced with "to".
</ul>
</p>
<p>
<h4>New in version 1.5.0 (3/15/2009)</h4>
<table>
<tr>
<td>Source:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.tar.gz">task-1.5.0.tar.gz</a></td>
</tr>
<tr>
<td>Mac OS X 10.5 (Leopard) Intel-only:</td>
<td><a href="http://www.beckingham.net/task-1.5.0.pkg">task-1.5.0.pkg</a></td>
</tr>
<tr>
<td>
Debian:
(Thanks to <a href="http://blog.rfquerin.org">Richard&nbsp;Querin</a>):
</td>
<td><a href="http://www.beckingham.net/task_1.5.0-1_i386.deb">task_1.5.0-1_i386.deb</a></td>
</tr>
<tr>
<td>
Red Hat:
(Thanks to <a href="http://www.ultrafredde.com">Federico&nbsp;Hernandez</a>):
</td>
<td><a href="http://www.beckingham.net/task-1.5.0-1.i386.rpm">task-1.5.0-1.i386.rpm</a></td>
</tr>
</table>
<ul>
<li>Removed deprecated TUTORIAL file.
<li>Removed support for the "showage" configuration variable.
<li>"task stop" can remove the start time from a started task.
<li>"task ghistory" now displays a differently aligned graph, allowing
easier comparison by month of tasks added versus completed and deleted.
<li>"task version" command now reports unrecognized configuration variables,
which may be spelling mistakes or deprecated variables.
<li>"configure --enable-debug" now supported to suppress compiler optimization
to allow debugging.
<li>Allow lower case priorities, and automatically upper case them.
<li>Added support for "due" configuration variable which defines the number
of days in the future when a task is considered due.
<li>Added support for custom reports, comprised of a set of column names and
sort order, with optional filtering in the configuration file. This
means user-defined reports can be written, and the reports currently
in the configuration file can be renamed. Several of task's built in
reports have been converted to user-defined reports.
<li>New online documentation for custom reports.
<li>New algorithm for determining when the "nag" message is displayed.
<li>Fixed bug where task hangs with a certain combination of recurring tasks
and shadow files.
<li>Fixed bug with the task sort algorithm, which led to an unstable sequence
when there were only a handful of tasks.
<li>Performance enhanced by eliminating unnecessary sorting.
<li>Task now has a large (and growing) test suite and bug regression tests
to help ensure higher quality releases.
<li>Fixed bug that caused large performance hit during table rendering.
<li>Fixed bug that concatenated a modified description without spaces.
<li>Added new column 'recur' that displays the recurrence period of any
recurring tasks. This column can be added to any custom report.
<li>Added support for "color.recurring" configuration variable which
specifies the color of recurring tasks.
<li>Added support for "locking" configuration variable that controls whether
file locking is used.
<li>Task export feature now includes recurrence information, removes nested
quotes, and limits output to pending tasks.
<li>Task no longer includes deleted tasks in the summary report (thanks to
Benjamin Tegarden).
<li>Fixed bug that prevented the summary report from properly reporting
recently completed tasks.
</ul>
</p>
<p>
<h4>New in version 1.4.3 (11/1/2008)</h4>
<a href="http://www.beckingham.net/task-1.4.3.tar.gz">task-1.4.3.tar.gz</a>
@@ -47,6 +194,7 @@
(Thanks to <a href="http://blog.rfquerin.org">Richard Querin</a>)
</p>
<p>
<ul>
<li>Fixed misleading task count at bottom of "info" report.
<li>Added support for a shadow file that contains a plain text task report,

View File

@@ -1,109 +1,109 @@
Hello, and welcome to this quick demo of the task program.
Hello, and welcome to this quick demo of the task program.
task add do laundry Let's add some tasks
I need to do laundry
task add do laundry Let's add some tasks
I need to do laundry
task add project:garage order dumpster Oh yeah, I need to order the dumpster
task add project:garage order dumpster Oh yeah, I need to order the dumpster
task add +phone tell mom i loveher Must call Mom (that "phone" there is a tag - they can
be useful for searching and categorizing)
task add +phone tell mom i loveher Must call Mom (that "phone" there is a tag - they can
be useful for searching and categorizing)
task add +phone pro:garage schedule
goodwill pickup
task ad +email pro:garage ask Tom if Notice I can abbreviate commands
task ad +email pro:garage ask Tom if Notice I can abbreviate commands
he wants that old bkie
task ls Let's see what we've got
Oh, I spelled bike wrong
task ls Let's see what we've got
Oh, I spelled bike wrong
task 5 /bkie/bike/
task ls That's better
task ls That's better
task 1 pro:home Let's assign projects
task 1 pro:home Let's assign projects
task 3 pro:home tell mom I love her
task ls pro:garage
task long pro:garage Let's see all the columns
task long pro:garage Let's see all the columns
task list pro:garage There are different ways to list
task list pro:garage There are different ways to list
task lis +phone By tag
task li pro:garage +phone By project and tag
task l mom By word
task lis +phone By tag
task li pro:garage +phone By project and tag
task l mom By word
task 1 priority:H Priorities can be High, Medium or Low
task 1 priority:H Priorities can be High, Medium or Low
task pri:H 3
task 1 pri:M
task li The list is sorted by priority.
task li The list is sorted by priority.
task 2 pri:L
task li
task done 3 Suppose task 3 is done
task li ...and it's gone
task done 3 Suppose task 3 is done
task li ...and it's gone
task 2 +phone +mistake Lets add tags
task 2 +phone +mistake Lets add tags
# Oops!
task 2 -mistake or remove tags
task 2 -mistake or remove tags
task tags or look at all the tags
task tags or look at all the tags
task info 2 or all the details
task info 2 or all the details
task projects or all the projects
task projects or all the projects
task 3 fg:bold Let's make it colorful
task 3 fg:bold Let's make it colorful
task 4 fg:bold_green
task li
task 3 fg:bold_underline_white
task li
task 4 bg:on_bright_red fg:bold_yellow
task li Oh that's just nasty - let's get rid of that.
task li Oh that's just nasty - let's get rid of that.
task 4 bg:
task li
task 4 fg:
task 3 fg:
task colors There are many combinations to choose from
task colors There are many combinations to choose from
(Slashes!!!)
task 1 due:6/8/2008 Let's add a due date
task 1 due:6/8/2008 Let's add a due date
date
task li
task calendar Notice the due task is in yellow, today is marked cyan
task calendar Notice the due task is in yellow, today is marked cyan
task 1 due:5/20/2008 This is now an overdue task
task li and it shows up red
task 1 due:5/20/2008 This is now an overdue task
task li and it shows up red
task overdue
task cal
task export file.csv You can export the tasks to a spreadsheet
task export file.csv You can export the tasks to a spreadsheet
cat file.csv
task start 1 Started tasks can be used as reminders
of what you are supposed to be doing
task start 1 Started tasks can be used as reminders
of what you are supposed to be doing
task active They show up as active
task done 1 Let's clear out a couple
task active They show up as active
task done 1 Let's clear out a couple
task li
task done 3
task active
task summary Summary shows progress on all projects
task summary Summary shows progress on all projects
task history History shows general activity - how many added,
completed etc, by month
task history History shows general activity - how many added,
completed etc, by month
task ghistory This report shows a histogram of tasks that were
added (in red), completed (in green) and deleted
(in yellow), all by month.
task ghistory This report shows a histogram of tasks that were
added (in red), completed (in green) and deleted
(in yellow), all by month.
And that's it. There are more commands than this
covered in the online documentation, but this should give
the basic idea.
And that's it. There are more commands than this
covered in the online documentation, but this should give
the basic idea.
Thank you for watching.
Thank you for watching.

View File

@@ -46,23 +46,28 @@ Config::Config ()
{
(*this)["report.long.description"] = "Lists all task, all data, matching the specified criteria";
(*this)["report.long.columns"] = "id,project,priority,entry,start,due,recur,age,tags,description";
(*this)["report.long.labels"] = "ID,Project,Pri,Added,Started,Due,Recur,Age,Tags,Description";
(*this)["report.long.sort"] = "due+,priority-,project+";
(*this)["report.list.description"] = "Lists all tasks matching the specified criteria";
(*this)["report.list.columns"] = "id,project,priority,due,active,age,description";
(*this)["report.list.labels"] = "ID,Project,Pri,Due,Active,Age,Description";
(*this)["report.list.sort"] = "due+,priority-,project+";
(*this)["report.ls.description"] = "Minimal listing of all tasks matching the specified criteria";
(*this)["report.ls.columns"] = "id,project,priority,description";
(*this)["report.ls.labels"] = "ID,Project,Pri,Description";
(*this)["report.ls.sort"] = "priority-,project+";
(*this)["report.newest.description"] = "Shows the newest tasks";
(*this)["report.newest.columns"] = "id,project,priority,due,active,age,description";
(*this)["report.newest.labels"] = "ID,Project,Pri,Due,Active,Age,Description";
(*this)["report.newest.sort"] = "id-";
(*this)["report.newest.limit"] = "10";
(*this)["report.oldest.description"] = "Shows the oldest tasks";
(*this)["report.oldest.columns"] = "id,project,priority,due,active,age,description";
(*this)["report.oldest.labels"] = "ID,Project,Pri,Due,Active,Age,Description";
(*this)["report.oldest.sort"] = "id+";
(*this)["report.oldest.limit"] = "10";
}
@@ -140,6 +145,7 @@ void Config::createDefault (const std::string& home)
{
fprintf (out, "data.location=%s\n", dataDir.c_str ());
fprintf (out, "confirmation=yes\n");
fprintf (out, "echo.command=yes\n");
fprintf (out, "next=2\n");
fprintf (out, "dateformat=m/d/Y\n");
fprintf (out, "#monthsperline=2\n");
@@ -170,29 +176,35 @@ void Config::createDefault (const std::string& home)
// Custom reports.
fprintf (out, "# Fields: id,uuid,project,priority,entry,start,due,recur,age,active,tags,description\n");
fprintf (out, "# description_only\n");
fprintf (out, "# Description: This report is ...\n");
fprintf (out, "# Sort: due+,priority-,project+\n");
fprintf (out, "# Filter: pro:x pri:H +bug\n");
fprintf (out, "# Limit: 10\n");
fprintf (out, "report.long.description=Lists all task, all data, matching the specified criteria\n");
fprintf (out, "report.long.labels=ID,Project,Pri,Added,Started,Due,Recur,Age,Tags,Description\n");
fprintf (out, "report.long.columns=id,project,priority,entry,start,due,recur,age,tags,description\n");
fprintf (out, "report.long.sort=due+,priority-,project+\n");
fprintf (out, "report.list.description=Lists all tasks matching the specified criteria\n");
fprintf (out, "report.list.labels=ID,Project,Pri,Due,Active,Age,Description\n");
fprintf (out, "report.list.columns=id,project,priority,due,active,age,description\n");
fprintf (out, "report.list.sort=due+,priority-,project+\n");
fprintf (out, "report.ls.description=Minimal listing of all tasks matching the specified criteria\n");
fprintf (out, "report.ls.labels=ID,Project,Pri,Description\n");
fprintf (out, "report.ls.columns=id,project,priority,description\n");
fprintf (out, "report.ls.sort=priority-,project+\n");
fprintf (out, "report.newest.description=Shows the newest tasks\n");
fprintf (out, "report.newest.labels=ID,Project,Pri,Due,Active,Age,Description\n");
fprintf (out, "report.newest.columns=id,project,priority,due,active,age,description\n");
fprintf (out, "report.newest.sort=id-\n");
fprintf (out, "report.newest.limit=10\n");
fprintf (out, "report.oldest.description=Shows the oldest tasks\n");
fprintf (out, "report.oldest.labels=ID,Project,Pri,Due,Active,Age,Description\n");
fprintf (out, "report.oldest.columns=id,project,priority,due,active,age,description\n");
fprintf (out, "report.oldest.sort=id+\n");
fprintf (out, "report.oldest.limit=10\n");

View File

@@ -1,2 +1,2 @@
bin_PROGRAMS = task
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp Timer.cpp color.cpp parse.cpp task.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp Config.h Date.h T.h TDB.h Table.h Grid.h Timer.h color.h task.h
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp Timer.cpp color.cpp parse.cpp task.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp import.cpp Config.h Date.h T.h TDB.h Table.h Grid.h Timer.h color.h task.h

View File

@@ -47,7 +47,7 @@ am_task_OBJECTS = Config.$(OBJEXT) Date.$(OBJEXT) T.$(OBJEXT) \
TDB.$(OBJEXT) Table.$(OBJEXT) Grid.$(OBJEXT) Timer.$(OBJEXT) \
color.$(OBJEXT) parse.$(OBJEXT) task.$(OBJEXT) \
command.$(OBJEXT) report.$(OBJEXT) util.$(OBJEXT) \
text.$(OBJEXT) rules.$(OBJEXT)
text.$(OBJEXT) rules.$(OBJEXT) import.$(OBJEXT)
task_OBJECTS = $(am_task_OBJECTS)
task_LDADD = $(LDADD)
DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@
@@ -155,7 +155,7 @@ sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp Timer.cpp color.cpp parse.cpp task.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp Config.h Date.h T.h TDB.h Table.h Grid.h Timer.h color.h task.h
task_SOURCES = Config.cpp Date.cpp T.cpp TDB.cpp Table.cpp Grid.cpp Timer.cpp color.cpp parse.cpp task.cpp command.cpp report.cpp util.cpp text.cpp rules.cpp import.cpp Config.h Date.h T.h TDB.h Table.h Grid.h Timer.h color.h task.h
all: all-am
.SUFFIXES:
@@ -231,6 +231,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Timer.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/command.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/report.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rules.Po@am__quote@

239
src/T.cpp
View File

@@ -25,6 +25,7 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <sstream>
#include <algorithm>
#include "task.h"
#include "T.h"
@@ -39,6 +40,10 @@ T::T ()
mTags.clear ();
mAttributes.clear ();
mDescription = "";
mFrom = "";
mTo = "";
mGlobal = false;
mAnnotations.clear ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -58,6 +63,7 @@ T::T (const T& other)
mTags = other.mTags;
mRemoveTags = other.mRemoveTags;
mAttributes = other.mAttributes;
mAnnotations = other.mAnnotations;
}
////////////////////////////////////////////////////////////////////////////////
@@ -72,6 +78,7 @@ T& T::operator= (const T& other)
mTags = other.mTags;
mRemoveTags = other.mRemoveTags;
mAttributes = other.mAttributes;
mAnnotations = other.mAnnotations;
}
return *this;
@@ -109,6 +116,12 @@ void T::addRemoveTag (const std::string& tag)
mRemoveTags.push_back (tag);
}
////////////////////////////////////////////////////////////////////////////////
int T::getTagCount () const
{
return mTags.size ();
}
////////////////////////////////////////////////////////////////////////////////
void T::getTags (std::vector<std::string>& all) const
{
@@ -230,21 +243,51 @@ void T::removeAttribute (const std::string& name)
}
////////////////////////////////////////////////////////////////////////////////
void T::getSubstitution (std::string& from, std::string& to) const
void T::getSubstitution (
std::string& from,
std::string& to,
bool& global) const
{
from = mFrom;
to = mTo;
from = mFrom;
to = mTo;
global = mGlobal;
}
////////////////////////////////////////////////////////////////////////////////
void T::setSubstitution (const std::string& from, const std::string& to)
void T::setSubstitution (
const std::string& from,
const std::string& to,
bool global)
{
mFrom = from;
mTo = to;
mFrom = from;
mTo = to;
mGlobal = global;
}
////////////////////////////////////////////////////////////////////////////////
// uuid status [tags] [attributes] description
void T::getAnnotations (std::map <time_t, std::string>& all) const
{
all = mAnnotations;
}
////////////////////////////////////////////////////////////////////////////////
void T::setAnnotations (const std::map <time_t, std::string>& all)
{
mAnnotations = all;
}
////////////////////////////////////////////////////////////////////////////////
void T::addAnnotation (const std::string& description)
{
std::string sanitized = description;
std::replace (sanitized.begin (), sanitized.end (), '"', '\'');
std::replace (sanitized.begin (), sanitized.end (), '[', '(');
std::replace (sanitized.begin (), sanitized.end (), ']', ')');
mAnnotations[time (NULL)] = sanitized;
}
////////////////////////////////////////////////////////////////////////////////
// uuid status [tags] [attributes] [annotations] description
//
// uuid \x{8}-\x{4}-\x{4}-\x{4}-\x{12}
// status - + X r
@@ -282,10 +325,26 @@ const std::string T::compose () const
++count;
}
line += "] ";
line += "] [";
// Annotations
std::stringstream annotation;
bool first = true;
foreach (note, mAnnotations)
{
if (first)
first = false;
else
annotation << " ";
annotation << note->first << ":\"" << note->second << "\"";
}
line += annotation.str () + "] ";
// Description
line += mDescription;
// EOL
line += "\n";
if (line.length () > T_LINE_MAX)
@@ -421,10 +480,12 @@ void T::parse (const std::string& line)
}
else
throw std::string ("Line too short");
mAnnotations.clear ();
}
break;
// File format version 2, from 2008.1.1
// File format version 2, from 2008.1.1 - 2009.3.23
case 2:
{
if (line.length () > 46) // ^.{36} . \[\] \[\] \n
@@ -446,24 +507,122 @@ void T::parse (const std::string& line)
if (openAttrBracket != std::string::npos &&
closeAttrBracket != std::string::npos)
{
std::string tags = line.substr (
openTagBracket + 1, closeTagBracket - openTagBracket - 1);
std::vector <std::string> rawTags;
split (mTags, tags, ' ');
std::string tags = line.substr (
openTagBracket + 1, closeTagBracket - openTagBracket - 1);
std::vector <std::string> rawTags;
split (mTags, tags, ' ');
std::string attributes = line.substr (
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
std::vector <std::string> pairs;
split (pairs, attributes, ' ');
for (size_t i = 0; i < pairs.size (); ++i)
{
std::vector <std::string> pair;
split (pair, pairs[i], ':');
if (pair.size () == 2)
mAttributes[pair[0]] = pair[1];
}
std::string attributes = line.substr (
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
std::vector <std::string> pairs;
split (pairs, attributes, ' ');
for (size_t i = 0; i < pairs.size (); ++i)
{
std::vector <std::string> pair;
split (pair, pairs[i], ':');
if (pair.size () == 2)
mAttributes[pair[0]] = pair[1];
}
mDescription = line.substr (closeAttrBracket + 2, std::string::npos);
mDescription = line.substr (closeAttrBracket + 2, std::string::npos);
}
else
throw std::string ("Missing attribute brackets");
}
else
throw std::string ("Missing tag brackets");
}
else
throw std::string ("Line too short");
mAnnotations.clear ();
}
break;
// File format version 3, from 2009.3.23
case 3:
{
if (line.length () > 49) // ^.{36} . \[\] \[\] \[\] \n
{
mUUID = line.substr (0, 36);
mStatus = line[37] == '+' ? completed
: line[37] == 'X' ? deleted
: line[37] == 'r' ? recurring
: pending;
size_t openTagBracket = line.find ("[");
size_t closeTagBracket = line.find ("]", openTagBracket);
if (openTagBracket != std::string::npos &&
closeTagBracket != std::string::npos)
{
size_t openAttrBracket = line.find ("[", closeTagBracket);
size_t closeAttrBracket = line.find ("]", openAttrBracket);
if (openAttrBracket != std::string::npos &&
closeAttrBracket != std::string::npos)
{
size_t openAnnoBracket = line.find ("[", closeAttrBracket);
size_t closeAnnoBracket = line.find ("]", openAnnoBracket);
if (openAnnoBracket != std::string::npos &&
closeAnnoBracket != std::string::npos)
{
std::string tags = line.substr (
openTagBracket + 1, closeTagBracket - openTagBracket - 1);
std::vector <std::string> rawTags;
split (mTags, tags, ' ');
std::string attributes = line.substr (
openAttrBracket + 1, closeAttrBracket - openAttrBracket - 1);
std::vector <std::string> pairs;
split (pairs, attributes, ' ');
for (size_t i = 0; i < pairs.size (); ++i)
{
std::vector <std::string> pair;
split (pair, pairs[i], ':');
if (pair.size () == 2)
mAttributes[pair[0]] = pair[1];
}
// Extract and split the annotations, which are of the form:
// 1234:"..." 5678:"..."
std::string annotations = line.substr (
openAnnoBracket + 1, closeAnnoBracket - openAnnoBracket - 1);
pairs.clear ();
std::string::size_type start = 0;
std::string::size_type end = 0;
do
{
end = annotations.find ('"', start);
if (end != std::string::npos)
{
end = annotations.find ('"', end + 1);
if (start != std::string::npos &&
end != std::string::npos)
{
pairs.push_back (annotations.substr (start, end - start + 1));
start = end + 2;
}
}
}
while (start != std::string::npos &&
end != std::string::npos);
for (size_t i = 0; i < pairs.size (); ++i)
{
std::string pair = pairs[i];
std::string::size_type colon = pair.find (":");
if (colon != std::string::npos)
{
std::string name = pair.substr (0, colon);
std::string value = pair.substr (colon + 2, pair.length () - colon - 3);
mAnnotations[::atoi (name.c_str ())] = value;
}
}
mDescription = line.substr (closeAnnoBracket + 2, std::string::npos);
}
}
else
throw std::string ("Missing attribute brackets");
@@ -512,16 +671,31 @@ int T::determineVersion (const std::string& line)
line[23] == '-' &&
line[36] == ' ' &&
(line[37] == '-' || line[37] == '+' || line[37] == 'X' || line[37] == 'r'))
return 2;
{
// Version 3 looks like:
//
// uuid status [tags] [attributes] [annotations] description\n
//
// Scan for the number of [] pairs.
std::string::size_type tagAtts = line.find ("] [", 0);
std::string::size_type attsAnno = line.find ("] [", tagAtts + 1);
std::string::size_type annoDesc = line.find ("] ", attsAnno + 1);
if (tagAtts != std::string::npos &&
attsAnno != std::string::npos &&
annoDesc != std::string::npos)
return 3;
else
return 2;
}
// Version 3?
// Version 4?
//
// Fortunately, with the hindsight that will come with version 3, the
// identifying characteristics of 1 and 2 may be modified such that if 3 has
// a UUID followed by a status, then there is still a way to differentiate
// between 2 and 3.
// Fortunately, with the hindsight that will come with version 4, the
// identifying characteristics of 1, 2 and 3 may be modified such that if 4
// has a UUID followed by a status, then there is still a way to differentiate
// between 2, 3 and 4.
//
// The danger is that a version 2 binary reads and misinterprets a version 3
// The danger is that a version 3 binary reads and misinterprets a version 4
// file. This is why it is a good idea to rely on an explicit version
// declaration rather than chance positioning.
@@ -534,6 +708,7 @@ int T::determineVersion (const std::string& line)
bool T::validate () const
{
// TODO Verify until > due
// TODO Verify entry < until, due, start, end
return true;
}

13
src/T.h
View File

@@ -56,15 +56,17 @@ public:
const std::string getDescription () const { return mDescription; }
void setDescription (const std::string& description) { mDescription = description; }
int getAnnotationCount () const { return mAnnotations.size (); }
void getSubstitution (std::string&, std::string&) const;
void setSubstitution (const std::string&, const std::string&);
void getSubstitution (std::string&, std::string&, bool&) const;
void setSubstitution (const std::string&, const std::string&, bool);
bool hasTag (const std::string&) const;
void getRemoveTags (std::vector<std::string>&); // SPECIAL
void addRemoveTag (const std::string&); // SPECIAL
int getTagCount () const;
void getTags (std::vector<std::string>&) const;
void addTag (const std::string&);
void addTags (const std::vector <std::string>&);
@@ -77,6 +79,10 @@ public:
void removeAttribute (const std::string&);
void removeAttributes ();
void getAnnotations (std::map <time_t, std::string>&) const;
void setAnnotations (const std::map <time_t, std::string>&);
void addAnnotation (const std::string&);
const std::string compose () const;
const std::string composeCSV ();
void parse (const std::string&);
@@ -93,9 +99,10 @@ private:
std::vector<std::string> mTags;
std::vector<std::string> mRemoveTags;
std::map<std::string, std::string> mAttributes;
std::string mFrom;
std::string mTo;
bool mGlobal;
std::map <time_t, std::string> mAnnotations;
};
#endif

View File

@@ -227,7 +227,7 @@ void Table::setRowBg (const int row, const Text::color c)
////////////////////////////////////////////////////////////////////////////////
void Table::addCell (const int row, const int col, const std::string& data)
{
int length = 0;
unsigned int length = 0;
if (mSuppressWS)
{
@@ -238,7 +238,19 @@ void Table::addCell (const int row, const int col, const std::string& data)
data2 = data;
clean (data2);
length = data2.length ();
// For multi-line cells, find the longest line.
if (data2.find ("\n") != std::string::npos)
{
length = 0;
std::vector <std::string> lines;
split (lines, data2, "\n");
for (unsigned int i = 0; i < lines.size (); ++i)
if (lines[i].length () > length)
length = lines[i].length ();
}
else
length = data2.length ();
mData.add (row, col, data2);
}
else
@@ -248,11 +260,22 @@ void Table::addCell (const int row, const int col, const std::string& data)
else
mData.add (row, col, data);
length = data.length ();
// For multi-line cells, find the longest line.
if (data.find ("\n") != std::string::npos)
{
length = 0;
std::vector <std::string> lines;
split (lines, data, "\n");
for (unsigned int i = 0; i < lines.size (); ++i)
if (lines[i].length () > length)
length = lines[i].length ();
}
else
length = data.length ();
}
// Automatically maintain max width.
mMaxDataWidth[col] = max (mMaxDataWidth[col], length);
mMaxDataWidth[col] = max (mMaxDataWidth[col], (int)length);
}
////////////////////////////////////////////////////////////////////////////////
@@ -508,9 +531,7 @@ void Table::calculateColumnWidths ()
}
else
{
// std::cout << "# insufficient room, considering only flexible columns." << std::endl;
// The fallback position is to assume no width was specificed, and just
// The fallback position is to assume no width was specified, and just
// calculate widths accordingly.
mTableWidth = 0;
calculateColumnWidths ();

View File

@@ -283,7 +283,7 @@ std::string handleVersion (Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -371,8 +371,13 @@ std::string handleVersion (Config& conf)
"blanklines color color.active color.due color.overdue color.pri.H "
"color.pri.L color.pri.M color.pri.none color.recurring color.tagged "
"confirmation curses data.location dateformat default.command "
"default.priority defaultwidth due locking monthsperline nag next project "
"shadow.command shadow.file shadow.notify";
"default.priority defaultwidth due echo.command locking monthsperline nag "
"next project shadow.command shadow.file shadow.notify "
"import.synonym.id import.synonym.uuid import.synonym.status "
"import.synonym.tags import.synonym.entry import.synonym.start "
"import.synonym.due import.synonym.recur import.synonym.end "
"import.synonym.project import.synonym.priority import.synonym.fg "
"import.synonym.bg import.synonym.description";
// This configuration variable is supported, but not documented. It exists
// so that unit tests can force color to be on even when the output from task
@@ -432,7 +437,9 @@ std::string handleVersion (Config& conf)
////////////////////////////////////////////////////////////////////////////////
std::string handleDelete (TDB& tdb, T& task, Config& conf)
{
if (conf.get ("confirmation") != "yes" || confirm ("Permanently delete task?"))
std::stringstream out;
if (!conf.get (std::string ("confirmation"), false) || confirm ("Permanently delete task?"))
{
std::vector <T> all;
tdb.allPendingT (all);
@@ -450,11 +457,19 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
// Scan all pending tasks for siblings of this task, and the parent
// itself, and delete them.
foreach (sibling, all)
{
if (sibling->getAttribute ("parent") == parent ||
sibling->getUUID () == parent)
{
tdb.deleteT (*sibling);
return std::string ("");
if (conf.get ("echo.command", true))
out << "Deleting recurring task "
<< sibling->getId ()
<< " "
<< sibling->getDescription ()
<< std::endl;
}
}
}
else
{
@@ -462,20 +477,32 @@ std::string handleDelete (TDB& tdb, T& task, Config& conf)
t->setStatus (T::deleted);
updateRecurrenceMask (tdb, all, *t);
tdb.deleteT (*t);
return std::string ("");
out << "Deleting recurring task "
<< t->getId ()
<< " "
<< t->getDescription ()
<< std::endl;
}
}
else
{
tdb.deleteT (*t);
if (conf.get ("echo.command", true))
out << "Deleting task "
<< t->getId ()
<< " "
<< t->getDescription ()
<< std::endl;
}
break; // No point continuing the loop.
}
}
}
else
return std::string ("Task not deleted.\n");
out << "Task not deleted." << std::endl;
return std::string ("");
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -490,6 +517,7 @@ std::string handleStart (TDB& tdb, T& task, Config& conf)
if (it->getId () == task.getId ())
{
T original (*it);
std::stringstream out;
if (original.getAttribute ("start") == "")
{
@@ -500,15 +528,20 @@ std::string handleStart (TDB& tdb, T& task, Config& conf)
original.setId (task.getId ());
tdb.modifyT (original);
if (conf.get ("echo.command", true))
out << "Started "
<< original.getId ()
<< " "
<< original.getDescription ()
<< std::endl;
nag (tdb, task, conf);
return std::string ("");
}
else
{
std::stringstream out;
out << "Task " << task.getId () << " already started." << std::endl;
return out.str ();
}
return out.str ();
}
}
@@ -528,6 +561,7 @@ std::string handleStop (TDB& tdb, T& task, Config& conf)
if (it->getId () == task.getId ())
{
T original (*it);
std::stringstream out;
if (original.getAttribute ("start") != "")
{
@@ -535,14 +569,15 @@ std::string handleStop (TDB& tdb, T& task, Config& conf)
original.setId (task.getId ());
tdb.modifyT (original);
return std::string ("");
if (conf.get ("echo.command", true))
out << "Stopped " << original.getId () << " " << original.getDescription () << std::endl;
}
else
{
std::stringstream out;
out << "Task " << task.getId () << " not started." << std::endl;
return out.str ();
}
return out.str ();
}
}
@@ -565,6 +600,13 @@ std::string handleDone (TDB& tdb, T& task, Config& conf)
{
if (t->getId () == task.getId ())
{
if (conf.get ("echo.command", true))
out << "Completed "
<< t->getId ()
<< " "
<< t->getDescription ()
<< std::endl;
t->setStatus (T::completed);
updateRecurrenceMask (tdb, all, *t);
break;
@@ -637,12 +679,30 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
{
std::stringstream out;
std::vector <T> all;
tdb.pendingT (all);
tdb.allPendingT (all);
// Lookup the complete task.
T complete = findT (task.getId (), all);
// Perform some logical consistency checks.
if (task.getAttribute ("recur") != "" &&
task.getAttribute ("due") == "" &&
complete.getAttribute ("due") == "")
throw std::string ("You cannot specify a recurring task without a due date.");
if (task.getAttribute ("until") != "" &&
task.getAttribute ("recur") == "" &&
complete.getAttribute ("recur") == "")
throw std::string ("You cannot specify an until date for a non-recurring task.");
int count = 0;
std::vector <T>::iterator it;
for (it = all.begin (); it != all.end (); ++it)
{
if (it->getId () == task.getId ())
if (it->getId () == complete.getId () || // Self
(complete.getAttribute ("parent") != "" &&
it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling
it->getUUID () == complete.getAttribute ("parent")) // Parent
{
T original (*it);
@@ -688,39 +748,191 @@ std::string handleModify (TDB& tdb, T& task, Config& conf)
if (i->second == "")
original.removeAttribute (i->first);
else
{
original.setAttribute (i->first, i->second);
// If a "recur" attribute is added, upgrade to a recurring task.
if (i->first == "recur")
original.setStatus (T::recurring);
}
++changes;
}
std::string from;
std::string to;
task.getSubstitution (from, to);
bool global;
task.getSubstitution (from, to, global);
if (from != "")
{
std::string description = original.getDescription ();
size_t pattern = description.find (from);
if (pattern != std::string::npos)
size_t pattern;
if (global)
{
description = description.substr (0, pattern) +
to +
description.substr (pattern + from.length (), std::string::npos);
// Perform all subs on description.
while ((pattern = description.find (from)) != std::string::npos)
{
description.replace (pattern, from.length (), to);
++changes;
}
original.setDescription (description);
++changes;
// Perform all subs on annotations.
std::map <time_t, std::string> annotations;
original.getAnnotations (annotations);
std::map <time_t, std::string>::iterator it;
for (it = annotations.begin (); it != annotations.end (); ++it)
{
while ((pattern = it->second.find (from)) != std::string::npos)
{
it->second.replace (pattern, from.length (), to);
++changes;
}
}
original.setAnnotations (annotations);
}
else
{
// Perform first description substitution.
if ((pattern = description.find (from)) != std::string::npos)
{
description.replace (pattern, from.length (), to);
original.setDescription (description);
++changes;
}
// Failing that, perform the first annotation substitution.
else
{
std::map <time_t, std::string> annotations;
original.getAnnotations (annotations);
std::map <time_t, std::string>::iterator it;
for (it = annotations.begin (); it != annotations.end (); ++it)
{
if ((pattern = it->second.find (from)) != std::string::npos)
{
it->second.replace (pattern, from.length (), to);
++changes;
break;
}
}
original.setAnnotations (annotations);
}
}
}
if (changes)
{
original.setId (task.getId ());
tdb.modifyT (original);
}
return out.str ();
++count;
}
}
throw std::string ("Task not found.");
if (count == 0)
throw std::string ("Task not found.");
if (conf.get ("echo.command", true))
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
std::string handleAppend (TDB& tdb, T& task, Config& conf)
{
std::stringstream out;
std::vector <T> all;
tdb.allPendingT (all);
// Lookup the complete task.
T complete = findT (task.getId (), all);
int count = 0;
std::vector <T>::iterator it;
for (it = all.begin (); it != all.end (); ++it)
{
if (it->getId () == complete.getId () || // Self
(complete.getAttribute ("parent") != "" &&
it->getAttribute ("parent") == complete.getAttribute ("parent")) || // Sibling
it->getUUID () == complete.getAttribute ("parent")) // Parent
{
T original (*it);
// A non-zero value forces a file write.
int changes = 0;
// Apply a new description, if any.
if (task.getDescription () != "")
{
original.setDescription (original.getDescription () +
" " +
task.getDescription ());
++changes;
}
// Apply or remove tags, if any.
std::vector <std::string> tags;
task.getTags (tags);
for (unsigned int i = 0; i < tags.size (); ++i)
{
if (tags[i][0] == '+')
original.addTag (tags[i].substr (1, std::string::npos));
else
original.addTag (tags[i]);
++changes;
}
task.getRemoveTags (tags);
for (unsigned int i = 0; i < tags.size (); ++i)
{
if (tags[i][0] == '-')
original.removeTag (tags[i].substr (1, std::string::npos));
else
original.removeTag (tags[i]);
++changes;
}
// Apply or remove attributes, if any.
std::map <std::string, std::string> attributes;
task.getAttributes (attributes);
foreach (i, attributes)
{
if (i->second == "")
original.removeAttribute (i->first);
else
original.setAttribute (i->first, i->second);
++changes;
}
if (changes)
{
tdb.modifyT (original);
if (conf.get ("echo.command", true))
out << "Appended '"
<< task.getDescription ()
<< "' to task "
<< original.getId ()
<< std::endl;
}
++count;
}
}
if (count == 0)
throw std::string ("Task not found.");
if (conf.get ("echo.command", true))
out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl;
return out.str ();
}
@@ -813,3 +1025,45 @@ std::string handleColor (Config& conf)
}
////////////////////////////////////////////////////////////////////////////////
std::string handleAnnotate (TDB& tdb, T& task, Config& conf)
{
std::stringstream out;
std::vector <T> all;
tdb.pendingT (all);
std::vector <T>::iterator it;
for (it = all.begin (); it != all.end (); ++it)
{
if (it->getId () == task.getId ())
{
it->addAnnotation (task.getDescription ());
tdb.modifyT (*it);
if (conf.get ("echo.command", true))
out << "Annotated "
<< task.getId ()
<< " with '"
<< task.getDescription ()
<< "'"
<< std::endl;
return out.str ();
}
}
throw std::string ("Task not found.");
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
T findT (int id, const std::vector <T>& all)
{
std::vector <T>::const_iterator it;
for (it = all.begin (); it != all.end (); ++it)
if (id == it->getId ())
return *it;
return T ();
}
////////////////////////////////////////////////////////////////////////////////

1203
src/import.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -120,6 +120,8 @@ static const char* commands[] =
{
"active",
"add",
"append",
"annotate",
"calendar",
"colors",
"completed",
@@ -129,6 +131,7 @@ static const char* commands[] =
"help",
"history",
"ghistory",
"import",
"info",
"next",
"overdue",
@@ -188,7 +191,6 @@ void guess (const std::string& type, std::vector<std::string>& options, std::str
candidate = matches[0];
else if (0 == matches.size ())
// throw std::string ("Unrecognized ") + type + " '" + candidate + "'";
candidate = "";
else
@@ -233,7 +235,7 @@ bool validDate (std::string& date, Config& conf)
{
Date test (date, conf.get ("dateformat", "m/d/Y"));
char epoch[12];
char epoch[16];
sprintf (epoch, "%d", (int) test.toEpoch ());
date = epoch;
@@ -346,7 +348,8 @@ static bool validCommand (std::string& input)
static bool validSubstitution (
std::string& input,
std::string& from,
std::string& to)
std::string& to,
bool& global)
{
size_t first = input.find ('/');
if (first != std::string::npos)
@@ -360,10 +363,17 @@ static bool validSubstitution (
if (first == 0 &&
first < second &&
second < third &&
third == input.length () - 1)
(third == input.length () - 1 ||
third == input.length () - 2))
{
from = input.substr (first + 1, second - first - 1);
to = input.substr (second + 1, third - second - 1);
global = false;
if (third == input.length () - 2 &&
input.find ('g', third + 1) != std::string::npos)
global = true;
return true;
}
}
@@ -409,6 +419,7 @@ void parse (
size_t colon; // Pointer to colon in argument.
std::string from;
std::string to;
bool global;
// An id is the first argument found that contains all digits.
if (lowerCase (command) != "add" && // "add" doesn't require an ID
@@ -441,13 +452,17 @@ void parse (
// If it is not a valid attribute, then allow the argument as part of
// the description.
else
{
if (descCandidate.length ())
descCandidate += " ";
descCandidate += arg;
}
}
// Substitution of description text.
else if (validSubstitution (arg, from, to))
else if (validSubstitution (arg, from, to, global))
{
task.setSubstitution (from, to);
task.setSubstitution (from, to, global);
}
// Command.
@@ -460,7 +475,7 @@ void parse (
{
if (descCandidate.length ())
descCandidate += " ";
descCandidate += std::string (arg);
descCandidate += arg;
}
}
@@ -469,19 +484,11 @@ void parse (
{
if (descCandidate.length ())
descCandidate += " ";
descCandidate += std::string (arg);
descCandidate += arg;
}
}
}
if (task.getAttribute ("recur") != "" &&
task.getAttribute ("due") == "")
throw std::string ("You cannot specify a recurring task without a due date.");
if (task.getAttribute ("until") != "" &&
task.getAttribute ("recur") == "")
throw std::string ("You cannot specify an until date for a non-recurring task.");
if (validDescription (descCandidate))
task.setDescription (descCandidate);
}

View File

@@ -147,7 +147,7 @@ std::string handleCompleted (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -207,7 +207,18 @@ std::string handleCompleted (TDB& tdb, T& task, Config& conf)
table.addCell (row, 0, end.toString (conf.get ("dateformat", "m/d/Y")));
table.addCell (row, 1, refTask.getAttribute ("project"));
table.addCell (row, 2, refTask.getDescription ());
std::string description = refTask.getDescription ();
std::string when;
std::map <time_t, std::string> annotations;
refTask.getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
table.addCell (row, 2, description);
if (conf.get ("color", true) || conf.get (std::string ("_forcecolor"), false))
{
@@ -240,7 +251,7 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -270,7 +281,7 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
table.setTableDashedUnderline ();
table.setColumnWidth (0, Table::minimum);
table.setColumnWidth (1, Table::minimum);
table.setColumnWidth (1, Table::flexible);
table.setColumnJustification (0, Table::left);
table.setColumnJustification (1, Table::left);
@@ -296,9 +307,20 @@ std::string handleInfo (TDB& tdb, T& task, Config& conf)
: refTask.getStatus () == T::recurring ? "Recurring"
: ""));
std::string description = refTask.getDescription ();
std::string when;
std::map <time_t, std::string> annotations;
refTask.getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
row = table.addRow ();
table.addCell (row, 0, "Description");
table.addCell (row, 1, refTask.getDescription ());
table.addCell (row, 1, description);
if (refTask.getAttribute ("project") != "")
{
@@ -612,7 +634,7 @@ std::string handleReportNext (TDB& tdb, T& task, Config& conf)
gatherNextTasks (tdb, task, conf, pending, matching);
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -714,7 +736,19 @@ std::string handleReportNext (TDB& tdb, T& task, Config& conf)
table.addCell (row, 3, due);
table.addCell (row, 4, active);
table.addCell (row, 5, age);
table.addCell (row, 6, refTask.getDescription ());
std::string description = refTask.getDescription ();
std::string when;
std::map <time_t, std::string> annotations;
refTask.getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
table.addCell (row, 6, description);
if (conf.get ("color", true) || conf.get (std::string ("_forcecolor"), false))
{
@@ -967,7 +1001,7 @@ std::string handleReportGHistory (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -1330,7 +1364,7 @@ std::string handleReportCalendar (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -1439,7 +1473,7 @@ std::string handleReportActive (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -1520,7 +1554,19 @@ std::string handleReportActive (TDB& tdb, T& task, Config& conf)
table.addCell (row, 1, refTask.getAttribute ("project"));
table.addCell (row, 2, refTask.getAttribute ("priority"));
table.addCell (row, 3, due);
table.addCell (row, 4, refTask.getDescription ());
std::string description = refTask.getDescription ();
std::string when;
std::map <time_t, std::string> annotations;
refTask.getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
table.addCell (row, 4, description);
if (conf.get ("color", true) || conf.get (std::string ("_forcecolor"), false))
{
@@ -1560,7 +1606,7 @@ std::string handleReportOverdue (TDB& tdb, T& task, Config& conf)
std::stringstream out;
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -1634,7 +1680,19 @@ std::string handleReportOverdue (TDB& tdb, T& task, Config& conf)
table.addCell (row, 1, refTask.getAttribute ("project"));
table.addCell (row, 2, refTask.getAttribute ("priority"));
table.addCell (row, 3, due);
table.addCell (row, 4, refTask.getDescription ());
std::string description = refTask.getDescription ();
std::string when;
std::map <time_t, std::string> annotations;
refTask.getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
table.addCell (row, 4, description);
if (conf.get ("color", true) || conf.get (std::string ("_forcecolor"), false))
{
@@ -1683,6 +1741,7 @@ std::string handleReportStats (TDB& tdb, T& task, Config& conf)
int pendingT = 0;
int completedT = 0;
int taggedT = 0;
int annotationsT = 0;
int recurringT = 0;
float daysPending = 0.0;
int descLength = 0;
@@ -1713,6 +1772,8 @@ std::string handleReportStats (TDB& tdb, T& task, Config& conf)
descLength += it->getDescription ().length ();
annotationsT += it->getAnnotationCount ();
std::vector <std::string> tags;
it->getTags (tags);
if (tags.size ()) ++taggedT;
@@ -1731,6 +1792,13 @@ std::string handleReportStats (TDB& tdb, T& task, Config& conf)
<< "Deleted " << deletedT << std::endl
<< "Total " << totalT << std::endl;
out << "Annotations " << annotationsT << std::endl;
out << "Unique tags " << allTags.size () << std::endl;
out << "Projects " << allProjects.size () << std::endl;
if (totalT)
out << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl;
if (tasks.size ())
{
Date e (earliest);
@@ -1755,13 +1823,7 @@ std::string handleReportStats (TDB& tdb, T& task, Config& conf)
<< std::endl;
if (totalT)
{
out << "Average desc length " << (int) (descLength / totalT) << " characters" << std::endl;
out << "Tasks tagged " << std::setprecision (3) << (100.0 * taggedT / totalT) << "%" << std::endl;
}
out << "Unique tags " << allTags.size () << std::endl;
out << "Projects " << allProjects.size () << std::endl;
return out.str ();
}
@@ -1962,7 +2024,7 @@ std::string handleCustomReport (
const std::string& report)
{
// Determine window size, and set table accordingly.
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -1978,6 +2040,19 @@ std::string handleCustomReport (
split (columns, columnList, ',');
validReportColumns (columns);
std::string labelList = conf.get ("report." + report + ".labels");
std::vector <std::string> labels;
split (labels, labelList, ',');
if (columns.size () != labels.size () && labels.size () != 0)
throw std::string ("There are a different number of columns than labels ") +
"for report '" + report + "'. Please correct this.";
std::map <std::string, std::string> columnLabels;
if (labels.size ())
for (unsigned int i = 0; i < columns.size (); ++i)
columnLabels[columns[i]] = labels[i];
std::string sortList = conf.get ("report." + report + ".sort");
std::vector <std::string> sortOrder;
split (sortOrder, sortList, ',');
@@ -2019,7 +2094,7 @@ std::string handleCustomReport (
// Add each column individually.
if (*col == "id")
{
table.addColumn ("ID");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "ID");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2029,7 +2104,7 @@ std::string handleCustomReport (
else if (*col == "uuid")
{
table.addColumn ("UUID");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "UUID");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
@@ -2039,7 +2114,7 @@ std::string handleCustomReport (
else if (*col == "project")
{
table.addColumn ("Project");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Project");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
@@ -2049,7 +2124,7 @@ std::string handleCustomReport (
else if (*col == "priority")
{
table.addColumn ("Pri");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Pri");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
@@ -2059,7 +2134,7 @@ std::string handleCustomReport (
else if (*col == "entry")
{
table.addColumn ("Added");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Added");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2078,7 +2153,7 @@ std::string handleCustomReport (
else if (*col == "start")
{
table.addColumn ("Started");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Started");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2097,7 +2172,7 @@ std::string handleCustomReport (
else if (*col == "due")
{
table.addColumn ("Due");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2118,7 +2193,7 @@ std::string handleCustomReport (
else if (*col == "age")
{
table.addColumn ("Age");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Age");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2139,7 +2214,7 @@ std::string handleCustomReport (
else if (*col == "active")
{
table.addColumn ("Active");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Active");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
@@ -2150,7 +2225,7 @@ std::string handleCustomReport (
else if (*col == "tags")
{
table.addColumn ("Tags");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Tags");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::left);
@@ -2164,9 +2239,9 @@ std::string handleCustomReport (
}
}
else if (*col == "description")
else if (*col == "description_only")
{
table.addColumn ("Description");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description");
table.setColumnWidth (columnCount, Table::flexible);
table.setColumnJustification (columnCount, Table::left);
@@ -2174,9 +2249,33 @@ std::string handleCustomReport (
table.addCell (row, columnCount, tasks[row].getDescription ());
}
else if (*col == "description")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Description");
table.setColumnWidth (columnCount, Table::flexible);
table.setColumnJustification (columnCount, Table::left);
std::string description;
std::string when;
for (unsigned int row = 0; row < tasks.size(); ++row)
{
description = tasks[row].getDescription ();
std::map <time_t, std::string> annotations;
tasks[row].getAnnotations (annotations);
foreach (anno, annotations)
{
Date dt (anno->first);
when = dt.toString (conf.get ("dateformat", "m/d/Y"));
description += "\n" + when + " " + anno->second;
}
table.addCell (row, columnCount, description);
}
}
else if (*col == "recur")
{
table.addColumn ("Recur");
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Recur");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
@@ -2184,6 +2283,28 @@ std::string handleCustomReport (
table.addCell (row, columnCount, tasks[row].getAttribute ("recur"));
}
else if (*col == "recurrence_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "R");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size (); ++row)
table.addCell (row, columnCount,
tasks[row].getAttribute ("recur") != "" ? "R" : "");
}
else if (*col == "tag_indicator")
{
table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "T");
table.setColumnWidth (columnCount, Table::minimum);
table.setColumnJustification (columnCount, Table::right);
for (unsigned int row = 0; row < tasks.size (); ++row)
table.addCell (row, columnCount,
tasks[row].getTagCount () ? "+" : "");
}
// Common to all columns.
// Add underline.
if (conf.get (std::string ("color"), true) || conf.get (std::string ("_forcecolor"), false))
@@ -2302,17 +2423,20 @@ void validReportColumns (const std::vector <std::string>& columns)
std::vector <std::string>::const_iterator it;
for (it = columns.begin (); it != columns.end (); ++it)
if (*it != "id" &&
*it != "uuid" &&
*it != "project" &&
*it != "priority" &&
*it != "entry" &&
*it != "start" &&
*it != "due" &&
*it != "age" &&
*it != "active" &&
*it != "tags" &&
*it != "recur" &&
if (*it != "id" &&
*it != "uuid" &&
*it != "project" &&
*it != "priority" &&
*it != "entry" &&
*it != "start" &&
*it != "due" &&
*it != "age" &&
*it != "active" &&
*it != "tags" &&
*it != "recur" &&
*it != "recurrence_indicator" &&
*it != "tag_indicator" &&
*it != "description_only" &&
*it != "description")
bad.push_back (*it);

View File

@@ -52,7 +52,7 @@ static std::string shortUsage (Config& conf)
{
std::stringstream out;
Table table;
int width = conf.get ("defaultwidth", 80);
int width = conf.get ("defaultwidth", (int) 80);
#ifdef HAVE_LIBNCURSES
if (conf.get ("curses", true))
{
@@ -84,6 +84,14 @@ static std::string shortUsage (Config& conf)
table.addCell (row, 1, "task add [tags] [attrs] desc...");
table.addCell (row, 2, "Adds a new task");
row = table.addRow ();
table.addCell (row, 1, "task append [tags] [attrs] desc...");
table.addCell (row, 2, "Appends more description to an existing task");
row = table.addRow ();
table.addCell (row, 1, "task annotate ID desc...");
table.addCell (row, 2, "Adds an annotation to an existing task");
row = table.addRow ();
table.addCell (row, 1, "task completed [tags] [attrs] desc...");
table.addCell (row, 2, "Chronological listing of all completed tasks matching the specified criteria");
@@ -94,7 +102,11 @@ static std::string shortUsage (Config& conf)
row = table.addRow ();
table.addCell (row, 1, "task ID /from/to/");
table.addCell (row, 2, "Perform the substitution on the desc, for fixing mistakes");
table.addCell (row, 2, "Performs one substitution on the task description, for fixing mistakes");
row = table.addRow ();
table.addCell (row, 1, "task ID /from/to/g");
table.addCell (row, 2, "Performs all substitutions on the task description, for fixing mistakes");
row = table.addRow ();
table.addCell (row, 1, "task delete ID");
@@ -164,6 +176,10 @@ static std::string shortUsage (Config& conf)
table.addCell (row, 1, "task stats");
table.addCell (row, 2, "Shows task database statistics");
row = table.addRow ();
table.addCell (row, 1, "task import");
table.addCell (row, 2, "Imports tasks from a variety of formats");
row = table.addRow ();
table.addCell (row, 1, "task export");
table.addCell (row, 2, "Exports all tasks as a CSV file");
@@ -561,6 +577,18 @@ Date getNextRecurrence (Date& current, std::string& period)
return Date (m, d, y);
}
if (period == "weekdays")
{
int dow = current.dayOfWeek ();
int days;
if (dow == 5) days = 3;
else if (dow == 6) days = 2;
else days = 1;
return current + (days * 86400);
}
if (isdigit (period[0]) && period[period.length () - 1] == 'm')
{
std::string numeric = period.substr (0, period.length () - 1);
@@ -650,6 +678,19 @@ Date getNextRecurrence (Date& current, std::string& period)
return Date (m, d, y);
}
else if (period == "annual" ||
period == "yearly")
{
y += 1;
// If the due data just happens to be 2/29 in a leap year, then simply
// incrementing y is going to create an invalid date.
if (m == 2 && d == 29)
d = 28;
return Date (m, d, y);
}
// If the period is an 'easy' one, add it to current, and we're done.
int days = convertDuration (period);
return current + (days * 86400);
@@ -743,12 +784,12 @@ void updateShadowFile (TDB& tdb, Config& conf)
catch (std::string& error)
{
std::cout << error << std::endl;
std::cerr << error << std::endl;
}
catch (...)
{
std::cout << "Unknown error." << std::endl;
std::cerr << "Unknown error." << std::endl;
}
}
@@ -816,15 +857,17 @@ std::string runTaskCommand (
// Commands that cause updates.
else if (command == "" && task.getId ()) { cmdMod = true; out = handleModify (tdb, task, conf); }
else if (command == "add") { cmdMod = true; out = handleAdd (tdb, task, conf); }
else if (command == "append") { cmdMod = true; out = handleAppend (tdb, task, conf); }
else if (command == "annotate") { cmdMod = true; out = handleAnnotate (tdb, task, conf); }
else if (command == "done") { cmdMod = true; out = handleDone (tdb, task, conf); }
else if (command == "undelete") { cmdMod = true; out = handleUndelete (tdb, task, conf); }
else if (command == "delete") { cmdMod = true; out = handleDelete (tdb, task, conf); }
else if (command == "start") { cmdMod = true; out = handleStart (tdb, task, conf); }
else if (command == "stop") { cmdMod = true; out = handleStop (tdb, task, conf); }
else if (command == "undo") { cmdMod = true; out = handleUndo (tdb, task, conf); }
else if (command == "import") { cmdMod = true; out = handleImport (tdb, task, conf); }
// Command that display IDs and therefore need TDB::gc first.
else if (command == "completed") { if (gc) gcMod = tdb.gc (); out = handleCompleted (tdb, task, conf); }
else if (command == "next") { if (gc) gcMod = tdb.gc (); out = handleReportNext (tdb, task, conf); }
else if (command == "active") { if (gc) gcMod = tdb.gc (); out = handleReportActive (tdb, task, conf); }

View File

@@ -75,6 +75,7 @@ std::string runTaskCommand (std::vector <std::string>&, TDB&, Config&, bool gc =
// command.cpp
std::string handleAdd (TDB&, T&, Config&);
std::string handleAppend (TDB&, T&, Config&);
std::string handleExport (TDB&, T&, Config&);
std::string handleDone (TDB&, T&, Config&);
std::string handleModify (TDB&, T&, Config&);
@@ -87,6 +88,8 @@ std::string handleStart (TDB&, T&, Config&);
std::string handleStop (TDB&, T&, Config&);
std::string handleUndo (TDB&, T&, Config&);
std::string handleColor (Config&);
std::string handleAnnotate (TDB&, T&, Config&);
T findT (int, const std::vector <T>&);
// report.cpp
void filter (std::vector<T>&, T&);
@@ -105,13 +108,12 @@ std::string handleCustomReport (TDB&, T&, Config&, const std::string&);
void validReportColumns (const std::vector <std::string>&);
void validSortColumns (const std::vector <std::string>&, const std::vector <std::string>&);
// util.cpp
bool confirm (const std::string&);
// text.cpp
void wrapText (std::vector <std::string>&, const std::string&, const int);
std::string trimLeft (const std::string& in, const std::string& t = " ");
std::string trimRight (const std::string& in, const std::string& t = " ");
std::string trim (const std::string& in, const std::string& t = " ");
void extractParagraphs (const std::string&, std::vector<std::string>&);
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&);
@@ -119,12 +121,15 @@ void join (std::string&, const std::string&, const std::vector<std::string>&);
std::string commify (const std::string&);
std::string lowerCase (const std::string&);
std::string upperCase (const std::string&);
const char* optionalBlankLine (Config&);
// util.cpp
bool confirm (const std::string&);
void delay (float);
int autoComplete (const std::string&, const std::vector<std::string>&, std::vector<std::string>&);
void formatTimeDeltaDays (std::string&, time_t);
std::string formatSeconds (time_t);
int autoComplete (const std::string&, const std::vector<std::string>&, std::vector<std::string>&);
const std::string uuid ();
const char* optionalBlankLine (Config&);
int convertDuration (const std::string&);
std::string expandPath (const std::string&);
@@ -137,8 +142,13 @@ std::string expandPath (const std::string&);
int flock (int, int);
#endif
bool slurp (const std::string&, std::vector <std::string>&, bool trimLines = false);
// rules.cpp
void initializeColorRules (Config&);
void autoColorize (T&, Text::color&, Text::color&, Config&);
// import.cpp
std::string handleImport (TDB&, T&, Config&);
////////////////////////////////////////////////////////////////////////////////

View File

@@ -3,4 +3,5 @@ t.benchmark.t
tdb.t
date.t
duration.t
text.t
autocomplete.t

View File

@@ -1,4 +1,4 @@
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
LFLAGS = -L/usr/local/lib
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../util.o ../Config.o
@@ -32,3 +32,9 @@ duration.t: duration.t.o $(OBJECTS) test.o
t.benchmark.t: t.benchmark.t.o $(OBJECTS) test.o
g++ t.benchmark.t.o $(OBJECTS) test.o $(LFLAGS) -o t.benchmark.t
text.t: text.t.o $(OBJECTS) test.o
g++ text.t.o $(OBJECTS) test.o $(LFLAGS) -o text.t
autocomplete.t: autocomplete.t.o $(OBJECTS) test.o
g++ autocomplete.t.o $(OBJECTS) test.o $(LFLAGS) -o autocomplete.t

74
src/tests/annotate.t Executable file
View File

@@ -0,0 +1,74 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'annotate.rc')
{
print $fh "data.location=.\n",
"report.r.description=r\n",
"report.r.columns=id,description\n",
"report.r.sort=id+\n";
close $fh;
ok (-r 'annotate.rc', 'Created annotate.rc');
}
# Add two tasks, annotate one twice.
qx{../task rc:annotate.rc add one};
qx{../task rc:annotate.rc add two};
qx{../task rc:annotate.rc annotate 1 foo};
sleep 2;
qx{../task rc:annotate.rc annotate 1 bar};
my $output = qx{../task rc:annotate.rc r};
# ID Description
# -- -------------------------------
# 1 one
# 3/24/2009 foo
# 3/24/2009 bar
# 2 two
#
# 2 tasks
like ($output, qr/1 one/, 'task 1');
like ($output, qr/2 two/, 'task 2');
like ($output, qr/one.+\d{1,2}\/\d{1,2}\/\d{4} foo/ms, 'first annotation');
like ($output, qr/foo.+\d{1,2}\/\d{1,2}\/\d{4} bar/ms, 'second annotation');
like ($output, qr/2 tasks/, 'count');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'annotate.rc';
ok (!-r 'annotate.rc', 'Removed annotate.rc');
exit 0;

55
src/tests/append.t Executable file
View File

@@ -0,0 +1,55 @@
#! /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 => 4;
# Create the rc file.
if (open my $fh, '>', 'append.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'append.rc', 'Created append.rc');
}
# Add a task, then append more decsription.
qx{../task rc:append.rc add foo};
qx{../task rc:append.rc 1 append bar};
my $output = qx{../task rc:append.rc info 1};
like ($output, qr/Description\s+foo\sbar\n/, 'append worked');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'append.rc';
ok (!-r 'append.rc', 'Removed append.rc');
exit 0;

View File

@@ -0,0 +1,63 @@
////////////////////////////////////////////////////////////////////////////////
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <test.h>
#include <../task.h>
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (8);
std::vector <std::string> options;
options.push_back ("abc");
options.push_back ("abcd");
options.push_back ("abcde");
options.push_back ("bcdef");
options.push_back ("cdefg");
std::vector <std::string> matches;
int result = autoComplete ("", options, matches);
t.is (result, 0, "no match on empty string");
result = autoComplete ("x", options, matches);
t.is (result, 0, "no match on wrong string");
result = autoComplete ("abcd", options, matches);
t.is (result, 1, "exact match on 'abcd'");
t.is (matches[0], "abcd", "exact match on 'abcd'");
result = autoComplete ("ab", options, matches);
t.is (result, 3, "partial match on 'ab'");
t.is (matches[0], "abc", "partial match on 'abc'");
t.is (matches[1], "abcd", "partial match on 'abcd'");
t.is (matches[2], "abcde", "partial match on 'abcde'");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

79
src/tests/bug.annual.t Executable file
View File

@@ -0,0 +1,79 @@
#! /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 => 14;
# Create the rc file.
if (open my $fh, '>', 'annual.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'annual.rc', 'Created annual.rc');
}
# If a task is added with a due date ten years ago, with an annual recurrence,
# then the synthetic tasks in between then and now have a due date that creeps.
#
# ID Project Pri Due Active Age Description
# -- ------- --- ---------- ------ --- -----------
# 2 1/1/2000 - foo
# 3 12/31/2000 - foo
# 4 12/31/2001 - foo
# 5 12/31/2002 - foo
# 6 12/31/2003 - foo
# 7 12/30/2004 - foo
# 8 12/30/2005 - foo
# 9 12/30/2006 - foo
# 10 12/30/2007 - foo
# 11 12/29/2008 - foo
# 12 12/29/2009 - foo
qx{../task rc:annual.rc add foo due:1/1/2000 recur:annual until:1/1/2009};
my $output = qx{../task rc:annual.rc list};
like ($output, qr/2\s+1\/1\/2000\s+- foo/, 'synthetic 1 no creep');
like ($output, qr/3\s+1\/1\/2001\s+- foo/, 'synthetic 2 no creep');
like ($output, qr/4\s+1\/1\/2002\s+- foo/, 'synthetic 3 no creep');
like ($output, qr/5\s+1\/1\/2003\s+- foo/, 'synthetic 4 no creep');
like ($output, qr/6\s+1\/1\/2004\s+- foo/, 'synthetic 5 no creep');
like ($output, qr/7\s+1\/1\/2005\s+- foo/, 'synthetic 6 no creep');
like ($output, qr/8\s+1\/1\/2006\s+- foo/, 'synthetic 7 no creep');
like ($output, qr/9\s+1\/1\/2007\s+- foo/, 'synthetic 8 no creep');
like ($output, qr/10\s+1\/1\/2008\s+- foo/, 'synthetic 9 no creep');
like ($output, qr/11\s+1\/1\/2009\s+- foo/, 'synthetic 10 no creep');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'annual.rc';
ok (!-r 'annual.rc', 'Removed annual.rc');
exit 0;

View File

@@ -28,7 +28,7 @@
use strict;
use warnings;
use Test::More tests => 5;
use Test::More tests => 6;
# Create the rc file.
if (open my $fh, '>', 'bug_concat.rc')
@@ -47,7 +47,6 @@ if (open my $fh, '>', 'bug_concat.rc')
# Thisisanewdescription
qx{../task rc:bug_concat.rc add This is the original text};
my $output = qx{../task rc:bug_concat.rc info 1};
like ($output, qr/Description\s+This is the original text\n/, 'original correct');
@@ -55,6 +54,18 @@ qx{../task rc:bug_concat.rc 1 This is the modified text};
$output = qx{../task rc:bug_concat.rc info 1};
like ($output, qr/Description\s+This is the modified text\n/, 'modified correct');
# When a task is added like this:
#
# % task add aaa bbb:ccc ddd
#
# The description is concatenated thus:
#
# aaabbb:ccc ddd
qx{../task rc:bug_concat.rc add aaa bbb:ccc ddd};
$output = qx{../task rc:bug_concat.rc info 2};
like ($output, qr/Description\s+aaa bbb:ccc ddd\n/, 'properly concatenated');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');

109
src/tests/confirmation.t Executable file
View File

@@ -0,0 +1,109 @@
#! /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 => 26;
# Create the rc file.
if (open my $fh, '>', 'confirm.rc')
{
print $fh "data.location=.\n",
"confirmation=yes\n";
close $fh;
ok (-r 'confirm.rc', 'Created confirm.rc');
}
# Create the response file.
if (open my $fh, '>', 'response.txt')
{
print $fh "\n\nn\n";
close $fh;
ok (-r 'response.txt', 'Created 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\? \(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\? \(y\/n\)/, 'confirmation - ye works');
unlike ($output, qr/Task not deleted\./, 'confirmation - ye works');
$output = qx{echo "y" | ../task rc:confirm.rc del 3};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - y works');
unlike ($output, qr/Task not deleted\./, 'confirmation - y works');
$output = qx{echo "YES" | ../task rc:confirm.rc del 4};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - YES works');
unlike ($output, qr/Task not deleted\./, 'confirmation - YES works');
$output = qx{echo "YE" | ../task rc:confirm.rc del 5};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - YE works');
unlike ($output, qr/Task not deleted\./, 'confirmation - YE works');
$output = qx{echo "Y" | ../task rc:confirm.rc del 6};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - Y works');
unlike ($output, qr/Task not deleted\./, 'confirmation - Y works');
# Test the various forms of "no".
$output = qx{echo "no" | ../task rc:confirm.rc del 7};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - no works');
like ($output, qr/Task not deleted\./, 'confirmation - no works');
$output = qx{echo "n" | ../task rc:confirm.rc del 7};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - n works');
like ($output, qr/Task not deleted\./, 'confirmation - n works');
$output = qx{echo "NO" | ../task rc:confirm.rc del 7};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - NO works');
like ($output, qr/Task not deleted\./, 'confirmation - NO works');
$output = qx{echo "N" | ../task rc:confirm.rc del 7};
like ($output, qr/Permanently delete task\? \(y\/n\)/, 'confirmation - N works');
like ($output, qr/Task not deleted\./, 'confirmation - N works');
# Test newlines.
$output = qx{cat response.txt | ../task rc:confirm.rc del 7};
like ($output, qr/(Permanently delete task\? \(y\/n\)) \1 \1/, 'confirmation - \n re-prompt works');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'response.txt';
ok (!-r 'response.txt', 'Removed response.txt');
unlink 'confirm.rc';
ok (!-r 'confirm.rc', 'Removed confirm.rc');
exit 0;

61
src/tests/custom.recur_ind.t Executable file
View File

@@ -0,0 +1,61 @@
#! /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, '>', 'custom.rc')
{
print $fh "data.location=.\n",
"report.foo.description=DESC\n",
"report.foo.columns=id,recurrence_indicator\n",
"report.foo.labels=ID,R\n",
"report.foo.sort=id+\n";
close $fh;
ok (-r 'custom.rc', 'Created custom.rc');
}
# Generate the usage screen, and locate the custom report on it.
qx{../task rc:custom.rc add foo due:tomorrow recur:weekly};
qx{../task rc:custom.rc add bar};
my $output = qx{../task rc:custom.rc foo 2>&1};
like ($output, qr/ID R/, 'Recurrence indicator heading');
like ($output, qr/3\s+R/, 'Recurrence indicator t1');
unlike ($output, qr/2\s+R/, 'No recurrence indicator t2');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'custom.rc';
ok (!-r 'custom.rc', 'Removed custom.rc');
exit 0;

61
src/tests/custom.tag_ind.t Executable file
View File

@@ -0,0 +1,61 @@
#! /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, '>', 'custom.rc')
{
print $fh "data.location=.\n",
"report.foo.description=DESC\n",
"report.foo.columns=id,tag_indicator\n",
"report.foo.labels=ID,T\n",
"report.foo.sort=id+\n";
close $fh;
ok (-r 'custom.rc', 'Created custom.rc');
}
# Generate the usage screen, and locate the custom report on it.
qx{../task rc:custom.rc add foo +tag};
qx{../task rc:custom.rc add bar};
my $output = qx{../task rc:custom.rc foo 2>&1};
like ($output, qr/ID T/, 'Tag indicator heading');
like ($output, qr/1\s+\+/, 'Tag indicator t1');
unlike ($output, qr/2\s+\+/, 'No tag indicator t2');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'custom.rc';
ok (!-r 'custom.rc', 'Removed custom.rc');
exit 0;

View File

@@ -33,7 +33,8 @@ use Test::More tests => 16;
# Create the rc file.
if (open my $fh, '>', 'undelete.rc')
{
print $fh "data.location=.\n";
print $fh "data.location=.\n",
"echo.command=no\n";
close $fh;
ok (-r 'undelete.rc', 'Created undelete.rc');
}

View File

@@ -37,17 +37,18 @@ if (open my $fh, '>', 'due.rc')
"due=4\n",
"color=on\n",
"color.due=red\n",
"_forcecolor=on\n";
"_forcecolor=on\n",
"dateformat=m/d/Y\n";
close $fh;
ok (-r 'due.rc', 'Created due.rc');
}
# Add a task that is almost due, and one that is just due.
my ($d, $m, $y) = (localtime (time + 3 * 86_400))[3..5];
my $just = sprintf ("%d/%02d/%d", $m + 1, $d, $y + 1900);
my $just = sprintf ("%d/%d/%d", $m + 1, $d, $y + 1900);
($d, $m, $y) = (localtime (time + 5 * 86_400))[3..5];
my $almost = sprintf ("%d/%02d/%d", $m + 1, $d, $y + 1900);
my $almost = sprintf ("%d/%d/%d", $m + 1, $d, $y + 1900);
qx{../task rc:due.rc add one due:$just};
qx{../task rc:due.rc add two due:$almost};

View File

@@ -42,6 +42,7 @@ int main (int argc, char** argv)
std::string d;
d = "daily"; t.is (convertDuration (d), 1, "duration daily = 1");
d = "weekdays"; t.is (convertDuration (d), 1, "duration weekdays = 1");
d = "day"; t.is (convertDuration (d), 1, "duration day = 1");
d = "0d"; t.is (convertDuration (d), 0, "duration 0d = 0");
d = "1d"; t.is (convertDuration (d), 1, "duration 1d = 1");
@@ -53,7 +54,6 @@ int main (int argc, char** argv)
d = "sennight"; t.is (convertDuration (d), 7, "duration sennight = 7");
d = "biweekly"; t.is (convertDuration (d), 14, "duration biweekly = 14");
d = "fortnight"; t.is (convertDuration (d), 14, "duration fortnight = 14");
d = "week"; t.is (convertDuration (d), 7, "duration week = 7");
d = "0w"; t.is (convertDuration (d), 0, "duration 0w = 0");
d = "1w"; t.is (convertDuration (d), 7, "duration 1w = 7");
d = "7w"; t.is (convertDuration (d), 49, "duration 7w = 49");

70
src/tests/import.143.t Executable file
View File

@@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'id','status','tags','entry','start','due','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

70
src/tests/import.150.t Executable file
View File

@@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'id','uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

70
src/tests/import.160.t Executable file
View File

@@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'uuid','status','tags','entry','start','due','recur','end','project','priority','fg','bg','description'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo bar'\n",
"'7f7a4191-c2f2-487f-8855-7a1eb378c267','pending','',1238037947,,,,,'A','M',,,'foo, bar'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+M.+foo bar/, 't1');
like ($output, qr/2.+A.+M.+foo, bar/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

69
src/tests/import.cmd.t Executable file
View File

@@ -0,0 +1,69 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "This is a test priority:H project:A\n",
"Another task\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+A.+H.+This is a test/, 't1');
like ($output, qr/2.+Another task/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

70
src/tests/import.csv.t Executable file
View File

@@ -0,0 +1,70 @@
#! /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 => 8;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "'id','priority','description'\n",
"1,H,'this is a test'\n",
"2,,'another task'\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 2 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+H.+this is a test/, 't1');
like ($output, qr/2.+another task/, 't2');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

76
src/tests/import.todo.t Executable file
View File

@@ -0,0 +1,76 @@
#! /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 => 10;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "x 2009-03-25 Walk the dog +project \@context\n",
"This is a test +project \@context\n",
"(A) A prioritized task\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 3 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+project.+This is a test/, 't1');
like ($output, qr/2.+H.+A prioritized task/, 't2');
$output = qx{../task rc:import.rc completed};
like ($output, qr/3\/25\/2009.+Walk the dog/, 't3');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

71
src/tests/import.txt.t Executable file
View File

@@ -0,0 +1,71 @@
#! /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 => 9;
# Create the rc file.
if (open my $fh, '>', 'import.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'import.rc', 'Created import.rc');
}
# Create import file.
if (open my $fh, '>', 'import.txt')
{
print $fh "Get milk, bread\n",
"Order cake\n",
"Clean house\n",
"\n";
close $fh;
ok (-r 'import.txt', 'Created sample import data');
}
my $output = qx{../task rc:import.rc import import.txt};
is ($output, "Imported 3 tasks successfully, with 0 errors.\n", 'no errors');
$output = qx{../task rc:import.rc list};
like ($output, qr/1.+Get milk, bread/, 't1');
like ($output, qr/2.+Order cake/, 't2');
like ($output, qr/3.+Clean house/, 't3');
# Cleanup.
unlink 'import.txt';
ok (!-r 'import.txt', 'Removed import.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'import.rc';
ok (!-r 'import.rc', 'Removed import.rc');
exit 0;

66
src/tests/label.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 => 8;
# Create the rc file.
if (open my $fh, '>', 'custom.rc')
{
print $fh "data.location=.\n",
"report.foo.description=DESC\n",
"report.foo.labels=ID,DESCRIPTION\n",
"report.foo.columns=id,description\n",
"report.foo.sort=id+\n",
"report.foo.filter=project:A\n";
close $fh;
ok (-r 'custom.rc', 'Created custom.rc');
}
# Generate the usage screen, and locate the custom report on it.
my $output = qx{../task rc:custom.rc usage};
like ($output, qr/task foo \[tags\] \[attrs\] desc\.\.\.\s+DESC\n/m, 'report.foo');
qx{../task rc:custom.rc add project:A one};
qx{../task rc:custom.rc add two};
$output = qx{../task rc:custom.rc foo};
like ($output, qr/ID/, 'custom label for id column');
like ($output, qr/DESCRIPTION/, 'custom label for description column');
like ($output, qr/one/, 'custom filter included');
unlike ($output, qr/two/, 'custom filter excluded');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'custom.rc';
ok (!-r 'custom.rc', 'Removed custom.rc');
exit 0;

View File

@@ -28,7 +28,7 @@
use strict;
use warnings;
use Test::More tests => 25;
use Test::More tests => 33;
# Create the rc file.
if (open my $fh, '>', 'oldest.rc')
@@ -39,10 +39,23 @@ if (open my $fh, '>', 'oldest.rc')
}
# Add 11 tasks. Oldest should show 1-10, newest should show 2-11.
diag ("Adding 11 tasks - takes 10 seconds");
diag ("Adding 3 tasks - takes 2 seconds");
qx{../task rc:oldest.rc add one; sleep 1};
qx{../task rc:oldest.rc add two; sleep 1};
qx{../task rc:oldest.rc add three; sleep 1};
my $output = qx{../task rc:oldest.rc oldest};
like ($output, qr/one/, 'oldest: one');
like ($output, qr/two/, 'oldest: two');
like ($output, qr/three/, 'oldest: three');
like ($output, qr/one.*two.*three/ms, 'oldest: sort');
$output = qx{../task rc:oldest.rc newest};
like ($output, qr/three/, 'newest: three');
like ($output, qr/two/, 'newest: two');
like ($output, qr/one/, 'newest: one');
like ($output, qr/three.*two.*one/ms, 'newest: sort');
diag ("Adding 8 tasks - takes 7 seconds");
qx{../task rc:oldest.rc add four; sleep 1};
qx{../task rc:oldest.rc add five; sleep 1};
qx{../task rc:oldest.rc add six; sleep 1};
@@ -52,7 +65,7 @@ qx{../task rc:oldest.rc add nine; sleep 1};
qx{../task rc:oldest.rc add ten; sleep 1};
qx{../task rc:oldest.rc add eleven};
my $output = qx{../task rc:oldest.rc oldest};
$output = qx{../task rc:oldest.rc oldest};
like ($output, qr/one/, 'oldest: one');
like ($output, qr/two/, 'oldest: two');
like ($output, qr/three/, 'oldest: three');

View File

@@ -28,7 +28,7 @@
use strict;
use warnings;
use Test::More tests => 6;
use Test::More tests => 5;
# Create the rc file.
if (open my $fh, '>', 'recur.rc')
@@ -54,9 +54,6 @@ $output = qx{../task rc:recur.rc desc};
like ($output, qr/second .* third .* first/msx, 'weekly 3d daily');
# Cleanup.
unlink 'shadow.txt';
ok (!-r 'shadow.txt', 'Removed shadow.txt');
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');

64
src/tests/recur.weekdays.t Executable file
View File

@@ -0,0 +1,64 @@
#! /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, '>', 'recur.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'recur.rc', 'Created recur.rc');
}
# Create a few recurring tasks, and test the sort order of the recur column.
qx{../task rc:recur.rc add due:friday recur:weekdays one};
my $output = qx{../task rc:recur.rc list};
like ($output, qr/one/, 'recur weekdays');
$output = qx{../task rc:recur.rc info 1};
like ($output, qr/Status\s+Recurring/, 'task is recurring');
like ($output, qr/Recurrence\s+weekdays/, 'task recurs every weekday');
qx{../task rc:recur.rc do 1};
$output = qx{../task rc:recur.rc list};
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'completed.data';
ok (!-r 'completed.data', 'Removed completed.data');
unlink 'recur.rc';
ok (!-r 'recur.rc', 'Removed recur.rc');
exit 0;

69
src/tests/substitute.t Executable file
View File

@@ -0,0 +1,69 @@
#! /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, '>', 'subst.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'subst.rc', 'Created subst.rc');
}
# Test the substitution command.
qx{../task rc:subst.rc add foo foo foo};
qx{../task rc:subst.rc 1 /foo/FOO/};
my $output = qx{../task rc:subst.rc info 1};
like ($output, qr/FOO foo foo/, 'substitution in description');
qx{../task rc:subst.rc 1 /foo/FOO/g};
$output = qx{../task rc:subst.rc info 1};
like ($output, qr/FOO FOO FOO/, 'global substitution in description');
# Test the substitution command on annotations.
qx{../task rc:subst.rc annotate 1 bar bar bar};
qx{../task rc:subst.rc 1 /bar/BAR/};
$output = qx{../task rc:subst.rc info 1};
like ($output, qr/BAR bar bar/, 'substitution in annotation');
qx{../task rc:subst.rc 1 /bar/BAR/g};
$output = qx{../task rc:subst.rc info 1};
like ($output, qr/BAR BAR BAR/, 'global substitution in annotation');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'subst.rc';
ok (!-r 'subst.rc', 'Removed subst.rc');
exit 0;

View File

@@ -31,11 +31,11 @@
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest test (5);
UnitTest test (10);
T t;
std::string s = t.compose ();
test.is ((int)s.length (), 46, "T::T (); T::compose ()");
test.is ((int)s.length (), 49, "T::T (); T::compose ()");
test.diag (s);
t.setStatus (T::completed);
@@ -53,12 +53,42 @@ int main (int argc, char** argv)
test.is (s[37], 'r', "T::setStatus (recurring)");
test.diag (s);
// Round trip test.
std::string sample = "00000000-0000-0000-0000-000000000000 - [] [] Sample";
T t2;
t2.parse (sample);
sample += "\n";
test.is (t2.compose (), sample, "T::parse -> T::compose round trip");
std::string format3 = "00000000-0000-0000-0000-000000000000 - [] [] [] Sample\n";
// Format 1 Round trip test.
std::string sample = "[] [] Sample";
T tf1;
tf1.parse (sample);
test.is (tf1.compose ().substr (36, std::string::npos),
format3.substr (36, std::string::npos),
"T::parse format 1 -> T::compose round trip");
// Format 2 Round trip test.
sample = "00000000-0000-0000-0000-000000000000 - [] [] Sample";
T tf2;
tf2.parse (sample);
test.is (tf2.compose (), format3, "T::parse format 2 -> T::compose round trip");
// Format 3 Round trip test.
sample = "00000000-0000-0000-0000-000000000000 - [] [] [] Sample";
T tf3;
tf3.parse (sample);
test.is (tf3.compose (), format3, "T::parse format 3 -> T::compose round trip");
// b10b3236-70d8-47bb-840a-b4c430758fb6 - [foo] [bar:baz] [1237865996:'woof'] sample\n
// ....:....|....:....|....:....|....:....|....:....|....:....|....:....|....:....|....:....|
// ^ ^ ^
// 0 36 66
t.setStatus (T::pending);
t.addTag ("foo");
t.setAttribute ("bar", "baz");
t.addAnnotation ("woof");
t.setDescription ("sample");
std::string format = t.compose ();
test.is (format.substr (36, 20), " - [foo] [bar:baz] [", "compose tag, attribute");
test.is (format.substr (66, 16), ":\"woof\"] sample\n", "compose annotation");
test.is (t.getAnnotationCount (), 1, "annotation count");
return 0;
}

234
src/tests/text.t.cpp Normal file
View File

@@ -0,0 +1,234 @@
////////////////////////////////////////////////////////////////////////////////
// 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
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include "task.h"
#include "test.h"
////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv)
{
UnitTest t (94);
// 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.";
std::vector <std::string> lines;
wrapText (lines, text, 10);
t.is (lines.size (), (size_t) 5, "wrapText 'This is a test of the line wrapping code.' -> total 5 lines");
t.is (lines[0], "This is a", "wrapText line 0 -> 'This is a'");
t.is (lines[1], "test of", "wrapText line 1 -> 'test of'");
t.is (lines[2], "the line", "wrapText line 2 -> 'the line'");
t.is (lines[3], "wrapping", "wrapText line 3 -> 'wrapping'");
t.is (lines[4], "code.", "wrapText line 4 -> 'code.'");
#ifdef NOPE
// void wrapText (std::vector <std::string>& lines, const std::string& text, const int width)
text = "This ☺ is a test of utf8 line extraction.";
lines.clear ();
wrapText (lines, text, 7);
t.is (lines.size (), (size_t) 7, "wrapText 'This ☺ is a test of utf8 line extraction.' -> total 7 lines");
t.is (lines[0], "This ☺", "wrapText line 0 -> 'This ☺'");
t.is (lines[1], "is a", "wrapText line 1 -> 'is a'");
t.is (lines[2], "test of", "wrapText line 2 -> 'test of'");
t.is (lines[3], "utf8", "wrapText line 3 -> 'utf8'");
t.is (lines[4], "line", "wrapText line 4 -> 'line'");
t.is (lines[5], "extrac-", "wrapText line 5 -> 'extrac-'");
t.is (lines[6], "tion.", "wrapText line 6 -> 'tion.'");
// void extractLine (std::string& text, std::string& line, int length)
text = "This ☺ is a test of utf8 line extraction.";
#endif
std::string line;
#ifdef NOPE
extractLine (text, line, 7);
t.is (line, "line 1", "extractLine 7 'This ☺ is a test of utf8 line extraction.' -> 'This ☺'");
#endif
// void extractLine (std::string& text, std::string& line, int length)
text = "line 1\nlengthy second line that exceeds width";
extractLine (text, line, 10);
t.is (line, "line 1", "extractLine 10 'line 1\\nlengthy second line that exceeds width' -> 'line 1'");
extractLine (text, line, 10);
t.is (line, "lengthy", "extractLine 10 'lengthy second line that exceeds width' -> 'lengthy'");
extractLine (text, line, 10);
t.is (line, "second", "extractLine 10 'second line that exceeds width' -> 'second'");
extractLine (text, line, 10);
t.is (line, "line that", "extractLine 10 'line that exceeds width' -> 'line that'");
extractLine (text, line, 10);
t.is (line, "exceeds", "extractLine 10 'exceeds width' -> 'exceeds'");
extractLine (text, line, 10);
t.is (line, "width", "extractLine 10 'width' -> 'width'");
extractLine (text, line, 10);
t.is (line, "", "extractLine 10 '' -> ''");
// void split (std::vector<std::string>& results, const std::string& input, const char delimiter)
std::vector <std::string> items;
std::string unsplit = "";
split (items, unsplit, '-');
t.is (items.size (), (size_t) 0, "split '' '-' -> 0 items");
unsplit = "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] ''");
unsplit = "-a-bc-def";
split (items, unsplit, '-');
t.is (items.size (), (size_t) 4, "split '-a-bc-def' '-' -> '' 'a' 'bc' 'def'");
t.is (items[0], "", "split '-a-bc-def' '-' -> [0] ''");
t.is (items[1], "a", "split '-a-bc-def' '-' -> [1] 'a'");
t.is (items[2], "bc", "split '-a-bc-def' '-' -> [2] 'bc'");
t.is (items[3], "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");
unsplit = "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] ''");
unsplit = "--a--bc--def";
split (items, unsplit, "--");
t.is (items.size (), (size_t) 4, "split '-a-bc-def' '--' -> '' 'a' 'bc' 'def'");
t.is (items[0], "", "split '-a-bc-def' '--' -> [0] ''");
t.is (items[1], "a", "split '-a-bc-def' '--' -> [1] 'a'");
t.is (items[2], "bc", "split '-a-bc-def' '--' -> [2] 'bc'");
t.is (items[3], "def", "split '-a-bc-def' '--' -> [3] 'def'");
// void join (std::string& result, const std::string& separator, const std::vector<std::string>& items)
std::vector <std::string> unjoined;
std::string joined;
join (joined, "", unjoined);
t.is (joined.length (), (size_t) 0, "join -> length 0");
t.is (joined, "", "join -> ''");
unjoined.push_back ("");
unjoined.push_back ("a");
unjoined.push_back ("bc");
unjoined.push_back ("def");
join (joined, "", unjoined);
t.is (joined.length (), (size_t) 6, "join '' 'a' 'bc' 'def' -> length 6");
t.is (joined, "abcdef", "join '' 'a' 'bc' 'def' -> 'abcdef'");
join (joined, "-", unjoined);
t.is (joined.length (), (size_t) 9, "join '' - 'a' - 'bc' - 'def' -> length 9");
t.is (joined, "-a-bc-def", "join '' - 'a' - 'bc' - 'def' -> '-a-bc-def'");
// std::string trimLeft (const std::string& in, const std::string& t /*= " "*/)
t.is (trimLeft (""), "", "trimLeft '' -> ''");
t.is (trimLeft ("", " \t"), "", "trimLeft '' -> ''");
t.is (trimLeft ("xxx"), "xxx", "trimLeft 'xxx' -> 'xxx'");
t.is (trimLeft ("xxx", " \t"), "xxx", "trimLeft 'xxx' -> 'xxx'");
t.is (trimLeft (" \t xxx \t "), "\t xxx \t ", "trimLeft ' \\t xxx \\t ' -> '\\t xxx \\t '");
t.is (trimLeft (" \t xxx \t ", " \t"), "xxx \t ", "trimLeft ' \\t xxx \\t ' -> 'xxx \\t '");
// std::string trimRight (const std::string& in, const std::string& t /*= " "*/)
t.is (trimRight (""), "", "trimRight '' -> ''");
t.is (trimRight ("", " \t"), "", "trimRight '' -> ''");
t.is (trimRight ("xxx"), "xxx", "trimRight 'xxx' -> 'xxx'");
t.is (trimRight ("xxx", " \t"), "xxx", "trimRight 'xxx' -> 'xxx'");
t.is (trimRight (" \t xxx \t "), " \t xxx \t", "trimRight ' \\t xxx \\t ' -> ' \\t xxx \\t'");
t.is (trimRight (" \t xxx \t ", " \t"), " \t xxx", "trimRight ' \\t xxx \\t ' -> ' \\t xxx'");
// std::string trim (const std::string& in, const std::string& t /*= " "*/)
t.is (trim (""), "", "trim '' -> ''");
t.is (trim ("", " \t"), "", "trim '' -> ''");
t.is (trim ("xxx"), "xxx", "trim 'xxx' -> 'xxx'");
t.is (trim ("xxx", " \t"), "xxx", "trim 'xxx' -> 'xxx'");
t.is (trim (" \t xxx \t "), "\t xxx \t", "trim ' \\t xxx \\t ' -> '\\t xxx \\t'");
t.is (trim (" \t xxx \t ", " \t"), "xxx", "trim ' \\t xxx \\t ' -> 'xxx'");
// std::string unquoteText (const std::string& text)
t.is (unquoteText (""), "", "unquoteText '' -> ''");
t.is (unquoteText ("x"), "x", "unquoteText 'x' -> 'x'");
t.is (unquoteText ("'x"), "'x", "unquoteText ''x' -> ''x'");
t.is (unquoteText ("x'"), "x'", "unquoteText 'x'' -> 'x''");
t.is (unquoteText ("\"x"), "\"x", "unquoteText '\"x' -> '\"x'");
t.is (unquoteText ("x\""), "x\"", "unquoteText 'x\"' -> 'x\"'");
t.is (unquoteText ("''"), "", "unquoteText '''' -> ''");
t.is (unquoteText ("'''"), "'", "unquoteText ''''' -> '''");
t.is (unquoteText ("\"\""), "", "unquoteText '\"\"' -> ''");
t.is (unquoteText ("\"\"\""), "\"", "unquoteText '\"\"\"' -> '\"'");
t.is (unquoteText ("''''"), "''", "unquoteText '''''' -> ''''");
t.is (unquoteText ("\"\"\"\""), "\"\"", "unquoteText '\"\"\"\"' -> '\"\"'");
t.is (unquoteText ("'\"\"'"), "\"\"", "unquoteText '''\"\"' -> '\"\"'");
t.is (unquoteText ("\"''\""), "''", "unquoteText '\"''\"' -> ''''");
t.is (unquoteText ("'x'"), "x", "unquoteText ''x'' -> 'x'");
t.is (unquoteText ("\"x\""), "x", "unquoteText '\"x\"' -> 'x'");
// std::string commify (const std::string& data)
t.is (commify (""), "", "commify '' -> ''");
t.is (commify ("1"), "1", "commify '1' -> '1'");
t.is (commify ("12"), "12", "commify '12' -> '12'");
t.is (commify ("123"), "123", "commify '123' -> '123'");
t.is (commify ("1234"), "1,234", "commify '1234' -> '1,234'");
t.is (commify ("12345"), "12,345", "commify '12345' -> '12,345'");
t.is (commify ("123456"), "123,456", "commify '123456' -> '123,456'");
t.is (commify ("1234567"), "1,234,567", "commify '1234567' -> '1,234,567'");
t.is (commify ("12345678"), "12,345,678", "commify '12345678' -> '12,345,678'");
t.is (commify ("123456789"), "123,456,789", "commify '123456789' -> '123,456,789'");
t.is (commify ("1234567890"), "1,234,567,890", "commify '1234567890' -> '1,234,567,890'");
t.is (commify ("pre"), "pre", "commify 'pre' -> 'pre'");
t.is (commify ("pre1234"), "pre1,234", "commify 'pre1234' -> 'pre1,234'");
t.is (commify ("1234post"), "1,234post", "commify '1234post' -> '1,234post'");
t.is (commify ("pre1234post"), "pre1,234post", "commify 'pre1234post' -> 'pre1,234post'");
// std::string lowerCase (const std::string& input)
t.is (lowerCase (""), "", "lowerCase '' -> ''");
t.is (lowerCase ("pre01_:POST"), "pre01_:post", "lowerCase 'pre01_:POST' -> 'pre01_:post'");
// std::string upperCase (const std::string& input)
t.is (upperCase (""), "", "upperCase '' -> ''");
t.is (upperCase ("pre01_:POST"), "PRE01_:POST", "upperCase 'pre01_:POST' -> 'PRE01_:POST'");
return 0;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -33,7 +33,8 @@ use Test::More tests => 15;
# Create the rc file.
if (open my $fh, '>', 'undo.rc')
{
print $fh "data.location=.\n";
print $fh "data.location=.\n",
"echo.command=no\n";
close $fh;
ok (-r 'undo.rc', 'Created undo.rc');
}

80
src/tests/utf8.t Executable file
View File

@@ -0,0 +1,80 @@
#! /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, '>', 'utf8.rc')
{
print $fh "data.location=.\n";
close $fh;
ok (-r 'utf8.rc', 'Created utf8.rc');
}
# Add a task with UTF8 in the description.
qx{../task rc:utf8.rc add Çirçös};
qx{../task rc:utf8.rc add Hello world ☺};
qx{../task rc:utf8.rc add ¥£€$¢₡₢₣₤₥₦₧₨₩₪₫₭₮₯};
qx{../task rc:utf8.rc add Pchnąć w tę łódź jeża lub ośm skrzyń fig};
qx{../task rc:utf8.rc add ๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า};
qx{../task rc:utf8.rc add イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム};
qx{../task rc:utf8.rc add いろはにほへとちりぬるを};
qx{../task rc:utf8.rc add D\\'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh};
qx{../task rc:utf8.rc add Árvíztűrő tükörfúrógép};
qx{../task rc:utf8.rc add Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa};
qx{../task rc:utf8.rc add Sævör grét áðan því úlpan var ónýt};
qx{../task rc:utf8.rc add Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen Wolther spillede på xylofon.};
qx{../task rc:utf8.rc add Falsches Üben von Xylophonmusik quält jeden größeren Zwerg};
qx{../task rc:utf8.rc add Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich};
qx{../task rc:utf8.rc add Heizölrückstoßabdämpfung};
qx{../task rc:utf8.rc add Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο};
qx{../task rc:utf8.rc add Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία};
my $output = qx{../task rc:utf8.rc ls};
diag ($output);
like ($output, qr/17/, 'all 17 tasks shown');
qx{../task rc:utf8.rc add project:Çirçös utf8 in project};
$output = qx{../task rc:utf8.rc ls project:Çirçös};
like ($output, qr/Çirçös.+utf8 in project/, 'utf8 in project works');
qx{../task rc:utf8.rc add utf8 in tag +☺};
$output = qx{../task rc:utf8.rc ls +☺};
like ($output, qr/utf8 in tag/, 'utf8 in tag works');
# Cleanup.
unlink 'pending.data';
ok (!-r 'pending.data', 'Removed pending.data');
unlink 'utf8.rc';
ok (!-r 'utf8.rc', 'Removed utf8.rc');
exit 0;

View File

@@ -104,28 +104,6 @@ void join (
}
}
////////////////////////////////////////////////////////////////////////////////
void extractParagraphs (const std::string& input, std::vector<std::string>& output)
{
std::string copy = input;
while (1)
{
size_t so = copy.find ("<p>");
size_t eo = copy.find ("</p>");
if (so == std::string::npos && eo == std::string::npos)
break;
std::string extract = trim (copy.substr (so + 3, eo - so - 3));
copy = copy.substr (eo + 4, std::string::npos);
output.push_back (extract);
}
// There were no paragraphs.
if (!output.size ())
output.push_back (input);
}
////////////////////////////////////////////////////////////////////////////////
std::string trimLeft (const std::string& in, const std::string& t /*= " "*/)
{
@@ -149,12 +127,19 @@ std::string trim (const std::string& in, const std::string& t /*= " "*/)
////////////////////////////////////////////////////////////////////////////////
// Remove enclosing balanced quotes. Assumes trimmed text.
void unquoteText (std::string& text)
std::string unquoteText (const std::string& input)
{
char quote = text[0];
if (quote == '\'' || quote == '"')
if (text[text.length () - 1] == quote)
text = text.substr (1, text.length () - 3);
std::string output = input;
if (output.length () > 1)
{
char quote = output[0];
if ((quote == '\'' || quote == '"') &&
output[output.length () - 1] == quote)
return output.substr (1, output.length () - 2);
}
return output;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -25,6 +25,7 @@
//
////////////////////////////////////////////////////////////////////////////////
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sys/types.h>
@@ -41,17 +42,28 @@
#include "../auto.h"
////////////////////////////////////////////////////////////////////////////////
// Uses std::getline, because std::cin eats leading whitespace, and that means
// that if a newline is entered, std::cin eats it and never returns from the
// "std::cin >> answer;" line, but it does disply the newline. This way, with
// std::getline, the newline can be detected, and the prompt re-written.
bool confirm (const std::string& question)
{
std::cout << question << " (y/n) ";
std::string answer;
std::cin >> answer;
answer = trim (answer);
if (answer == "y" || answer == "Y")
return true;
do
{
std::cout << question << " (y/n) ";
std::getline (std::cin, answer);
answer = lowerCase (trim (answer));
if (answer == "\n") std::cout << "newline\n";
}
while (answer != "y" &&
answer != "ye" &&
answer != "yes" &&
answer != "n" &&
answer != "no");
return false;
return (answer == "y" || answer == "ye" || answer == "yes") ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
@@ -84,19 +96,31 @@ void formatTimeDeltaDays (std::string& output, time_t delta)
if (days > 365)
sprintf (formatted, "%.1f yrs", (days / 365.2422));
else if (days > 84)
sprintf (formatted, "%1d mth%s", (int) (days / 30.6), ((int) (days / 30.6) == 1 ? "" : "s"));
sprintf (formatted, "%1d mth%s",
(int) (days / 30.6),
((int) (days / 30.6) == 1 ? "" : "s"));
else if (days > 13)
sprintf (formatted, "%d wk%s", (int) (days / 7.0), ((int) (days / 7.0) == 1 ? "" : "s"));
sprintf (formatted, "%d wk%s",
(int) (days / 7.0),
((int) (days / 7.0) == 1 ? "" : "s"));
else if (days > 5.0)
sprintf (formatted, "%d day%s", (int) days, ((int) days == 1 ? "" : "s"));
sprintf (formatted, "%d day%s",
(int) days,
((int) days == 1 ? "" : "s"));
else if (days > 1.0)
sprintf (formatted, "%.1f days", days);
else if (days * 24 > 1.0)
sprintf (formatted, "%d hr%s", (int) (days * 24.0), ((int) (days * 24.0) == 1 ? "" : "s"));
sprintf (formatted, "%d hr%s",
(int) (days * 24.0),
((int) (days * 24.0) == 1 ? "" : "s"));
else if (days * 24 * 60 > 1)
sprintf (formatted, "%d min%s", (int) (days * 24 * 60), ((int) (days * 24 * 60) == 1 ? "" : "s"));
sprintf (formatted, "%d min%s",
(int) (days * 24 * 60),
((int) (days * 24 * 60) == 1 ? "" : "s"));
else if (days * 24 * 60 * 60 > 1)
sprintf (formatted, "%d sec%s", (int) (days * 24 * 60 * 60), ((int) (days * 24 * 60 * 60) == 1 ? "" : "s"));
sprintf (formatted, "%d sec%s",
(int) (days * 24 * 60 * 60),
((int) (days * 24 * 60 * 60) == 1 ? "" : "s"));
else
strcpy (formatted, "-");
@@ -112,19 +136,31 @@ std::string formatSeconds (time_t delta)
if (days > 365)
sprintf (formatted, "%.1f yrs", (days / 365.2422));
else if (days > 84)
sprintf (formatted, "%1d mth%s", (int) (days / 30.6), ((int) (days / 30.6) == 1 ? "" : "s"));
sprintf (formatted, "%1d mth%s",
(int) (days / 30.6),
((int) (days / 30.6) == 1 ? "" : "s"));
else if (days > 13)
sprintf (formatted, "%d wk%s", (int) (days / 7.0), ((int) (days / 7.0) == 1 ? "" : "s"));
sprintf (formatted, "%d wk%s",
(int) (days / 7.0),
((int) (days / 7.0) == 1 ? "" : "s"));
else if (days > 5.0)
sprintf (formatted, "%d day%s", (int) days, ((int) days == 1 ? "" : "s"));
sprintf (formatted, "%d day%s",
(int) days,
((int) days == 1 ? "" : "s"));
else if (days > 1.0)
sprintf (formatted, "%.1f days", days);
else if (days * 24 > 1.0)
sprintf (formatted, "%d hr%s", (int) (days * 24.0), ((int) (days * 24) == 1 ? "" : "s"));
sprintf (formatted, "%d hr%s",
(int) (days * 24.0),
((int) (days * 24) == 1 ? "" : "s"));
else if (days * 24 * 60 > 1)
sprintf (formatted, "%d min%s", (int) (days * 24 * 60), ((int) (days * 24 * 60) == 1 ? "" : "s"));
sprintf (formatted, "%d min%s",
(int) (days * 24 * 60),
((int) (days * 24 * 60) == 1 ? "" : "s"));
else if (days * 24 * 60 * 60 > 1)
sprintf (formatted, "%d sec%s", (int) (days * 24 * 60 * 60), ((int) (days * 24 * 60 * 60) == 1 ? "" : "s"));
sprintf (formatted, "%d sec%s",
(int) (days * 24 * 60 * 60),
((int) (days * 24 * 60 * 60) == 1 ? "" : "s"));
else
strcpy (formatted, "-");
@@ -251,6 +287,7 @@ int convertDuration (const std::string& input)
supported.push_back ("daily");
supported.push_back ("day");
supported.push_back ("weekly");
supported.push_back ("weekdays");
supported.push_back ("sennight");
supported.push_back ("biweekly");
supported.push_back ("fortnight");
@@ -269,6 +306,7 @@ int convertDuration (const std::string& input)
std::string found = matches[0];
if (found == "daily" || found == "day") return 1;
else if (found == "weekdays") return 1;
else if (found == "weekly" || found == "sennight") return 7;
else if (found == "biweekly" || found == "fortnight") return 14;
else if (found == "monthly") return 30;
@@ -367,3 +405,28 @@ int flock (int fd, int operation)
#endif
////////////////////////////////////////////////////////////////////////////////
bool slurp (
const std::string& file,
std::vector <std::string>& contents,
bool trimLines /* = false */)
{
contents.clear ();
std::ifstream in (file.c_str ());
if (in.good ())
{
std::string line;
while (getline (in, line))
{
if (trimLines) line = trim (line);
contents.push_back (line);
}
in.close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1 +1 @@
<pkgref spec="1.12" uuid="C71026FD-E252-42CD-89C3-2F6F087AAF17"><config><identifier>com.beckingham.task142.task.pkg</identifier><version/><description></description><post-install type="none"/><requireAuthorization/><installFrom>/Users/paul/task.git/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.task143.task.pkg</identifier><version></version><description></description><post-install type="none"/><requireAuthorization/><installFrom>/Users/paul/task.git/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>

View File

@@ -1 +1 @@
<pkmkdoc spec="1.12"><properties><title>Task 1.4.2</title><build>/Users/paul/Documents/task-1.4.2.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.task142.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="none" bg-align="topleft"><locale lang="en"><resource mod="true" type="license">/Users/paul/task.git/binary/COPYING.txt</resource><resource mod="true" type="readme">/Users/paul/task.git/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.4.3</title><build>/Users/paul/Desktop/task-1.4.3.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.task143.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="none" bg-align="topleft"><locale lang="en"><resource mod="true" type="license">/Users/paul/task.git/binary/COPYING.txt</resource><resource mod="true" type="readme">/Users/paul/task.git/binary/README.txt</resource></locale></resources><flags/><item type="file">01task.xml</item><mod>properties.title</mod><mod>properties.systemDomain</mod><mod>properties.anywhereDomain</mod></pkmkdoc>