From 2b71317e09e8a0d11acccad21d2b5b3ba15817a4 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 10 Jan 2010 01:32:28 -0500 Subject: [PATCH 01/27] Build Error - Fixed build error on Ubuntu caused by using a Darwin-specific struct member. --- src/Path.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Path.cpp b/src/Path.cpp index a6551e423..b74c4d8f0 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -187,7 +187,7 @@ std::vector Path::glob (const std::string& pattern) glob_t g; if (!::glob (pattern.c_str (), GLOB_ERR | GLOB_BRACE | GLOB_TILDE, NULL, &g)) - for (int i = 0; i < g.gl_matchc; ++i) + for (int i = 0; i < (int) g.gl_pathc; ++i) results.push_back (g.gl_pathv[i]); globfree (&g); From 875c5c1880aecd48fe835d97ada0262ae17d6219 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 10 Jan 2010 01:34:16 -0500 Subject: [PATCH 02/27] Build Error - Fixed missing include. --- src/Directory.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Directory.cpp b/src/Directory.cpp index 2f7599996..068eb973f 100644 --- a/src/Directory.cpp +++ b/src/Directory.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "Directory.h" //////////////////////////////////////////////////////////////////////////////// From e92fb9287aede285f98ac56c552985292f59d31c Mon Sep 17 00:00:00 2001 From: Federico Hernandez Date: Sun, 10 Jan 2010 21:42:34 +0100 Subject: [PATCH 03/27] Man pages - Bumped rest of man pages to same date as FAQ man page. - Added name section to FAQ man page. --- doc/man/task-faq.5 | 8 ++++++++ doc/man/task-tutorial.5 | 2 +- doc/man/task.1 | 2 +- doc/man/taskrc.5 | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/doc/man/task-faq.5 b/doc/man/task-faq.5 index 0092b7997..ee770e3ac 100644 --- a/doc/man/task-faq.5 +++ b/doc/man/task-faq.5 @@ -1,5 +1,13 @@ .TH task-faq 5 2010-01-03 "task 1.9.0" "User Manuals" +.SH NAME +task-faq \- A FAQ for the task(1) command line todo manager. + +.SH DESCRIPTION +Task is a command line TODO list manager. It maintains a list of tasks that you +want to do, allowing you to add/remove, and otherwise manipulate them. Task +has a rich list of commands that allow you to do various things with it. + .SH WELCOME Welcome to the task FAQ. If you have would like to see a question answered here, please send us a note at . diff --git a/doc/man/task-tutorial.5 b/doc/man/task-tutorial.5 index b4aa2754c..f3a12ac8a 100644 --- a/doc/man/task-tutorial.5 +++ b/doc/man/task-tutorial.5 @@ -1,4 +1,4 @@ -.TH task-tutorial 5 2009-09-07 "task 1.9.0" "User Manuals" +.TH task-tutorial 5 2010-01-03 "task 1.9.0" "User Manuals" .SH NAME task-tutorial \- A tutorial for the task(1) command line todo manager. diff --git a/doc/man/task.1 b/doc/man/task.1 index 0749bb227..ef639bd5c 100644 --- a/doc/man/task.1 +++ b/doc/man/task.1 @@ -1,4 +1,4 @@ -.TH task 1 2009-09-07 "task 1.9.0" "User Manuals" +.TH task 1 2010-01-03 "task 1.9.0" "User Manuals" .SH NAME task \- A command line todo manager. diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index b38462dec..b7a1db9cf 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -1,4 +1,4 @@ -.TH taskrc 5 2009-09-07 "task 1.9.0" "User Manuals" +.TH taskrc 5 2010-01-03 "task 1.9.0" "User Manuals" .SH NAME taskrc \- Configuration file for the task(1) command From 0b5a105b9b70eaac2a864d82f2fc7b24f5ae19ed Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 00:58:55 -0500 Subject: [PATCH 04/27] Build - Included the new scripts/vim/syntax/taskrc.vim file in the distribution (thanks to John Florian). --- Makefile.am | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile.am b/Makefile.am index 79858c98f..d6ae8cf6f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,5 +16,7 @@ nobase_dist_zshscripts_DATA = scripts/zsh/_task vimscriptsdir = $(docdir) nobase_dist_vimscripts_DATA = scripts/vim/README scripts/vim/ftdetect/task.vim scripts/vim/syntax/taskdata.vim scripts/vim/syntax/taskedit.vim +nobase_dist_vimscripts_DATA = scripts/vim/README scripts/vim/ftdetect/task.vim scripts/vim/syntax/taskdata.vim scripts/vim/syntax/taskedit.vim scripts/vim/syntax/taskrc.vim + i18ndir = $(docdir) nobase_dist_i18n_DATA = i18n/strings.de-DE i18n/strings.en-US i18n/strings.es-ES i18n/strings.fr-FR i18n/strings.nl-NL i18n/strings.sv-SE i18n/tips.de-DE i18n/tips.en-US i18n/tips.sv-SE From c73376cb2ffa1bae41adbcd95dc5fd2950c1de0f Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 01:06:29 -0500 Subject: [PATCH 05/27] Build - Removed obsolete files that will be replaced soon by the new OSX packaging currently on the 1.8.5 branch. --- package-config/osx/binary/COPYING.txt | 281 -------------------------- package-config/osx/binary/README.txt | 24 --- 2 files changed, 305 deletions(-) delete mode 100644 package-config/osx/binary/COPYING.txt delete mode 100644 package-config/osx/binary/README.txt diff --git a/package-config/osx/binary/COPYING.txt b/package-config/osx/binary/COPYING.txt deleted file mode 100644 index 627d3eb39..000000000 --- a/package-config/osx/binary/COPYING.txt +++ /dev/null @@ -1,281 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - diff --git a/package-config/osx/binary/README.txt b/package-config/osx/binary/README.txt deleted file mode 100644 index de698bba5..000000000 --- a/package-config/osx/binary/README.txt +++ /dev/null @@ -1,24 +0,0 @@ - -Thank you for taking a look at task! - -Task is a GTD, todo list, task management, command line utility with a multitude -of features. It is a portable, well supported, very active project, and it is -Open Source. Task has binary distributions, online documentation, demonstration -movies, and you'll find all the details at the site: - - http://taskwarrior.org - -At the site you'll find a wiki, discussion forums, downloads, news and more. - - -Your contributions are especially welcome. Whether it comes in the form of -code patches, ideas, discussion, bug reports or just encouragement, your input -is needed. - -Please send your support questions and code patches to: - - support@taskwarrior.org - -Consider joining taskwarrior.org and participating in the future of task. - ---- From 5dbadda5129e54be6f6a01371b35111de88a79f4 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 01:16:59 -0500 Subject: [PATCH 06/27] Build - Removed duplicate entry. Oops. --- Makefile.am | 2 -- 1 file changed, 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index d6ae8cf6f..42096b174 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,8 +14,6 @@ zshscriptsdir = $(docdir) nobase_dist_zshscripts_DATA = scripts/zsh/_task vimscriptsdir = $(docdir) -nobase_dist_vimscripts_DATA = scripts/vim/README scripts/vim/ftdetect/task.vim scripts/vim/syntax/taskdata.vim scripts/vim/syntax/taskedit.vim - nobase_dist_vimscripts_DATA = scripts/vim/README scripts/vim/ftdetect/task.vim scripts/vim/syntax/taskdata.vim scripts/vim/syntax/taskedit.vim scripts/vim/syntax/taskrc.vim i18ndir = $(docdir) From c02cfd594c1a1648f76d446d371560ec07bae65c Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 01:18:55 -0500 Subject: [PATCH 07/27] Path Integration - Replaced calls to ::access --- src/Config.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 1366ce831..ce41a2f38 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -32,6 +32,7 @@ #include #include #include +#include "Path.h" #include "Config.h" #include "text.h" #include "util.h" @@ -99,16 +100,16 @@ bool Config::load (const std::string& file, int nest /* = 1 */) std::string::size_type include = line.find ("include"); // no i18n. if (include != std::string::npos) { - std::string included = expandPath ( trim ( line.substr (include + 7), " \t")); - if (isAbsolutePath (included)) + Path included (expandPath (trim (line.substr (include + 7), " \t"))); + if (isAbsolutePath (included.data)) { - if (!access (included.c_str (), F_OK | R_OK)) - this->load (included, nest + 1); + if (included.readable ()) + this->load (included.data, nest + 1); else - throw std::string ("Could not read include file '") + included + "'"; + throw std::string ("Could not read include file '") + included.data + "'"; } else - throw std::string ("Can only include files with absolute paths, not '") + included + "'"; + throw std::string ("Can only include files with absolute paths, not '") + included.data + "'"; } else throw std::string ("Malformed entry in ") + file + ": '" + line + "'"; @@ -293,7 +294,8 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data) //////////////////////////////////////////////////////////////////////////////// void Config::createDefaultData (const std::string& data) { - if (access (data.c_str (), F_OK)) + Path p (data); + if (! p.exists ()) mkdir (data.c_str (), S_IRWXU); } From e1f3f2355ade892ea678d605b5c848a431f41125 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 01:30:59 -0500 Subject: [PATCH 08/27] Enhancement - Added Path::is_absolute, and corresponding unit tests. - Replaced expandPath and isAbsolutePath call in Config.cpp. --- src/Config.cpp | 4 ++-- src/Path.cpp | 9 +++++++++ src/Path.h | 1 + src/tests/path.t.cpp | 9 ++++++++- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index ce41a2f38..d08701c3f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -100,8 +100,8 @@ bool Config::load (const std::string& file, int nest /* = 1 */) std::string::size_type include = line.find ("include"); // no i18n. if (include != std::string::npos) { - Path included (expandPath (trim (line.substr (include + 7), " \t"))); - if (isAbsolutePath (included.data)) + Path included (trim (line.substr (include + 7), " \t")); + if (included.is_absolute ()) { if (included.readable ()) this->load (included.data, nest + 1); diff --git a/src/Path.cpp b/src/Path.cpp index b74c4d8f0..8bcd41342 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -121,6 +121,15 @@ bool Path::is_directory () const return false; } +//////////////////////////////////////////////////////////////////////////////// +bool Path::is_absolute () const +{ + if (data.length () && data.substr (0, 1) == "/") + return true; + + return false; +} + //////////////////////////////////////////////////////////////////////////////// bool Path::readable () const { diff --git a/src/Path.h b/src/Path.h index 9dc2ad440..5f1ba2c1e 100644 --- a/src/Path.h +++ b/src/Path.h @@ -44,6 +44,7 @@ public: std::string extension () const; bool exists () const; bool is_directory () const; + bool is_absolute () const; bool readable () const; bool writable () const; bool executable () const; diff --git a/src/tests/path.t.cpp b/src/tests/path.t.cpp index 4fc679a1e..aaaed861b 100644 --- a/src/tests/path.t.cpp +++ b/src/tests/path.t.cpp @@ -33,7 +33,7 @@ Context context; int main (int argc, char** argv) { - UnitTest t (26); + UnitTest t (31); // Path (); Path p0; @@ -101,6 +101,13 @@ int main (int argc, char** argv) t.ok (out.size () == 1, "/[s-u]mp -> 1 result"); t.is (out[0], "/tmp", "/[s-u]mp -> /tmp"); + // bool is_absolute () const; + t.notok (p0.is_absolute (), "'' !is_absolute"); + t.notok (p1.is_absolute (), "foo !is_absolute"); + t.ok (p2.is_absolute (), "~ is_absolute (after expansion)"); + t.ok (p3.is_absolute (), "/tmp is_absolute"); + t.ok (p4.is_absolute (), "/a/b/c/file.ext is_absolute"); + return 0; } From 585020ef971a5b06226c831bf948d375eca0f7d2 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Tue, 12 Jan 2010 08:16:06 -0500 Subject: [PATCH 09/27] Documentation - Added a few questions to the FAQ. --- doc/man/task-faq.5 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/doc/man/task-faq.5 b/doc/man/task-faq.5 index ee770e3ac..50ed9cdca 100644 --- a/doc/man/task-faq.5 +++ b/doc/man/task-faq.5 @@ -13,20 +13,20 @@ Welcome to the task FAQ. If you have would like to see a question answered here, please send us a note at . .TP -.B Where does task store the data? +.B Q: Where does task store the data? By default, task creates a .taskrc file in your home directory and populates it with defaults. Task also creates a .task directory in your home directory and puts data files there. .TP -.B Can I edit that data? +.B Q: Can I edit that data? Of course you can. It is a simple text file, and looks somewhat like the JSON format, and if you are careful not to break the format, there is no reason not to edit it. But task provides a rich command set to do that manipulation for you, so it is probably best to leave those files alone. .TP -.B How do I restore my .taskrc file to defaults? +.B Q: How do I restore my .taskrc file to defaults? If you delete (or rename) your .taskrc file, task will offer to create a default one for you. Another way to do this is with the command: @@ -36,14 +36,14 @@ Task will create 'new-file' if it doesn't already exist. Note that this is a good way to learn about new configuration settings, if your .taskrc file was created by an older version of task. +.TP +.B Q: Can I share my tasks between different machines? +.TP +.B Q: The undo.data file gets very large - do I need it? - - - - - - +.TP +.B Q: How do I know whether my terminal support 256 colors? .SH "CREDITS & COPYRIGHTS" task was written by P. Beckingham . From 660d0cca3effac8581de478e67e3b32759d69d36 Mon Sep 17 00:00:00 2001 From: Federico Hernandez Date: Tue, 12 Jan 2010 23:12:22 +0100 Subject: [PATCH 10/27] Report date format - added new reportdateformat to extend the formatting of due dates in the reports and "task info" - added new conversion sequences a, A, b, B and Y to be used with reportdateformat --- ChangeLog | 3 + NEWS | 1 + doc/man/taskrc.5 | 50 +++++++++++---- src/Config.cpp | 1 + src/Date.cpp | 138 ++++++++++++++++++++++++++++++++++------- src/Date.h | 1 + src/Table.cpp | 43 +++++++++++++ src/Table.h | 2 + src/command.cpp | 6 +- src/custom.cpp | 21 +++++-- src/main.h | 2 +- src/report.cpp | 12 ++-- src/tests/date.t.cpp | 17 ++++- src/tests/dateformat.t | 24 ++++++- 14 files changed, 269 insertions(+), 52 deletions(-) diff --git a/ChangeLog b/ChangeLog index deae5c454..3ba115dda 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,9 @@ 1.9.0 () + Added feature #292 that permits alternate line coloration in reports (thanks to Richard Querin). + + Added feature #254 (#295) which gives task a second date format to be + used in the reports with more conversion sequences like weekday name + or weeknumber. The date format is set with variable "reportdateformat". + Added feature #307 that provides vim with syntax highlighting for .taskrc. + Added feature #336 which gives task a 'prepend' command for symmetry with the 'append' command. diff --git a/NEWS b/NEWS index d9c6f8212..25e266810 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ New Features in task 1.9 - Supports nested .taskrc files with the new "include" statement" - New columns that include the time as well as date - New attribute modifiers + - New date format for reports - Improved .taskrc validation - Improved calendar report with custom coloring and optional task details output diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index b7a1db9cf..4e5f1bc59 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -148,33 +148,59 @@ tag names you have used, or just the ones used in active tasks. .TP .B dateformat=m/d/Y -This is a string of characters that define how task formats dates. The default value is: m/d/Y. -The string should contain the characters +.TP +.B reportdateformat=m/d/Y +This is a string of characters that define how task formats dates. If +.B reportdateformat +is set it will be used for the due date in the output of reports and "task info". + +The default value is: m/d/Y. The string should contain the characters .RS -m minimal-digit month, for example 1 or 12 +m minimal-digit month, for example 1 or 12 .br -d minimal-digit day, for example 1 or 30 +d minimal-digit day, for example 1 or 30 .br -y two-digit year, for example 09 +y two-digit year, for example 09 .br -D two-digit day, for example 01 or 30 +D two-digit day, for example 01 or 30 .br -M two-digit month, for example 01 or 12 +M two-digit month, for example 01 or 12 .br -Y four-digit year, for example 2009 +Y four-digit year, for example 2009 +.br +a short name of weekday, for example Mon or Wed +.br +A long name of weekday, for example Monday or Wednesday +.br +b short name of month, for example Jan or Aug +.br +B long name of month, for example January or August +.br +V weeknumber, for example 03 or 37 .RE The string may also contain other characters to act as spacers, or formatting. Examples for other -variable values: +values of dateformat: .RS .br -d/m/Y would output 24/7/2009 +d/m/Y would use for input and output 24/7/2009 .br -YMD would output 20090724 +yMD would use for input and output 090724 .br -m-d-y would output 07-24-09 +M-D-Y would use for input and output 07-24-2009 +.RE + +Examples for other values of reportdateformat: + +.RS +.br +a D b Y (V) would do an output as "Fri 24 Jul 2009 (30)" +.br +A, B D, Y would do an output as "Friday, July 24, 2009" +.br +vV a Y-M-D would do an output as "v30 Fri 2009-07.24" .RE .TP diff --git a/src/Config.cpp b/src/Config.cpp index 1366ce831..c5d97c179 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -149,6 +149,7 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data) << "\n" << "# Dates\n" << "dateformat=m/d/Y # Preferred input and display date format\n" + << "#reportdateformat=m/d/Y # Preferred input and display date format\n" << "weekstart=Sunday # Sunday or Monday only\n" << "displayweeknumber=yes # Show week numbers on calendar\n" << "due=7 # Task is considered due in 7 days\n" diff --git a/src/Date.cpp b/src/Date.cpp index 7971f075e..65bb47dbb 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -33,6 +33,9 @@ #include "Date.h" #include "text.h" #include "util.h" +#include "Context.h" + +extern Context context; //////////////////////////////////////////////////////////////////////////////// // Defaults to "now". @@ -85,7 +88,7 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) if (i >= mdy.length () || ! isdigit (mdy[i])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (m)."; } if (i + 1 < mdy.length () && @@ -106,7 +109,7 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) if (i >= mdy.length () || ! isdigit (mdy[i])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (d)."; } if (i + 1 < mdy.length () && @@ -125,11 +128,11 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) // Double digit. case 'y': - if (i + 1 >= mdy.length () || + if (i + 1 >= mdy.length () || ! isdigit (mdy[i + 0]) || ! isdigit (mdy[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (y)."; } year = atoi (mdy.substr (i, 2).c_str ()) + 2000; @@ -141,7 +144,7 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) ! isdigit (mdy[i + 0]) || ! isdigit (mdy[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (M)."; } month = atoi (mdy.substr (i, 2).c_str ()); @@ -153,13 +156,24 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) ! isdigit (mdy[i + 0]) || ! isdigit (mdy[i + 1])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (D)."; } day = atoi (mdy.substr (i, 2).c_str ()); i += 2; break; + case 'V': + if (i + 1 >= mdy.length () || + ! isdigit (mdy[i + 0]) || + ! isdigit (mdy[i + 1])) + { + throw std::string ("\"") + mdy + "\" is not a valid date (V)."; + } + + i += 2; + break; + // Quadruple digit. case 'Y': if (i + 3 >= mdy.length () || @@ -168,18 +182,70 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) ! isdigit (mdy[i + 2]) || ! isdigit (mdy[i + 3])) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (Y)."; } year = atoi (mdy.substr (i, 4).c_str ()); i += 4; break; + // Short names with 3 characters + case 'a': + if (i + 2 >= mdy.length () || + isdigit (mdy[i + 0]) || + isdigit (mdy[i + 1]) || + isdigit (mdy[i + 2])) + { + throw std::string ("\"") + mdy + "\" is not a valid date (a)."; + } + + i += 3; + break; + + case 'b': + if (i + 2 >= mdy.length () || + isdigit (mdy[i + 0]) || + isdigit (mdy[i + 1]) || + isdigit (mdy[i + 2])) + { + throw std::string ("\"") + mdy + "\" is not a valid date (b)."; + } + + month = Date::monthOfYear (mdy.substr (i, 3).c_str()); + i += 3; + break; + + // Long names + case 'A': + if (i + 2 >= mdy.length () || + isdigit (mdy[i + 0]) || + isdigit (mdy[i + 1]) || + isdigit (mdy[i + 2])) + { + throw std::string ("\"") + mdy + "\" is not a valid date (A)."; + } + + i += Date::dayName( Date::dayOfWeek (mdy.substr (i, 3).c_str()) ).size(); + break; + + case 'B': + if (i + 2 >= mdy.length () || + isdigit (mdy[i + 0]) || + isdigit (mdy[i + 1]) || + isdigit (mdy[i + 2])) + { + throw std::string ("\"") + mdy + "\" is not a valid date (B)."; + } + + month = Date::monthOfYear (mdy.substr (i, 3).c_str()); + i += Date::monthName(month).size(); + break; + default: if (i >= mdy.length () || mdy[i] != format[f]) { - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (DEFAULT)."; } ++i; break; @@ -190,7 +256,7 @@ Date::Date (const std::string& mdy, const std::string& format /* = "m/d/Y" */) throw std::string ("\"") + mdy + "\" is not a valid date in " + format + " format."; if (!valid (month, day, year)) - throw std::string ("\"") + mdy + "\" is not a valid date."; + throw std::string ("\"") + mdy + "\" is not a valid date (VALID)."; // Duplicate Date::Date (const int, const int, const int); struct tm t = {0}; @@ -256,13 +322,18 @@ const std::string Date::toString (const std::string& format /*= "m/d/Y" */) cons char c = localFormat[i]; switch (c) { - case 'm': sprintf (buffer, "%d", this->month ()); break; - case 'M': sprintf (buffer, "%02d", this->month ()); break; - case 'd': sprintf (buffer, "%d", this->day ()); break; - case 'D': sprintf (buffer, "%02d", this->day ()); break; - case 'y': sprintf (buffer, "%02d", this->year () % 100); break; - case 'Y': sprintf (buffer, "%d", this->year ()); break; - default: sprintf (buffer, "%c", c); break; + case 'm': sprintf (buffer, "%d", this->month ()); break; + case 'M': sprintf (buffer, "%02d", this->month ()); break; + case 'd': sprintf (buffer, "%d", this->day ()); break; + case 'D': sprintf (buffer, "%02d", this->day ()); break; + case 'y': sprintf (buffer, "%02d", this->year () % 100); break; + case 'Y': sprintf (buffer, "%d", this->year ()); break; + case 'a': sprintf (buffer, "%.3s", Date::dayName(dayOfWeek()).c_str() ); break; + case 'A': sprintf (buffer, "%s", Date::dayName(dayOfWeek()).c_str() ); break; + case 'b': sprintf (buffer, "%.3s", Date::monthName(month()).c_str() ); break; + case 'B': sprintf (buffer, "%.9s", Date::monthName(month()).c_str() ); break; + case 'V': sprintf (buffer, "%02d", Date::weekOfYear(Date::dayOfWeek(context.config.get ("weekstart", "Sunday")))); break; + default: sprintf (buffer, "%c", c); break; } formatted += buffer; @@ -437,13 +508,34 @@ int Date::dayOfWeek (const std::string& input) { std::string in = lowerCase (input); - if (in == "sunday") return 0; - if (in == "monday") return 1; - if (in == "tuesday") return 2; - if (in == "wednesday") return 3; - if (in == "thursday") return 4; - if (in == "friday") return 5; - if (in == "saturday") return 6; + if (in == "sunday" || in == "sun") return 0; + if (in == "monday" || in == "mon") return 1; + if (in == "tuesday" || in == "tue") return 2; + if (in == "wednesday" || in == "wed") return 3; + if (in == "thursday" || in == "thu") return 4; + if (in == "friday" || in == "fri") return 5; + if (in == "saturday" || in == "sat") return 6; + + return -1; +} + +//////////////////////////////////////////////////////////////////////////////// +int Date::monthOfYear (const std::string& input) +{ + std::string in = lowerCase (input); + + if (in == "january" || in == "jan") return 1; + if (in == "february" || in == "feb") return 2; + if (in == "march" || in == "mar") return 3; + if (in == "april" || in == "apr") return 4; + if (in == "may" || in == "may") return 5; + if (in == "june" || in == "jun") return 6; + if (in == "july" || in == "jul") return 7; + if (in == "august" || in == "aug") return 8; + if (in == "september" || in == "sep") return 9; + if (in == "october" || in == "oct") return 10; + if (in == "november" || in == "nov") return 11; + if (in == "december" || in == "dec") return 12; return -1; } diff --git a/src/Date.h b/src/Date.h index 68385515f..7f7f22914 100644 --- a/src/Date.h +++ b/src/Date.h @@ -58,6 +58,7 @@ public: static std::string dayName (int); static int weekOfYear (const std::string&); static int dayOfWeek (const std::string&); + static int monthOfYear (const std::string&); int month () const; int day () const; diff --git a/src/Table.cpp b/src/Table.cpp index 06061d114..fd81db490 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -52,6 +52,9 @@ #include "Timer.h" #include "text.h" #include "util.h" +#include "Context.h" + +extern Context context; //////////////////////////////////////////////////////////////////////////////// Table::Table () @@ -868,6 +871,46 @@ void Table::sort (std::vector & order) } break; + case ascendingDueDate: + { + if ((std::string)*left != "" && (std::string)*right == "") + break; + + else if ((std::string)*left == "" && (std::string)*right != "") + SWAP + + else + { + Date dl ((std::string)*left, context.config.get("reportdateformat", + context.config.get("dateformat","m/d/Y"))); + Date dr ((std::string)*right, context.config.get("reportdateformat", + context.config.get("dateformat","m/d/Y"))); + if (dl > dr) + SWAP + } + } + break; + + case descendingDueDate: + { + if ((std::string)*left != "" && (std::string)*right == "") + break; + + else if ((std::string)*left == "" && (std::string)*right != "") + SWAP + + else + { + Date dl ((std::string)*left, context.config.get("reportdateformat", + context.config.get("dateformat","m/d/Y"))); + Date dr ((std::string)*right, context.config.get("reportdateformat", + context.config.get("dateformat","m/d/Y"))); + if (dl < dr) + SWAP + } + } + break; + case ascendingPriority: if (((std::string)*left == "" && (std::string)*right != "") || ((std::string)*left == "M" && (std::string)*right == "L") || diff --git a/src/Table.h b/src/Table.h index a280854af..dfe18d04e 100644 --- a/src/Table.h +++ b/src/Table.h @@ -41,11 +41,13 @@ public: ascendingCharacter, ascendingPriority, ascendingDate, + ascendingDueDate, ascendingPeriod, descendingNumeric, descendingCharacter, descendingPriority, descendingDate, + descendingDueDate, descendingPeriod}; enum sizing {minimum = -1, flexible = 0}; diff --git a/src/command.cpp b/src/command.cpp index 3326f9d5a..2fb9a5822 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -555,9 +555,9 @@ int handleConfig (std::string &outs) "color.due color.overdue color.pri.H color.pri.L color.pri.M color.pri.none " "color.recurring color.tagged color.footnote color.header color.debug color.alternate " "color.calendar.today color.calendar.due color.calendar.overdue color.calendar.weekend " - "confirmation curses data.location dateformat debug default.command default.priority " - "default.project defaultwidth due locale displayweeknumber echo.command " - "locking monthsperline nag next project shadow.command shadow.file " + "confirmation curses data.location dateformat reportdateformat debug default.command " + "default.priority default.project defaultwidth due locale displayweeknumber " + "echo.command locking monthsperline nag next project shadow.command shadow.file " "shadow.notify weekstart editor import.synonym.id import.synonym.uuid " "complete.all.projects complete.all.tags " #ifdef FEATURE_SHELL diff --git a/src/custom.cpp b/src/custom.cpp index 89a0bc66b..a4da90208 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -356,12 +356,15 @@ int runCustomReport ( { table.addColumn (columnLabels[*col] != "" ? columnLabels[*col] : "Due"); table.setColumnWidth (columnCount, Table::minimum); - table.setColumnJustification (columnCount, Table::right); + table.setColumnJustification (columnCount, Table::left); int row = 0; std::string due; foreach (task, tasks) - table.addCell (row++, columnCount, getDueDate (*task)); + table.addCell (row++, columnCount, + getDueDate (*task, + context.config.get ("reportdateformat", + context.config.get ("dateformat", "m/d/Y")))); dueColumn = columnCount; } @@ -550,13 +553,19 @@ int runCustomReport ( Table::ascendingPriority : Table::descendingPriority)); - else if (column == "entry" || column == "start" || column == "due" || - column == "wait" || column == "until" || column == "end") + else if (column == "entry" || column == "start" || column == "wait" || + column == "until" || column == "end") table.sortOn (columnIndex[column], (direction == '+' ? Table::ascendingDate : Table::descendingDate)); + else if (column == "due") + table.sortOn (columnIndex[column], + (direction == '+' ? + Table::ascendingDueDate : + Table::descendingDueDate)); + else if (column == "recur") table.sortOn (columnIndex[column], (direction == '+' ? @@ -571,10 +580,10 @@ int runCustomReport ( } // Now auto colorize all rows. - Color color_due (context.config.get ("color.due", "yellow")); + std::string due; + Color color_due (context.config.get ("color.due", "green")); Color color_overdue (context.config.get ("color.overdue", "red")); - std::string due; bool imminent; bool overdue; for (unsigned int row = 0; row < tasks.size (); ++row) diff --git a/src/main.h b/src/main.h index 9e1f3f53a..a934d8c53 100644 --- a/src/main.h +++ b/src/main.h @@ -103,7 +103,7 @@ int handleReportCalendar (std::string &); int handleReportStats (std::string &); int handleReportTimesheet (std::string &); std::string getFullDescription (Task&); -std::string getDueDate (Task&); +std::string getDueDate (Task&, const std::string&); // custom.cpp int handleCustomReport (const std::string&, std::string &); diff --git a/src/report.cpp b/src/report.cpp index edad19730..06043674e 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -412,7 +412,7 @@ int handleInfo (std::string &outs) table.addCell (row, 0, "Due"); Date dt (atoi (task->get ("due").c_str ())); - std::string due = getDueDate (*task); + std::string due = getDueDate (*task, context.config.get("reportdateformat", context.config.get("dateformat","m/d/Y"))); table.addCell (row, 1, due); overdue = (dt < now) ? true : false; @@ -1218,7 +1218,7 @@ int handleReportTimesheet (std::string &outs) { int row = completed.addRow (); completed.addCell (row, 1, task->get ("project")); - completed.addCell (row, 2, getDueDate (*task)); + completed.addCell (row, 2, getDueDate (*task,context.config.get("dateformat","m/d/Y"))); completed.addCell (row, 3, getFullDescription (*task)); if (color) @@ -1274,7 +1274,7 @@ int handleReportTimesheet (std::string &outs) { int row = started.addRow (); started.addCell (row, 1, task->get ("project")); - started.addCell (row, 2, getDueDate (*task)); + started.addCell (row, 2, getDueDate (*task,context.config.get("dateformat","m/d/Y"))); started.addCell (row, 3, getFullDescription (*task)); if (color) @@ -2124,13 +2124,15 @@ std::string getFullDescription (Task& task) } /////////////////////////////////////////////////////////////////////////////// -std::string getDueDate (Task& task) +std::string getDueDate (Task& task, const std::string& format) { std::string due = task.get ("due"); if (due.length ()) { Date d (atoi (due.c_str ())); - due = d.toString (context.config.get ("dateformat", "m/d/Y")); + due = d.toString (format); + //due = d.toString (context.config.get ("dateformat", "m/d/Y")); + //due = d.toString (context.config.get ("reportdateformat", context.config.get ("dateformat", "m/d/Y"))); } return due; diff --git a/src/tests/date.t.cpp b/src/tests/date.t.cpp index 5714c19cc..391dcb8c1 100644 --- a/src/tests/date.t.cpp +++ b/src/tests/date.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (102); + UnitTest t (111); try { @@ -180,6 +180,21 @@ int main (int argc, char** argv) t.is (fromString7.day (), 1, "ctor (std::string) -> d"); t.is (fromString7.year (), 2008, "ctor (std::string) -> y"); + Date fromString8 ("Tue 01 Jan 2008 (01)", "a D b Y (V)"); + t.is (fromString8.month (), 1, "ctor (std::string) -> m"); + t.is (fromString8.day (), 1, "ctor (std::string) -> d"); + t.is (fromString8.year (), 2008, "ctor (std::string) -> y"); + + Date fromString9 ("Tuesday, January 1, 2008", "A, B d, Y"); + t.is (fromString9.month (), 1, "ctor (std::string) -> m"); + t.is (fromString9.day (), 1, "ctor (std::string) -> d"); + t.is (fromString9.year (), 2008, "ctor (std::string) -> y"); + + Date fromString10 ("v01 Tue 2008-01-01", "vV a Y-M-D"); + t.is (fromString10.month (), 1, "ctor (std::string) -> m"); + t.is (fromString10.day (), 1, "ctor (std::string) -> d"); + t.is (fromString10.year (), 2008, "ctor (std::string) -> y"); + // Relative dates. Date r1 ("today"); t.ok (r1.sameDay (now), "today = now"); diff --git a/src/tests/dateformat.t b/src/tests/dateformat.t index 62df90a4b..e6d8d9066 100755 --- a/src/tests/dateformat.t +++ b/src/tests/dateformat.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 9; +use Test::More tests => 14; # Create the rc file. if (open my $fh, '>', 'date1.rc') @@ -47,6 +47,16 @@ if (open my $fh, '>', 'date2.rc') ok (-r 'date2.rc', 'Created date2.rc'); } +if (open my $fh, '>', 'date3.rc') +{ + print $fh "data.location=.\n", + "dateformat=m/d/y\n", + "weekstart=Monday\n", + "reportdateformat=A D B Y (vV)\n"; + close $fh; + ok (-r 'date3.rc', 'Created date3.rc'); +} + qx{../task rc:date1.rc add foo due:20091231}; my $output = qx{../task rc:date1.rc info 1}; like ($output, qr/\b20091231\b/, 'date format YMD parsed'); @@ -58,6 +68,15 @@ qx{../task rc:date2.rc add foo due:12/1/09}; $output = qx{../task rc:date2.rc info 1}; like ($output, qr/\b12\/1\/09\b/, 'date format m/d/y parsed'); +unlink 'pending.data'; +ok (!-r 'pending.data', 'Removed pending.data'); + +qx{../task rc:date3.rc add foo due:4/8/10}; +$output = qx{../task rc:date3.rc list}; +like ($output, qr/Thursday 08 April 2010 \(v14\)/, 'date format A D B Y (vV) parsed'); +$output = qx{../task rc:date3.rc rc.reportdateformat:"D b Y - a" list}; +like ($output, qr/08 Apr 2010 - Thu/, 'date format D b Y - a parsed'); + # Cleanup. unlink 'pending.data'; ok (!-r 'pending.data', 'Removed pending.data'); @@ -71,5 +90,8 @@ ok (!-r 'date1.rc', 'Removed date1.rc'); unlink 'date2.rc'; ok (!-r 'date2.rc', 'Removed date2.rc'); +unlink 'date3.rc'; +ok (!-r 'date3.rc', 'Removed date3.rc'); + exit 0; From 3aae7b180b858aa3f71430a541a9b3e5dd1fee6c Mon Sep 17 00:00:00 2001 From: Federico Hernandez Date: Thu, 14 Jan 2010 00:03:52 +0100 Subject: [PATCH 11/27] Feature - #283 verbosity of annotations - the configuration variable annotation.details now controls the verbosity of the output of annotations. --- ChangeLog | 6 ++-- doc/man/taskrc.5 | 4 +++ src/Config.cpp | 1 + src/command.cpp | 4 +-- src/report.cpp | 32 ++++++++++++++++---- src/tests/annotate.t | 71 +++++++++++++++++++++++++++++++++++++------- 6 files changed, 97 insertions(+), 21 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3ba115dda..741de875f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,11 +2,13 @@ ------ current release --------------------------- 1.9.0 () - + Added feature #292 that permits alternate line coloration in reports - (thanks to Richard Querin). + + Added feature #283 that makes it possible to control the verbosity + of the output of annotations. + Added feature #254 (#295) which gives task a second date format to be used in the reports with more conversion sequences like weekday name or weeknumber. The date format is set with variable "reportdateformat". + + Added feature #292 that permits alternate line coloration in reports + (thanks to Richard Querin). + Added feature #307 that provides vim with syntax highlighting for .taskrc. + Added feature #336 which gives task a 'prepend' command for symmetry with the 'append' command. diff --git a/doc/man/taskrc.5 b/doc/man/taskrc.5 index 4e5f1bc59..c3f705b1e 100644 --- a/doc/man/taskrc.5 +++ b/doc/man/taskrc.5 @@ -115,6 +115,10 @@ May be "yes" or "no", and determines whether task will ask for confirmation befo .B echo.command=yes 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 or delete commands. The default value is "yes". +.TP +.B annotation.details=2 +Controls the output of annotations in reports. Defaults to 2 - all annotations are displayed. Set to 1 only the last (youngest) annotation is displayed and if there are more than one present for a task a "+" sign is added to the description. Set to 0 the output of annotations is disabled and a "+" sign will be added if there are any annotations present. + .TP .B next=2 Is a number, defaulting to 2, which is the number of tasks for each project that are shown in the diff --git a/src/Config.cpp b/src/Config.cpp index 32d1a87b4..486be85af 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -144,6 +144,7 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data) << "# Miscellaneous\n" << "confirmation=yes # Confirmation on delete, big changes\n" << "echo.command=yes # Details on command just run\n" + << "annotation.details=2 # Level of verbosity for annotations in reports\n" << "next=2 # How many tasks per project in next report\n" << "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" << "nag=You have higher priority tasks. # Nag message to keep you honest\n" diff --git a/src/command.cpp b/src/command.cpp index 2fb9a5822..9907f9c53 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -551,8 +551,8 @@ int handleConfig (std::string &outs) // These are the regular configuration variables. // Note that there is a leading and trailing space, to make searching easier. std::string recognized = - " blanklines bulk calendar.details calendar.details.report color color.active " - "color.due color.overdue color.pri.H color.pri.L color.pri.M color.pri.none " + " annotation.details blanklines bulk calendar.details calendar.details.report color " + "color.active color.due color.overdue color.pri.H color.pri.L color.pri.M color.pri.none " "color.recurring color.tagged color.footnote color.header color.debug color.alternate " "color.calendar.today color.calendar.due color.calendar.overdue color.calendar.weekend " "confirmation curses data.location dateformat reportdateformat debug default.command " diff --git a/src/report.cpp b/src/report.cpp index 06043674e..1b87f2645 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -2113,12 +2113,32 @@ std::string getFullDescription (Task& task) std::vector annotations; task.getAnnotations (annotations); - foreach (anno, annotations) - { - Date dt (atoi (anno->name ().substr (11).c_str ())); - std::string when = dt.toString (context.config.get ("dateformat", "m/d/Y")); - desc += "\n" + when + " " + anno->value (); - } + + if (annotations.size () != 0) + switch (context.config.get("annotation.details",2)) + { + case 0: + desc = "+" + desc; + break; + case 1: + { + if (annotations.size () > 1) + desc = "+" + desc; + Att anno (annotations.back()); + Date dt (atoi (anno.name ().substr (11).c_str ())); + std::string when = dt.toString (context.config.get ("dateformat", "m/d/Y")); + desc += "\n" + when + " " + anno.value (); + } + break; + case 2: + foreach (anno, annotations) + { + Date dt (atoi (anno->name ().substr (11).c_str ())); + std::string when = dt.toString (context.config.get ("dateformat", "m/d/Y")); + desc += "\n" + when + " " + anno->value (); + } + break; + } return desc; } diff --git a/src/tests/annotate.t b/src/tests/annotate.t index 69f35412d..7d5eb259e 100755 --- a/src/tests/annotate.t +++ b/src/tests/annotate.t @@ -28,7 +28,7 @@ use strict; use warnings; -use Test::More tests => 9; +use Test::More tests => 37; # Create the rc file. if (open my $fh, '>', 'annotate.rc') @@ -43,27 +43,76 @@ if (open my $fh, '>', 'annotate.rc') ok (-r 'annotate.rc', 'Created annotate.rc'); } -# Add two tasks, annotate one twice. +# Add four tasks, annotate one three times, one twice, one just once and one none. 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}; +qx{../task rc:annotate.rc add three}; +qx{../task rc:annotate.rc add four}; +qx{../task rc:annotate.rc annotate 1 foo1}; +sleep 1; +qx{../task rc:annotate.rc annotate 1 foo2}; +sleep 1; +qx{../task rc:annotate.rc annotate 1 foo3}; +sleep 1; +qx{../task rc:annotate.rc annotate 2 bar1}; +sleep 1; +qx{../task rc:annotate.rc annotate 2 bar2}; +sleep 1; +qx{../task rc:annotate.rc annotate 3 baz1}; + my $output = qx{../task rc:annotate.rc rrr}; # ID Description # -- ------------------------------- # 1 one -# 3/24/2009 foo -# 3/24/2009 bar +# 3/24/2009 foo1 +# 3/24/2009 foo2 +# 3/24/2009 foo3 # 2 two +# 3/24/2009 bar1 +# 3/24/2009 bar2 +# 3 three +# 3/24/2009 baz1 +# 4 four # -# 2 tasks +# 4 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'); +like ($output, qr/3 three/, 'task 3'); +like ($output, qr/4 four/, 'task 4'); +like ($output, qr/one.+\d{1,2}\/\d{1,2}\/\d{4} foo1/ms, 'first annotation task 1'); +like ($output, qr/foo1.+\d{1,2}\/\d{1,2}\/\d{4} foo2/ms, 'second annotation task 1'); +like ($output, qr/foo2.+\d{1,2}\/\d{1,2}\/\d{4} foo3/ms, 'third annotation task 1'); +like ($output, qr/two.+\d{1,2}\/\d{1,2}\/\d{4} bar1/ms, 'first annotation task 2'); +like ($output, qr/bar1.+\d{1,2}\/\d{1,2}\/\d{4} bar2/ms, 'second annotation task 2'); +like ($output, qr/three.+\d{1,2}\/\d{1,2}\/\d{4} baz1/ms,'first annotation task 3'); +like ($output, qr/4 tasks/, 'count'); + +$output = qx{../task rc:annotate.rc rc.annotation.details:1 rrr}; +like ($output, qr/1 \+one/, 'task 1'); +like ($output, qr/2 \+two/, 'task 2'); +like ($output, qr/3 three/, 'task 3'); +like ($output, qr/4 four/, 'task 4'); +unlike ($output, qr/one.+\d{1,2}\/\d{1,2}\/\d{4} foo1/ms, 'first annotation task 1'); +unlike ($output, qr/foo1.+\d{1,2}\/\d{1,2}\/\d{4} foo2/ms, 'second annotation task 1'); +like ($output, qr/one.+\d{1,2}\/\d{1,2}\/\d{4} foo3/ms, 'third annotation task 1'); +unlike ($output, qr/two.+\d{1,2}\/\d{1,2}\/\d{4} bar1/ms, 'first annotation task 2'); +like ($output, qr/two.+\d{1,2}\/\d{1,2}\/\d{4} bar2/ms, 'second annotation task 2'); +like ($output, qr/three.+\d{1,2}\/\d{1,2}\/\d{4} baz1/ms, 'third annotation task 3'); +like ($output, qr/4 tasks/, 'count'); + +$output = qx{../task rc:annotate.rc rc.annotation.details:0 rrr}; +like ($output, qr/1 \+one/, 'task 1'); +like ($output, qr/2 \+two/, 'task 2'); +like ($output, qr/3 \+three/, 'task 3'); +like ($output, qr/4 four/, 'task 4'); +unlike ($output, qr/one.+\d{1,2}\/\d{1,2}\/\d{4} foo1/ms, 'first annotation task 1'); +unlike ($output, qr/foo1.+\d{1,2}\/\d{1,2}\/\d{4} foo2/ms, 'second annotation task 1'); +unlike ($output, qr/foo2.+\d{1,2}\/\d{1,2}\/\d{4} foo3/ms, 'third annotation task 1'); +unlike ($output, qr/two.+\d{1,2}\/\d{1,2}\/\d{4} bar1/ms, 'first annotation task 2'); +unlike ($output, qr/bar1.+\d{1,2}\/\d{1,2}\/\d{4} bar2/ms, 'second annotation task 2'); +unlike ($output, qr/three.+\d{1,2}\/\d{1,2}\/\d{4} baz1/ms, 'third annotation task 3'); +like ($output, qr/4 tasks/, 'count'); # Cleanup. unlink 'pending.data'; From f87b2ee636de5591dd07f71dc0c191694d120088 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Thu, 14 Jan 2010 23:43:00 -0500 Subject: [PATCH 12/27] Color Themes - Created the four basic themes, with no contents yet. TBD. --- doc/rc/16-dark.theme | 1 + doc/rc/16-light.theme | 1 + doc/rc/256-dark.theme | 1 + doc/rc/256-light.theme | 1 + 4 files changed, 4 insertions(+) create mode 100644 doc/rc/16-dark.theme create mode 100644 doc/rc/16-light.theme create mode 100644 doc/rc/256-dark.theme create mode 100644 doc/rc/256-light.theme diff --git a/doc/rc/16-dark.theme b/doc/rc/16-dark.theme new file mode 100644 index 000000000..99b48867b --- /dev/null +++ b/doc/rc/16-dark.theme @@ -0,0 +1 @@ +# Sample task 1.9 (or later) color theme diff --git a/doc/rc/16-light.theme b/doc/rc/16-light.theme new file mode 100644 index 000000000..99b48867b --- /dev/null +++ b/doc/rc/16-light.theme @@ -0,0 +1 @@ +# Sample task 1.9 (or later) color theme diff --git a/doc/rc/256-dark.theme b/doc/rc/256-dark.theme new file mode 100644 index 000000000..99b48867b --- /dev/null +++ b/doc/rc/256-dark.theme @@ -0,0 +1 @@ +# Sample task 1.9 (or later) color theme diff --git a/doc/rc/256-light.theme b/doc/rc/256-light.theme new file mode 100644 index 000000000..99b48867b --- /dev/null +++ b/doc/rc/256-light.theme @@ -0,0 +1 @@ +# Sample task 1.9 (or later) color theme From 0002376f2a91cf5cb98640d7a181b8e5a5e4fe1a Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Thu, 14 Jan 2010 23:44:58 -0500 Subject: [PATCH 13/27] Color Themes - Better names. --- doc/rc/{16-dark.theme => dark-16.theme} | 0 doc/rc/{16-light.theme => dark-256.theme} | 0 doc/rc/{256-dark.theme => light-16.theme} | 0 doc/rc/{256-light.theme => light-256.theme} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename doc/rc/{16-dark.theme => dark-16.theme} (100%) rename doc/rc/{16-light.theme => dark-256.theme} (100%) rename doc/rc/{256-dark.theme => light-16.theme} (100%) rename doc/rc/{256-light.theme => light-256.theme} (100%) diff --git a/doc/rc/16-dark.theme b/doc/rc/dark-16.theme similarity index 100% rename from doc/rc/16-dark.theme rename to doc/rc/dark-16.theme diff --git a/doc/rc/16-light.theme b/doc/rc/dark-256.theme similarity index 100% rename from doc/rc/16-light.theme rename to doc/rc/dark-256.theme diff --git a/doc/rc/256-dark.theme b/doc/rc/light-16.theme similarity index 100% rename from doc/rc/256-dark.theme rename to doc/rc/light-16.theme diff --git a/doc/rc/256-light.theme b/doc/rc/light-256.theme similarity index 100% rename from doc/rc/256-light.theme rename to doc/rc/light-256.theme From 0faf7fa8ee5ef1bb2a2bc10afa4c9ce5b037c51e Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 15 Jan 2010 02:50:46 -0500 Subject: [PATCH 14/27] Config - defaults - Config is now providing a default set of all configuration variables. - The default set is used to both initialize a Config object, and to create a sample .taskrc. --- ChangeLog | 4 +- src/Config.cpp | 597 ++++++++++++++++------------------- src/Config.h | 17 +- src/command.cpp | 1 - src/tests/add.t | 3 +- src/tests/annotate.t | 1 + src/tests/att.t.cpp | 4 +- src/tests/basic.t | 3 +- src/tests/config.duplicate.t | 59 ---- src/tests/delete.t | 1 + src/tests/sequence.t | 3 +- src/tests/shadow.t | 1 + 12 files changed, 298 insertions(+), 396 deletions(-) delete mode 100755 src/tests/config.duplicate.t diff --git a/ChangeLog b/ChangeLog index 741de875f..10f9b45c1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -24,8 +24,8 @@ + Added new 'config' command to display the configuration settings of task. As a consequence 'version' now only shows the version number and legal information. - + The 'config' command now complains about use of deprecated color names and - duplicate entries in .taskrc. + + The 'config' command now complains about use of deprecated color names in + your .taskrc file. + Task now supports nested .taskrc files using the "include /path" directive. + The 'entry', 'start' and 'end' columns now have equivalents that include the time, and are called 'entry_time', 'start_time', and 'end_time', for use in diff --git a/src/Config.cpp b/src/Config.cpp index 486be85af..f75691251 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -27,31 +27,229 @@ #include #include #include +#include #include #include #include #include #include #include "Path.h" +#include "File.h" #include "Config.h" #include "text.h" #include "util.h" //////////////////////////////////////////////////////////////////////////////// -// These are default (but overridable) reports. These entries are necessary -// because these three reports were converted from hard-coded reports to custom -// reports, and therefore need these config file entries. However, users are -// already used to seeing these five reports, but do not have these entries. -// The choice was a) make users edit their .taskrc files, b) write a .taskrc -// upgrade program to make the change, or c) this. +// This string is used in two ways: +// 1) It is used to create a new .taskrc file, by copying it directly to disk. +// 2) It is parsed and used as default values for all Config.get calls. +std::string Config::defaults = + "# Task program configuration file.\n" + "# For more documentation, see http://taskwarrior.org or try 'man task' and 'man taskrc'\n" + "\n" + "# Files\n" + "data.location=~/.task\n" + "locking=on # Use file-level locking\n" + "\n" + "# Terminal\n" + "curses=on # Use ncurses library to determine terminal width\n" + "defaultwidth=80 # Without ncurses, assumed width\n" + "#editor=vi # Preferred text editor\n" + "\n" + "# Miscellaneous\n" + "confirmation=yes # Confirmation on delete, big changes\n" + "echo.command=yes # Details on command just run\n" + "annotation.details=2 # Level of verbosity for annotations in reports\n" + "next=2 # How many tasks per project in next report\n" + "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" + "nag=You have higher priority tasks. # Nag message to keep you honest\n" + "\n" + "# Dates\n" + "dateformat=m/d/Y # Preferred input and display date format\n" + "#reportdateformat=m/d/Y # Preferred input and display date format\n" + "weekstart=Sunday # Sunday or Monday only\n" + "displayweeknumber=yes # Show week numbers on calendar\n" + "due=7 # Task is considered due in 7 days\n" + "#calendar.details=yes # Calendar shows information for tasks w/due dates\n" + "#calendar.details.report=list # Report to use when showing task information in cal\n" + "#monthsperline=3 # Number of calendar months on a line\n" + "\n" + "# Color controls.\n" + "color=on # Enable color\n" + "color.overdue=bold red # Color of overdue tasks\n" + "color.due=bold yellow # Color of due tasks\n" + "color.pri.H=bold # Color of priority:H tasks\n" + "#color.pri.M=on yellow # Color of priority:M tasks\n" + "#color.pri.L=on green # Color of priority:L tasks\n" + "#color.pri.none=white on blue # Color of priority: tasks\n" + "color.active=bold cyan # Color of active tasks\n" + "color.tagged=yellow # Color of tagged tasks\n" + "#color.tag.bug=yellow # Color of +bug tasks\n" + "#color.project.garden=on green # Color of project:garden tasks\n" + "#color.keyword.car=on blue # Color of description.contains:car tasks\n" + "#color.recurring=on red # Color of recur.any: tasks\n" + "#color.header=bold green # Color of header messages\n" + "#color.footnote=bold green # Color of footnote messages\n" + "#color.alternate=on rgb253 # Alternate color for line coloring\n" + "color.calendar.today=black on cyan # Color of today in calendar\n" + "color.calendar.due=black on green # Color of days with due tasks in calendar\n" + "color.calendar.overdue=black on red # Color of days with overdue tasks in calendar\n" + "color.calendar.weekend=black on white # Color of weekend days in calendar\n" + "#color.debug=magenta # Color of diagnostic output\n" + "color.pri.H=bold # Color of priority:H tasks\n" + "color.history.add=on red # Color of added tasks in the history reports\n" + "color.history.delete=on yellow # Color of deleted tasks in the history reports\n" + "color.history.done=on green # Color of completed tasks in the history reports\n" + "\n" + "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" + "#shadow.command=list # Task command for shadow file\n" + "#shadow.notify=on # Footnote when updated\n" + "\n" + "#default.project=foo # Default project for 'add' command\n" + "#default.priority=M # Default priority for 'add' command\n" + "default.command=list # When no arguments are specified\n" + "\n" + "_forcecolor=no # Forces color to be on, even for non TTY output\n" + "blanklines=true # Use more whitespace in output\n" + "complete.all.projects=no # Include old project names in 'projects' command\n" + "complete.all.tags=no # Include old tag names in 'tags' command\n" + "debug=no # Display diagnostics\n" + "fontunderline=yes # Uses underlines rather than -------\n" + "shell.prompt=task> # Prompt used by the shell command\n" + "\n" + "# Import heuristics - alternate names for fields (comma-separated list of names)\n" + "#import.synonym.bg=?\n" + "#import.synonym.description=?\n" + "#import.synonym.due=?\n" + "#import.synonym.end=?\n" + "#import.synonym.entry=?\n" + "#import.synonym.fg=?\n" + "#import.synonym.id=?\n" + "#import.synonym.priority=?\n" + "#import.synonym.project=?\n" + "#import.synonym.recur=?\n" + "#import.synonym.start=?\n" + "#import.synonym.status=?\n" + "#import.synonym.tags=?\n" + "#import.synonym.uuid=?\n" + "\n" + "alias.rm=delete # Alias for the delete command\n" + "\n" + "# Fields: id,uuid,project,priority,priority_long,entry,entry_time,\n" + "# start,entry_time,due,recur,recurrence_indicator,age,\n" + "# age_compact,active,tags,tag_indicator,description,\n" + "# description_only,end,end_time\n" + "# Description: This report is ...\n" + "# Sort: due+,priority-,project+\n" + "# Filter: pro:x pri:H +bug limit:10\n" + "\n" + "# task long\n" + "report.long.description=Lists all task, all data, matching the specified criteria\n" + "report.long.columns=id,project,priority,entry,start,due,recur,age,tags,description\n" + "report.long.labels=ID,Project,Pri,Added,Started,Due,Recur,Age,Tags,Description\n" + "report.long.sort=due+,priority-,project+\n" + "report.long.filter=status:pending\n" + "\n" + "# task list\n" + "report.list.description=Lists all tasks matching the specified criteria\n" + "report.list.columns=id,project,priority,due,active,age,description\n" + "report.list.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.list.sort=due+,priority-,project+\n" + "report.list.filter=status:pending\n" + "\n" + "# task ls\n" + "report.ls.description=Minimal listing of all tasks matching the specified criteria\n" + "report.ls.columns=id,project,priority,description\n" + "report.ls.labels=ID,Project,Pri,Description\n" + "report.ls.sort=priority-,project+\n" + "report.ls.filter=status:pending\n" + "\n" + "# task minimal\n" + "report.minimal.description=A really minimal listing\n" + "report.minimal.columns=id,project,description\n" + "report.minimal.labels=ID,Project,Description\n" + "report.minimal.sort=project+,description+\n" + "report.minimal.filter=status:pending\n" + "\n" + "# task newest\n" + "report.newest.description=Shows the newest tasks\n" + "report.newest.columns=id,project,priority,due,active,age,description\n" + "report.newest.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.newest.sort=id-\n" + "report.newest.filter=status:pending limit:10\n" + "\n" + "# task oldest\n" + "report.oldest.description=Shows the oldest tasks\n" + "report.oldest.columns=id,project,priority,due,active,age,description\n" + "report.oldest.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.oldest.sort=id+\n" + "report.oldest.filter=status:pending limit:10\n" + "\n" + "# task overdue\n" + "report.overdue.description=Lists overdue tasks matching the specified criteria\n" + "report.overdue.columns=id,project,priority,due,active,age,description\n" + "report.overdue.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.overdue.sort=due+,priority-,project+\n" + "report.overdue.filter=status:pending due.before:today\n" + "\n" + "# task active\n" + "report.active.description=Lists active tasks matching the specified criteria\n" + "report.active.columns=id,project,priority,due,active,age,description\n" + "report.active.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.active.sort=due+,priority-,project+\n" + "report.active.filter=status:pending start.any:\n" + "\n" + "# task completed\n" + "report.completed.description=Lists completed tasks matching the specified criteria\n" + "report.completed.columns=end,project,priority,age,description\n" + "report.completed.labels=Complete,Project,Pri,Age,Description\n" + "report.completed.sort=end+,priority-,project+\n" + "report.completed.filter=status:completed\n" + "\n" + "# task recurring\n" + "report.recurring.description=Lists recurring tasks matching the specified criteria\n" + "report.recurring.columns=id,project,priority,due,recur,active,age,description\n" + "report.recurring.labels=ID,Project,Pri,Due,Recur,Active,Age,Description\n" + "report.recurring.sort=due+,priority-,project+\n" + "report.recurring.filter=status:pending parent.any:\n" + "\n" + "# task waiting\n" + "report.waiting.description=Lists all waiting tasks matching the specified criteria\n" + "report.waiting.columns=id,project,priority,wait,age,description\n" + "report.waiting.labels=ID,Project,Pri,Wait,Age,Description\n" + "report.waiting.sort=wait+,priority-,project+\n" + "report.waiting.filter=status:waiting\n" + "\n" + "# task all\n" + "report.all.description=Lists all tasks matching the specified criteria\n" + "report.all.columns=id,project,priority,due,active,age,description\n" + "report.all.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.all.sort=due+,priority-,project+\n" + "\n" + "# task next\n" + "report.next.description=Lists the most urgent tasks\n" + "report.next.columns=id,project,priority,due,active,age,description\n" + "report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n" + "report.next.sort=due+,priority-,project+\n" + "report.next.filter=status:pending\n" + "\n"; + +//////////////////////////////////////////////////////////////////////////////// +// DO NOT CALL Config::setDefaults. +// +// This is a default constructor, and as such is only used to: +// a) initialize a default Context constructor +// b) run unit tests +// +// In all real use cases, Config::load is called. Config::Config () { - setDefaults (); } //////////////////////////////////////////////////////////////////////////////// Config::Config (const std::string& file) { + setDefaults (); load (file); } @@ -63,234 +261,94 @@ Config::Config (const std::string& file) // Nested files are now supported, with the following construct: // include /absolute/path/to/file // -bool Config::load (const std::string& file, int nest /* = 1 */) +void Config::load (const std::string& file, int nest /* = 1 */) { if (nest > 10) throw std::string ("Configuration file nested to more than 10 levels deep" " - this has to be a mistake."); - std::ifstream in; - in.open (file.c_str (), std::ifstream::in); - if (in.good ()) + // First time in, load the default values. + if (nest == 1) + setDefaults (); + + // Read the file, then parse the contents. + std::string contents; + if (File::read (file, contents) && contents.length ()) + parse (contents, nest); +} + +//////////////////////////////////////////////////////////////////////////////// +void Config::parse (const std::string& input, int nest /* = 1 */) +{ + // Shortcut case for default constructor. + if (input.length () == 0) + return; + + // Split the input into lines. + std::vector lines; + split (lines, input, "\n"); + + // Parse each line. + std::vector ::iterator it; + for (it = lines.begin (); it != lines.end (); ++it) { - std::string line; - while (getline (in, line)) + std::string line = *it; + + // Remove comments. + std::string::size_type pound = line.find ("#"); // no i18n + if (pound != std::string::npos) + line = line.substr (0, pound); + + line = trim (line, " \t"); // no i18n + + // Skip empty lines. + if (line.length () > 0) { - // Remove comments. - std::string::size_type pound = line.find ("#"); // no i18n - if (pound != std::string::npos) - line = line.substr (0, pound); - - line = trim (line, " \t"); // no i18n - - // Skip empty lines. - if (line.length () > 0) + std::string::size_type equal = line.find ("="); // no i18n + if (equal != std::string::npos) { - std::string::size_type equal = line.find ("="); // no i18n - if (equal != std::string::npos) - { - std::string key = trim (line.substr (0, equal), " \t"); // no i18n - std::string value = trim (line.substr (equal+1, line.length () - equal), " \t"); // no i18n + std::string key = trim (line.substr (0, equal), " \t"); // no i18n + std::string value = trim (line.substr (equal+1, line.length () - equal), " \t"); // no i18n - (*this)[key] = value; - sequence.push_back (key); - } - else + (*this)[key] = value; + } + else + { + std::string::size_type include = line.find ("include"); // no i18n. + if (include != std::string::npos) { - std::string::size_type include = line.find ("include"); // no i18n. - if (include != std::string::npos) + Path included (trim (line.substr (include + 7), " \t")); + if (included.is_absolute ()) { - Path included (trim (line.substr (include + 7), " \t")); - if (included.is_absolute ()) - { - if (included.readable ()) - this->load (included.data, nest + 1); - else - throw std::string ("Could not read include file '") + included.data + "'"; - } + if (included.readable ()) + this->load (included.data, nest + 1); else - throw std::string ("Can only include files with absolute paths, not '") + included.data + "'"; + throw std::string ("Could not read include file '") + included.data + "'"; } else - throw std::string ("Malformed entry in ") + file + ": '" + line + "'"; + throw std::string ("Can only include files with absolute paths, not '") + included.data + "'"; } + else + throw std::string ("Malformed entry '") + line + "'"; } } - - in.close (); - return true; } - - return false; } //////////////////////////////////////////////////////////////////////////////// void Config::createDefaultRC (const std::string& rc, const std::string& data) { - // Create a sample .taskrc file. - std::stringstream contents; - contents << "# Task program configuration file.\n" - << "# For more documentation, see http://taskwarrior.org or try 'man task' and 'man taskrc'\n" - << "\n" - << "# Files\n" - << "data.location=" << data << "\n" - << "locking=on # Use file-level locking\n" - << "\n" - << "# Terminal\n" - << "curses=on # Use ncurses library to determine terminal width\n" - << "#defaultwidth=80 # Without ncurses, assumed width\n" - << "#editor=vi # Preferred text editor\n" - << "\n" - << "# Miscellaneous\n" - << "confirmation=yes # Confirmation on delete, big changes\n" - << "echo.command=yes # Details on command just run\n" - << "annotation.details=2 # Level of verbosity for annotations in reports\n" - << "next=2 # How many tasks per project in next report\n" - << "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" - << "nag=You have higher priority tasks. # Nag message to keep you honest\n" - << "\n" - << "# Dates\n" - << "dateformat=m/d/Y # Preferred input and display date format\n" - << "#reportdateformat=m/d/Y # Preferred input and display date format\n" - << "weekstart=Sunday # Sunday or Monday only\n" - << "displayweeknumber=yes # Show week numbers on calendar\n" - << "due=7 # Task is considered due in 7 days\n" - << "#calendar.details=yes # Calendar shows information for tasks w/due dates\n" - << "#calendar.details.report=list # Report to use when showing task information in cal\n" - << "#monthsperline=2 # Number of calendar months on a line\n" - << "\n" - << "# Color controls.\n" - << "color=on # Use color\n" - << "color.overdue=bold_red # Color of overdue tasks\n" - << "color.due=bold_yellow # Color of due tasks\n" - << "color.pri.H=bold # Color of priority:H tasks\n" - << "#color.pri.M=on_yellow # Color of priority:M tasks\n" - << "#color.pri.L=on_green # Color of priority:L tasks\n" - << "#color.pri.none=white on_blue # Color of priority: tasks\n" - << "color.active=bold_cyan # Color of active tasks\n" - << "color.tagged=yellow # Color of tagged tasks\n" - << "#color.tag.bug=yellow # Color of +bug tasks\n" - << "#color.project.garden=on_green # Color of project:garden tasks\n" - << "#color.keyword.car=on_blue # Color of description.contains:car tasks\n" - << "#color.recurring=on_red # Color of recur.any: tasks\n" - << "#color.header=bold_green # Color of header messages\n" - << "#color.footnote=bold_green # Color of footnote messages\n" - << "#color.alternate=on_rgb253 # Alternate color for line coloring\n" - << "color.calendar.today=black on cyan # Color of today in calendar\n" - << "color.calendar.due=black on green # Color of days with due tasks in calendar\n" - << "color.calendar.overdue=black on red # Color of days with overdue tasks in calendar\n" - << "color.calendar.weekend=black on white # Color of weekend days in calendar\n" - << "\n" - << "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" - << "#shadow.command=list # Task command for shadow file\n" - << "#shadow.notify=on # Footnote when updated\n" - << "\n" - << "#default.project=foo # Unless otherwise specified\n" - << "#default.priority=M # Unless otherwise specified\n" - << "default.command=list # Unless otherwise specified\n" - << "\n" - << "alias.rm=delete\n" - << "\n" - << "# Fields: id,uuid,project,priority,priority_long,entry,entry_time,\n" - << "# start,entry_time,due,recur,recurrence_indicator,age,\n" - << "# age_compact,active,tags,tag_indicator,description,\n" - << "# description_only,end,end_time\n" - << "# Description: This report is ...\n" - << "# Sort: due+,priority-,project+\n" - << "# Filter: pro:x pri:H +bug limit:10\n" - << "\n" - << "# task long\n" - << "report.long.description=Lists all task, all data, matching the specified criteria\n" - << "report.long.columns=id,project,priority,entry,start,due,recur,age,tags,description\n" - << "report.long.labels=ID,Project,Pri,Added,Started,Due,Recur,Age,Tags,Description\n" - << "report.long.sort=due+,priority-,project+\n" - << "report.long.filter=status:pending\n" - << "\n" - << "# task list\n" - << "report.list.description=Lists all tasks matching the specified criteria\n" - << "report.list.columns=id,project,priority,due,active,age,description\n" - << "report.list.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.list.sort=due+,priority-,project+\n" - << "report.list.filter=status:pending\n" - << "\n" - << "# task ls\n" - << "report.ls.description=Minimal listing of all tasks matching the specified criteria\n" - << "report.ls.columns=id,project,priority,description\n" - << "report.ls.labels=ID,Project,Pri,Description\n" - << "report.ls.sort=priority-,project+\n" - << "report.ls.filter=status:pending\n" - << "\n" - << "# task minimal\n" - << "report.minimal.description=A really minimal listing\n" - << "report.minimal.columns=id,project,description\n" - << "report.minimal.labels=ID,Project,Description\n" - << "report.minimal.sort=project+,description+\n" - << "report.minimal.filter=status:pending\n" - << "\n" - << "# task newest\n" - << "report.newest.description=Shows the newest tasks\n" - << "report.newest.columns=id,project,priority,due,active,age,description\n" - << "report.newest.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.newest.sort=id-\n" - << "report.newest.filter=status:pending limit:10\n" - << "\n" - << "# task oldest\n" - << "report.oldest.description=Shows the oldest tasks\n" - << "report.oldest.columns=id,project,priority,due,active,age,description\n" - << "report.oldest.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.oldest.sort=id+\n" - << "report.oldest.filter=status:pending limit:10\n" - << "\n" - << "# task overdue\n" - << "report.overdue.description=Lists overdue tasks matching the specified criteria\n" - << "report.overdue.columns=id,project,priority,due,active,age,description\n" - << "report.overdue.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.overdue.sort=due+,priority-,project+\n" - << "report.overdue.filter=status:pending due.before:today\n" - << "\n" - << "# task active\n" - << "report.active.description=Lists active tasks matching the specified criteria\n" - << "report.active.columns=id,project,priority,due,active,age,description\n" - << "report.active.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.active.sort=due+,priority-,project+\n" - << "report.active.filter=status:pending start.any:\n" - << "\n" - << "# task completed\n" - << "report.completed.description=Lists completed tasks matching the specified criteria\n" - << "report.completed.columns=end,project,priority,age,description\n" - << "report.completed.labels=Complete,Project,Pri,Age,Description\n" - << "report.completed.sort=end+,priority-,project+\n" - << "report.completed.filter=status:completed\n" - << "\n" - << "# task recurring\n" - << "report.recurring.description=Lists recurring tasks matching the specified criteria\n" - << "report.recurring.columns=id,project,priority,due,recur,active,age,description\n" - << "report.recurring.labels=ID,Project,Pri,Due,Recur,Active,Age,Description\n" - << "report.recurring.sort=due+,priority-,project+\n" - << "report.recurring.filter=status:pending parent.any:\n" - << "\n" - << "# task waiting\n" - << "report.waiting.description=Lists all waiting tasks matching the specified criteria\n" - << "report.waiting.columns=id,project,priority,wait,age,description\n" - << "report.waiting.labels=ID,Project,Pri,Wait,Age,Description\n" - << "report.waiting.sort=wait+,priority-,project+\n" - << "report.waiting.filter=status:waiting\n" - << "\n" - << "# task all\n" - << "report.all.description=Lists all tasks matching the specified criteria\n" - << "report.all.columns=id,project,priority,due,active,age,description\n" - << "report.all.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.all.sort=due+,priority-,project+\n" - << "\n" - << "# task next\n" - << "report.next.description=Lists the most urgent tasks\n" - << "report.next.columns=id,project,priority,due,active,age,description\n" - << "report.next.labels=ID,Project,Pri,Due,Active,Age,Description\n" - << "report.next.sort=due+,priority-,project+\n" - << "report.next.filter=status:pending\n" - << "\n"; + // Override data.location in the defaults. + std::string::size_type loc = defaults.find ("data.location=~/.task"); + // loc+0^ +14^ +21^ - spit (rc, contents.str ()); + std::string contents = defaults.substr (0, loc + 14) + + data + + defaults.substr (loc + 21, std::string::npos); + + // Write out the new file. + if (! File::write (rc, contents)) + throw std::string ("Could not write to '") + rc + "'"; } //////////////////////////////////////////////////////////////////////////////// @@ -304,91 +362,13 @@ void Config::createDefaultData (const std::string& data) //////////////////////////////////////////////////////////////////////////////// void Config::setDefaults () { - set ("report.long.description", "Lists all task, all data, matching the specified criteria"); // TODO i18n - set ("report.long.columns", "id,project,priority,entry,start,due,recur,age,tags,description"); // TODO i18n - set ("report.long.labels", "ID,Project,Pri,Added,Started,Due,Recur,Age,Tags,Description"); // TODO i18n - set ("report.long.sort", "due+,priority-,project+"); // TODO i18n - set ("report.long.filter", "status:pending"); // TODO i18n - - set ("report.list.description", "Lists all tasks matching the specified criteria"); // TODO i18n - set ("report.list.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.list.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.list.sort", "due+,priority-,project+"); // TODO i18n - set ("report.list.filter", "status:pending"); // TODO i18n - - set ("report.ls.description", "Short listing of all tasks matching the specified criteria"); // TODO i18n - set ("report.ls.columns", "id,project,priority,description"); // TODO i18n - set ("report.ls.labels", "ID,Project,Pri,Description"); // TODO i18n - set ("report.ls.sort", "priority-,project+"); // TODO i18n - set ("report.ls.filter", "status:pending"); // TODO i18n - - set ("report.minimal.description", "A really minimal listing"); // TODO i18n - set ("report.minimal.columns", "id,project,description"); // TODO i18n - set ("report.minimal.labels", "ID,Project,Description"); // TODO i18n - set ("report.minimal.sort", "project+,description+"); // TODO i18n - set ("report.minimal.filter", "status:pending"); // TODO i18n - - set ("report.newest.description", "Shows the newest tasks"); // TODO i18n - set ("report.newest.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.newest.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.newest.sort", "id-"); // TODO i18n - set ("report.newest.filter", "status:pending limit:10"); // TODO i18n - - set ("report.oldest.description", "Shows the oldest tasks"); // TODO i18n - set ("report.oldest.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.oldest.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.oldest.sort", "id+"); // TODO i18n - set ("report.oldest.filter", "status:pending limit:10"); // TODO i18n - - set ("report.overdue.description", "Lists overdue tasks matching the specified criteria"); // TODO i18n - set ("report.overdue.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.overdue.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.overdue.sort", "due+,priority-,project+"); // TODO i18n - set ("report.overdue.filter", "status:pending due.before:today"); // TODO i18n - - set ("report.active.description", "Lists active tasks matching the specified criteria"); // TODO i18n - set ("report.active.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.active.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.active.sort", "due+,priority-,project+"); // TODO i18n - set ("report.active.filter", "status:pending start.any:"); // TODO i18n - - set ("report.completed.description", "Lists completed tasks matching the specified criteria"); // TODO i18n - set ("report.completed.columns", "end,project,priority,age,description"); // TODO i18n - set ("report.completed.labels", "Complete,Project,Pri,Age,Description"); // TODO i18n - set ("report.completed.sort", "end+,priority-,project+"); // TODO i18n - set ("report.completed.filter", "status:completed"); // TODO i18n - - set ("report.recurring.description", "Lists recurring tasks matching the specified criteria"); // TODO i18n - set ("report.recurring.columns", "id,project,priority,due,recur,active,age,description"); // TODO i18n - set ("report.recurring.labels", "ID,Project,Pri,Due,Recur,Active,Age,Description"); // TODO i18n - set ("report.recurring.sort", "due+,priority-,project+"); // TODO i18n - set ("report.recurring.filter", "status:pending parent.any:"); // TODO i18n - - set ("report.waiting.description", "Lists all waiting tasks matching the specified criteria"); // TODO i18n - set ("report.waiting.columns", "id,project,priority,wait,age,description"); // TODO i18n - set ("report.waiting.labels", "ID,Project,Pri,Wait,Age,Description"); // TODO i18n - set ("report.waiting.sort", "wait+,priority-,project+"); // TODO i18n - set ("report.waiting.filter", "status:waiting"); // TODO i18n - - set ("report.all.description", "Lists all tasks matching the specified criteria"); // TODO i18n - set ("report.all.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.all.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.all.sort", "due+,priority-,project+"); // TODO i18n - - set ("report.next.description", "Lists the most urgent tasks"); // TODO i18n - set ("report.next.columns", "id,project,priority,due,active,age,description"); // TODO i18n - set ("report.next.labels", "ID,Project,Pri,Due,Active,Age,Description"); // TODO i18n - set ("report.next.sort", "due+,priority-,project+"); // TODO i18n - set ("report.next.filter", "status:pending"); // TODO i18n - - set ("alias.rm", "delete"); // TODO i18n + parse (defaults); } //////////////////////////////////////////////////////////////////////////////// void Config::clear () { std::map ::clear (); - sequence.clear (); } //////////////////////////////////////////////////////////////////////////////// @@ -434,10 +414,11 @@ bool Config::get (const std::string& key, const bool default_value) if ((*this).find (key) != (*this).end ()) { std::string value = lowerCase ((*this)[key]); - if (value == "t" || // TODO i18n value == "true" || // TODO i18n value == "1" || // no i18n + value == "+" || // no i18n + value == "y" || // TODO i18n value == "yes" || // TODO i18n value == "on" || // TODO i18n value == "enable" || // TODO i18n @@ -498,40 +479,6 @@ void Config::all (std::vector& items) items.push_back (i->first); } -//////////////////////////////////////////////////////////////////////////////// -void Config::getSequence (std::vector& items) -{ - items = sequence; -} - -//////////////////////////////////////////////////////////////////////////////// -std::string Config::checkForDuplicates () -{ - std::vector duplicates; - std::map unique; - - foreach (i, sequence) - { - if (unique.find (*i) != unique.end ()) - duplicates.push_back (*i); - else - unique[*i] = 0; - } - - std::stringstream out; - if (duplicates.size ()) - { - out << "Found duplicate entries for:" << std::endl; - - foreach (i, duplicates) - out << " " << *i << std::endl; - - out << std::endl; - } - - return out.str (); -} - //////////////////////////////////////////////////////////////////////////////// std::string Config::checkForDeprecatedColor () { diff --git a/src/Config.h b/src/Config.h index f30c4e10f..c08a91c39 100644 --- a/src/Config.h +++ b/src/Config.h @@ -40,12 +40,21 @@ public: Config (const Config&); Config& operator= (const Config&); - bool load (const std::string&, int nest = 1); + void load (const std::string&, int nest = 1); + void parse (const std::string&, int nest = 1); + void createDefaultRC (const std::string&, const std::string&); void createDefaultData (const std::string&); void setDefaults (); void clear (); +/* + const std::string get (const std::string&); + const std::string getInteger (const std::string&); + const std::string getReal (const std::string&); + const std::string getBoolean (const std::string&); +*/ +// const std::string get (const char*); const std::string get (const char*, const char*); const std::string get (const std::string&); @@ -53,17 +62,17 @@ public: bool get (const std::string&, const bool); int get (const std::string&, const int); double get (const std::string&, const double); +// + void set (const std::string&, const int); void set (const std::string&, const double); void set (const std::string&, const std::string&); void all (std::vector &); - void getSequence (std::vector&); - std::string checkForDuplicates (); std::string checkForDeprecatedColor (); private: - std::vector sequence; + static std::string defaults; }; #endif diff --git a/src/command.cpp b/src/command.cpp index 9907f9c53..00963cc08 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -606,7 +606,6 @@ int handleConfig (std::string &outs) } out << context.config.checkForDeprecatedColor (); - out << context.config.checkForDuplicates (); // TODO Check for referenced but missing theme files. // TODO Check for referenced but missing string files. diff --git a/src/tests/add.t b/src/tests/add.t index f18443978..00541d9f1 100755 --- a/src/tests/add.t +++ b/src/tests/add.t @@ -33,7 +33,8 @@ use Test::More tests => 13; # Create the rc file. if (open my $fh, '>', 'add.rc') { - print $fh "data.location=.\n"; + print $fh "data.location=.\n", + "confirmation=off\n"; close $fh; ok (-r 'add.rc', 'Created add.rc'); } diff --git a/src/tests/annotate.t b/src/tests/annotate.t index 7d5eb259e..289699c3c 100755 --- a/src/tests/annotate.t +++ b/src/tests/annotate.t @@ -36,6 +36,7 @@ if (open my $fh, '>', 'annotate.rc') # Note: Use 'rrr' to guarantee a unique report name. Using 'r' conflicts # with 'recurring'. print $fh "data.location=.\n", + "confirmation=off\n", "report.rrr.description=rrr\n", "report.rrr.columns=id,description\n", "report.rrr.sort=id+\n"; diff --git a/src/tests/att.t.cpp b/src/tests/att.t.cpp index 3721b98a4..66df3d8e5 100644 --- a/src/tests/att.t.cpp +++ b/src/tests/att.t.cpp @@ -155,8 +155,8 @@ int main (int argc, char** argv) t.ok (good, "Att::mod (noword)"); good = true; - try {a6.mod ("fartwizzle");} catch (...) {good = false;} - t.notok (good, "Att::mod (fartwizzle)"); + try {a6.mod ("unrecognized");} catch (...) {good = false;} + t.notok (good, "Att::mod (unrecognized)"); // Att::parse Nibbler n (""); diff --git a/src/tests/basic.t b/src/tests/basic.t index 3ff3d58d4..c33a569f0 100755 --- a/src/tests/basic.t +++ b/src/tests/basic.t @@ -33,7 +33,8 @@ use Test::More tests => 7; # Create the rc file. if (open my $fh, '>', 'basic.rc') { - print $fh "data.location=.\n"; + print $fh "data.location=.\n", + "default.command=\n"; close $fh; ok (-r 'basic.rc', 'Created basic.rc'); } diff --git a/src/tests/config.duplicate.t b/src/tests/config.duplicate.t deleted file mode 100755 index 996dbdcf2..000000000 --- a/src/tests/config.duplicate.t +++ /dev/null @@ -1,59 +0,0 @@ -#! /usr/bin/perl -################################################################################ -## task - a command line task list manager. -## -## Copyright 2006 - 2010, 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, '>', 'duplicate.rc') -{ - print $fh "data.location=.\n", - "data.location=.\n", - "color=off\n"; - close $fh; - ok (-r 'duplicate.rc', 'Created duplicate.rc'); -} - -# Test the add command. -my $output = qx{../task rc:duplicate.rc config}; -like ($output, qr/data\.location/ms, 'Duplicate entry detected'); -unlike ($output, qr/colorl/ms, 'Single entry not ignored'); - -# Cleanup. -unlink 'pending.data'; -ok (!-r 'pending.data', 'Removed pending.data'); - -unlink 'undo.data'; -ok (!-r 'undo.data', 'Removed undo.data'); - -unlink 'duplicate.rc'; -ok (!-r 'duplicate.rc', 'Removed duplicate.rc'); - -exit 0; - diff --git a/src/tests/delete.t b/src/tests/delete.t index cf3f56042..97d992b52 100755 --- a/src/tests/delete.t +++ b/src/tests/delete.t @@ -34,6 +34,7 @@ use Test::More tests => 17; if (open my $fh, '>', 'delete.rc') { print $fh "data.location=.\n", + "confirmation=no\n", "echo.command=no\n"; close $fh; ok (-r 'delete.rc', 'Created delete.rc'); diff --git a/src/tests/sequence.t b/src/tests/sequence.t index d7d8ca417..58b3540bb 100755 --- a/src/tests/sequence.t +++ b/src/tests/sequence.t @@ -33,7 +33,8 @@ use Test::More tests => 28; # Create the rc file. if (open my $fh, '>', 'seq.rc') { - print $fh "data.location=.\n"; + print $fh "data.location=.\n", + "confirmation=off\n"; close $fh; ok (-r 'seq.rc', 'Created seq.rc'); } diff --git a/src/tests/shadow.t b/src/tests/shadow.t index 09797075d..fcdacea8d 100755 --- a/src/tests/shadow.t +++ b/src/tests/shadow.t @@ -34,6 +34,7 @@ use Test::More tests => 22; if (open my $fh, '>', 'shadow.rc') { print $fh "data.location=.\n", + "confirmation=off\n", "shadow.file=./shadow.txt\n", "shadow.command=rc:shadow.rc stats\n", "shadow.notify=on\n"; From cb821c2a25060821ceb32e63e0f341e792d01540 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 15 Jan 2010 20:55:06 -0500 Subject: [PATCH 15/27] Config - defaults - Implemented replacement Config::get* methods. - Replaced all calls throughout the code, with the new methods which have no static values as defaults. --- src/Att.cpp | 6 +- src/Config.cpp | 60 ++++------------- src/Config.h | 17 +---- src/Context.cpp | 20 +++--- src/Date.cpp | 24 +++---- src/Permission.cpp | 2 +- src/Table.cpp | 20 +++--- src/command.cpp | 104 ++++++++++++++-------------- src/custom.cpp | 45 +++++++------ src/edit.cpp | 12 ++-- src/import.cpp | 16 ++--- src/interactive.cpp | 4 +- src/recur.cpp | 4 +- src/report.cpp | 161 ++++++++++++++++++++++---------------------- src/text.cpp | 2 +- src/util.cpp | 2 +- 16 files changed, 234 insertions(+), 265 deletions(-) diff --git a/src/Att.cpp b/src/Att.cpp index 515dd7b4f..466af6e1a 100644 --- a/src/Att.cpp +++ b/src/Att.cpp @@ -331,7 +331,7 @@ bool Att::validNameValue ( { // Validate and convert to epoch. if (value != "") - value = Date (value, context.config.get ("dateformat", "m/d/Y")).toEpochString (); + value = Date (value, context.config.get ("dateformat")).toEpochString (); } else if (name == "recur") @@ -571,7 +571,7 @@ bool Att::match (const Att& other) const } else if (which == "date") { - Date literal (mValue.c_str (), context.config.get ("dateformat", "m/d/Y")); + Date literal (mValue.c_str (), context.config.get ("dateformat")); Date variable ((time_t)atoi (other.mValue.c_str ())); if (other.mValue == "" || ! (variable < literal)) return false; @@ -601,7 +601,7 @@ bool Att::match (const Att& other) const } else if (which == "date") { - Date literal (mValue.c_str (), context.config.get ("dateformat", "m/d/Y")); + Date literal (mValue.c_str (), context.config.get ("dateformat")); Date variable ((time_t)atoi (other.mValue.c_str ())); if (! (variable > literal)) return false; diff --git a/src/Config.cpp b/src/Config.cpp index f75691251..2255f3255 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -371,23 +371,6 @@ void Config::clear () std::map ::clear (); } -//////////////////////////////////////////////////////////////////////////////// -// Return the configuration value given the specified key. -const std::string Config::get (const char* key) -{ - return this->get (std::string (key)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Return the configuration value given the specified key. If a default_value -// is present, it will be the returned value in the event of a missing key. -const std::string Config::get ( - const char* key, - const char* default_value) -{ - return this->get (std::string (key), std::string (default_value)); -} - //////////////////////////////////////////////////////////////////////////////// // Return the configuration value given the specified key. const std::string Config::get (const std::string& key) @@ -396,20 +379,25 @@ const std::string Config::get (const std::string& key) } //////////////////////////////////////////////////////////////////////////////// -// Return the configuration value given the specified key. If a default_value -// is present, it will be the returned value in the event of a missing key. -const std::string Config::get ( - const std::string& key, - const std::string& default_value) +const int Config::getInteger (const std::string& key) { if ((*this).find (key) != (*this).end ()) - return (*this)[key]; + return atoi ((*this)[key].c_str ()); - return default_value; + return 0; } //////////////////////////////////////////////////////////////////////////////// -bool Config::get (const std::string& key, const bool default_value) +const double Config::getReal (const std::string& key) +{ + if ((*this).find (key) != (*this).end ()) + return atof ((*this)[key].c_str ()); + + return 0.0; +} + +//////////////////////////////////////////////////////////////////////////////// +const bool Config::getBoolean (const std::string& key) { if ((*this).find (key) != (*this).end ()) { @@ -424,29 +412,9 @@ bool Config::get (const std::string& key, const bool default_value) value == "enable" || // TODO i18n value == "enabled") // TODO i18n return true; - - return false; } - return default_value; -} - -//////////////////////////////////////////////////////////////////////////////// -int Config::get (const std::string& key, const int default_value) -{ - if ((*this).find (key) != (*this).end ()) - return atoi ((*this)[key].c_str ()); - - return default_value; -} - -//////////////////////////////////////////////////////////////////////////////// -double Config::get (const std::string& key, const double default_value) -{ - if ((*this).find (key) != (*this).end ()) - return atof ((*this)[key].c_str ()); - - return default_value; + return false; } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Config.h b/src/Config.h index c08a91c39..866ee5450 100644 --- a/src/Config.h +++ b/src/Config.h @@ -48,21 +48,10 @@ public: void setDefaults (); void clear (); -/* const std::string get (const std::string&); - const std::string getInteger (const std::string&); - const std::string getReal (const std::string&); - const std::string getBoolean (const std::string&); -*/ -// - const std::string get (const char*); - const std::string get (const char*, const char*); - const std::string get (const std::string&); - const std::string get (const std::string&, const std::string&); - bool get (const std::string&, const bool); - int get (const std::string&, const int); - double get (const std::string&, const double); -// + const int getInteger (const std::string&); + const double getReal (const std::string&); + const bool getBoolean (const std::string&); void set (const std::string&, const int); void set (const std::string&, const double); diff --git a/src/Context.cpp b/src/Context.cpp index 192acd0d2..71028b4db 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -98,11 +98,11 @@ void Context::initialize () { config.set ("curses", "off"); - if (! config.get (std::string ("_forcecolor"), false)) + if (! config.getBoolean ("_forcecolor")) config.set ("color", "off"); } - if (config.get ("color", true)) + if (config.getBoolean ("color")) initializeColorRules (); // Load appropriate stringtable as soon after the config file as possible, to @@ -154,16 +154,16 @@ int Context::run () } // Dump all debug messages. - if (config.get (std::string ("debug"), false)) + if (config.getBoolean ("debug")) foreach (d, debugMessages) - if (config.get ("color", true) || config.get (std::string ("_forcecolor"), false)) + if (config.getBoolean ("color") || config.getBoolean ("_forcecolor")) std::cout << colorizeDebug (*d) << std::endl; else std::cout << *d << std::endl; // Dump all headers. foreach (h, headers) - if (config.get ("color", true) || config.get (std::string ("_forcecolor"), false)) + if (config.getBoolean ("color") || config.getBoolean ("_forcecolor")) std::cout << colorizeHeader (*h) << std::endl; else std::cout << *h << std::endl; @@ -173,7 +173,7 @@ int Context::run () // Dump all footnotes. foreach (f, footnotes) - if (config.get ("color", true) || config.get (std::string ("_forcecolor"), false)) + if (config.getBoolean ("color") || config.getBoolean ("_forcecolor")) std::cout << colorizeFootnote (*f) << std::endl; else std::cout << *f << std::endl; @@ -268,8 +268,10 @@ void Context::shadow () // Run report. Use shadow.command, using default.command as a fallback // with "list" as a default. - std::string command = config.get ("shadow.command", - config.get ("default.command", "list")); + std::string command = config.get ("shadow.command"); + if (command == "") + command = config.get ("default.command"); + split (args, command, ' '); initialize (); @@ -292,7 +294,7 @@ void Context::shadow () config.set ("color", oldColor); // Optionally display a notification that the shadow file was updated. - if (config.get (std::string ("shadow.notify"), false)) + if (config.getBoolean ("shadow.notify")) footnote (std::string ("[Shadow file '") + shadowFile + "' updated]"); inShadow = false; diff --git a/src/Date.cpp b/src/Date.cpp index 65bb47dbb..d096852eb 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -322,18 +322,18 @@ const std::string Date::toString (const std::string& format /*= "m/d/Y" */) cons char c = localFormat[i]; switch (c) { - case 'm': sprintf (buffer, "%d", this->month ()); break; - case 'M': sprintf (buffer, "%02d", this->month ()); break; - case 'd': sprintf (buffer, "%d", this->day ()); break; - case 'D': sprintf (buffer, "%02d", this->day ()); break; - case 'y': sprintf (buffer, "%02d", this->year () % 100); break; - case 'Y': sprintf (buffer, "%d", this->year ()); break; - case 'a': sprintf (buffer, "%.3s", Date::dayName(dayOfWeek()).c_str() ); break; - case 'A': sprintf (buffer, "%s", Date::dayName(dayOfWeek()).c_str() ); break; - case 'b': sprintf (buffer, "%.3s", Date::monthName(month()).c_str() ); break; - case 'B': sprintf (buffer, "%.9s", Date::monthName(month()).c_str() ); break; - case 'V': sprintf (buffer, "%02d", Date::weekOfYear(Date::dayOfWeek(context.config.get ("weekstart", "Sunday")))); break; - default: sprintf (buffer, "%c", c); break; + case 'm': sprintf (buffer, "%d", this->month ()); break; + case 'M': sprintf (buffer, "%02d", this->month ()); break; + case 'd': sprintf (buffer, "%d", this->day ()); break; + case 'D': sprintf (buffer, "%02d", this->day ()); break; + case 'y': sprintf (buffer, "%02d", this->year () % 100); break; + case 'Y': sprintf (buffer, "%d", this->year ()); break; + case 'a': sprintf (buffer, "%.3s", Date::dayName (dayOfWeek ()).c_str ()); break; + case 'A': sprintf (buffer, "%s", Date::dayName (dayOfWeek ()).c_str ()); break; + case 'b': sprintf (buffer, "%.3s", Date::monthName (month ()).c_str ()); break; + case 'B': sprintf (buffer, "%.9s", Date::monthName (month ()).c_str ()); break; + case 'V': sprintf (buffer, "%02d", Date::weekOfYear (Date::dayOfWeek (context.config.get ("weekstart")))); break; + default: sprintf (buffer, "%c", c); break; } formatted += buffer; diff --git a/src/Permission.cpp b/src/Permission.cpp index ec61a1916..7e2520f28 100644 --- a/src/Permission.cpp +++ b/src/Permission.cpp @@ -40,7 +40,7 @@ Permission::Permission () , quit (false) { // Turning confirmations off is the same as entering "all". - if (context.config.get ("confirmation", true) == false) + if (context.config.getBoolean ("confirmation") == false) allConfirmed = true; } diff --git a/src/Table.cpp b/src/Table.cpp index fd81db490..060b5e84b 100644 --- a/src/Table.cpp +++ b/src/Table.cpp @@ -881,10 +881,12 @@ void Table::sort (std::vector & order) else { - Date dl ((std::string)*left, context.config.get("reportdateformat", - context.config.get("dateformat","m/d/Y"))); - Date dr ((std::string)*right, context.config.get("reportdateformat", - context.config.get("dateformat","m/d/Y"))); + std::string format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); + + Date dl ((std::string)*left, format); + Date dr ((std::string)*right, format); if (dl > dr) SWAP } @@ -901,10 +903,12 @@ void Table::sort (std::vector & order) else { - Date dl ((std::string)*left, context.config.get("reportdateformat", - context.config.get("dateformat","m/d/Y"))); - Date dr ((std::string)*right, context.config.get("reportdateformat", - context.config.get("dateformat","m/d/Y"))); + std::string format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); + + Date dl ((std::string)*left, format); + Date dr ((std::string)*right, format); if (dl < dr) SWAP } diff --git a/src/command.cpp b/src/command.cpp index 00963cc08..3e9eca531 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -70,12 +70,12 @@ int handleAdd (std::string &outs) // Override with default.project, if not specified. if (context.task.get ("project") == "") - context.task.set ("project", context.config.get ("default.project", "")); + context.task.set ("project", context.config.get ("default.project")); // Override with default.priority, if not specified. if (context.task.get ("priority") == "") { - std::string defaultPriority = context.config.get ("default.priority", ""); + std::string defaultPriority = context.config.get ("default.priority"); if (Att::validNameValue ("priority", "", defaultPriority)) context.task.set ("priority", defaultPriority); } @@ -96,7 +96,7 @@ int handleAdd (std::string &outs) // Only valid tasks can be added. context.task.validate (); - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); context.tdb.add (context.task); #ifdef FEATURE_NEW_ID @@ -123,7 +123,7 @@ int handleProjects (std::string &outs) context.filter.push_back (Att ("status", "pending")); std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); int quantity = context.tdb.loadPending (tasks, context.filter); context.tdb.commit (); context.tdb.unlock (); @@ -161,8 +161,8 @@ int handleProjects (std::string &outs) table.addColumn ("Pri:M"); table.addColumn ("Pri:H"); - if (context.config.get ("color", true) || - context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || + context.config.getBoolean ("_forcecolor")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -211,10 +211,10 @@ int handleProjects (std::string &outs) int handleCompletionProjects (std::string &outs) { std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; - if (context.config.get (std::string ("complete.all.projects"), false)) + if (context.config.getBoolean ("complete.all.projects")) context.tdb.load (tasks, filter); else context.tdb.loadPending (tasks, filter); @@ -246,7 +246,7 @@ int handleTags (std::string &outs) context.filter.push_back (Att ("status", "pending")); std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); int quantity = context.tdb.loadPending (tasks, context.filter); context.tdb.commit (); context.tdb.unlock (); @@ -273,8 +273,8 @@ int handleTags (std::string &outs) table.addColumn ("Tag"); table.addColumn ("Count"); - if (context.config.get ("color", true) || - context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || + context.config.getBoolean ("_forcecolor")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -312,10 +312,10 @@ int handleTags (std::string &outs) int handleCompletionTags (std::string &outs) { std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; - if (context.config.get (std::string ("complete.all.tags"), false)) + if (context.config.getBoolean ("complete.all.tags")) context.tdb.load (tasks, filter); else context.tdb.loadPending (tasks, filter); @@ -387,7 +387,7 @@ int handleCompletionVersion (std::string &outs) int handleCompletionIDs (std::string &outs) { std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); context.tdb.commit (); @@ -414,7 +414,7 @@ void handleUndo () { context.disallowModification (); - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); context.tdb.undo (); context.tdb.unlock (); } @@ -449,11 +449,11 @@ int handleVersion (std::string &outs) Color bold ("bold"); out << std::endl - << ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + << ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) ? bold.colorize (PACKAGE) : PACKAGE) << " " - << ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + << ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) ? bold.colorize (VERSION) : VERSION) << " built for " @@ -512,11 +512,11 @@ int handleConfig (std::string &outs) // Create a table for output. Table table; table.setTableWidth (width); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); table.addColumn ("Config variable"); table.addColumn ("Value"); - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -643,7 +643,7 @@ int handleDelete (std::string &outs) context.disallowModification (); std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -664,7 +664,7 @@ int handleDelete (std::string &outs) << task->get ("description") << "'?"; - if (!context.config.get (std::string ("confirmation"), false) || confirm (question.str ())) + if (!context.config.getBoolean ("confirmation") || confirm (question.str ())) { // Check for the more complex case of a recurring task. If this is a // recurring task, get confirmation to delete them all. @@ -684,7 +684,7 @@ int handleDelete (std::string &outs) sibling->set ("end", endTime); context.tdb.update (*sibling); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Deleting recurring task " << sibling->id << " '" @@ -717,7 +717,7 @@ int handleDelete (std::string &outs) task->set ("end", endTime); context.tdb.update (*task); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Deleting task " << task->id << " '" @@ -748,7 +748,7 @@ int handleStart (std::string &outs) context.disallowModification (); std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -766,7 +766,7 @@ int handleStart (std::string &outs) context.tdb.update (*task); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Started " << task->id << " '" @@ -804,7 +804,7 @@ int handleStop (std::string &outs) context.disallowModification (); std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -818,7 +818,7 @@ int handleStop (std::string &outs) task->remove ("start"); context.tdb.update (*task); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Stopped " << task->id << " '" @@ -853,7 +853,7 @@ int handleDone (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -862,7 +862,7 @@ int handleDone (std::string &outs) context.filter.applySequence (tasks, context.sequence); Permission permission; - if (context.sequence.size () > (size_t) context.config.get ("bulk", 2)) + if (context.sequence.size () > (size_t) context.config.getInteger ("bulk")) permission.bigSequence (); bool nagged = false; @@ -894,7 +894,7 @@ int handleDone (std::string &outs) { context.tdb.update (*task); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Completed " << task->id << " '" @@ -923,7 +923,7 @@ int handleDone (std::string &outs) context.tdb.commit (); context.tdb.unlock (); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Marked " << count << " task" @@ -960,7 +960,7 @@ int handleExport (std::string &outs) // Get all the tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -986,7 +986,7 @@ int handleModify (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -995,7 +995,7 @@ int handleModify (std::string &outs) context.filter.applySequence (tasks, context.sequence); Permission permission; - if (context.sequence.size () > (size_t) context.config.get ("bulk", 2)) + if (context.sequence.size () > (size_t) context.config.getInteger ("bulk")) permission.bigSequence (); foreach (task, tasks) @@ -1066,7 +1066,7 @@ int handleModify (std::string &outs) context.tdb.commit (); context.tdb.unlock (); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Modified " << count << " task" << (count == 1 ? "" : "s") << std::endl; outs = out.str (); @@ -1080,7 +1080,7 @@ int handleAppend (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -1089,7 +1089,7 @@ int handleAppend (std::string &outs) context.filter.applySequence (tasks, context.sequence); Permission permission; - if (context.sequence.size () > (size_t) context.config.get ("bulk", 2)) + if (context.sequence.size () > (size_t) context.config.getInteger ("bulk")) permission.bigSequence (); foreach (task, tasks) @@ -1117,7 +1117,7 @@ int handleAppend (std::string &outs) { context.tdb.update (*other); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Appended '" << context.task.get ("description") << "' to task " @@ -1134,7 +1134,7 @@ int handleAppend (std::string &outs) context.tdb.commit (); context.tdb.unlock (); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Appended " << count << " task" << (count == 1 ? "" : "s") << std::endl; outs = out.str (); @@ -1148,7 +1148,7 @@ int handlePrepend (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -1157,7 +1157,7 @@ int handlePrepend (std::string &outs) context.filter.applySequence (tasks, context.sequence); Permission permission; - if (context.sequence.size () > (size_t) context.config.get ("bulk", 2)) + if (context.sequence.size () > (size_t) context.config.getInteger ("bulk")) permission.bigSequence (); foreach (task, tasks) @@ -1185,7 +1185,7 @@ int handlePrepend (std::string &outs) { context.tdb.update (*other); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Prepended '" << context.task.get ("description") << "' to task " @@ -1202,7 +1202,7 @@ int handlePrepend (std::string &outs) context.tdb.commit (); context.tdb.unlock (); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Prepended " << count << " task" << (count == 1 ? "" : "s") << std::endl; outs = out.str (); @@ -1216,7 +1216,7 @@ int handleDuplicate (std::string &outs) int count = 0; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -1259,7 +1259,7 @@ int handleDuplicate (std::string &outs) context.tdb.add (dup); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Duplicated " << task->id << " '" @@ -1269,7 +1269,7 @@ int handleDuplicate (std::string &outs) ++count; } - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) { out << "Duplicated " << count << " task" << (count == 1 ? "" : "s") << std::endl; #ifdef FEATURE_NEW_ID @@ -1294,7 +1294,7 @@ void handleShell () { // Display some kind of welcome message. Color bold (Color::nocolor, Color::nocolor, false, true, false); - std::cout << ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + std::cout << ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) ? bold.colorize (PACKAGE_STRING) : PACKAGE_STRING) << " shell" @@ -1318,7 +1318,7 @@ void handleShell () do { - std::cout << context.config.get ("shell.prompt", "task>") << " "; + std::cout << context.config.get ("shell.prompt") << " "; command = ""; std::getline (std::cin, command); @@ -1370,7 +1370,7 @@ int handleColor (std::string &outs) int rc = 0; std::stringstream out; - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { // If there is something in the description, then assume that is a color, // and display it as a sample. @@ -1515,7 +1515,7 @@ int handleAnnotate (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); Filter filter; context.tdb.loadPending (tasks, filter); @@ -1523,7 +1523,7 @@ int handleAnnotate (std::string &outs) context.filter.applySequence (tasks, context.sequence); Permission permission; - if (context.sequence.size () > (size_t) context.config.get ("bulk", 2)) + if (context.sequence.size () > (size_t) context.config.getInteger ("bulk")) permission.bigSequence (); foreach (task, tasks) @@ -1537,7 +1537,7 @@ int handleAnnotate (std::string &outs) { context.tdb.update (*task); - if (context.config.get ("echo.command", true)) + if (context.config.getBoolean ("echo.command")) out << "Annotated " << task->id << " with '" diff --git a/src/custom.cpp b/src/custom.cpp index a4da90208..1de5b6149 100644 --- a/src/custom.cpp +++ b/src/custom.cpp @@ -82,7 +82,7 @@ int handleCustomReport (const std::string& report, std::string &outs) // Get all the tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -161,7 +161,7 @@ int runCustomReport ( Table table; table.setTableWidth (context.getWidth ()); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); foreach (task, tasks) table.addRow (); @@ -251,7 +251,7 @@ int runCustomReport ( if (entered.length ()) { Date dt (::atoi (entered.c_str ())); - entered = dt.toString (context.config.get ("dateformat", "m/d/Y")); + entered = dt.toString (context.config.get ("dateformat")); table.addCell (row, columnCount, entered); } } @@ -270,7 +270,7 @@ int runCustomReport ( if (entered.length ()) { Date dt (::atoi (entered.c_str ())); - entered = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + entered = dt.toStringWithTime (context.config.get ("dateformat")); table.addCell (row, columnCount, entered); } } @@ -289,7 +289,7 @@ int runCustomReport ( if (started.length ()) { Date dt (::atoi (started.c_str ())); - started = dt.toString (context.config.get ("dateformat", "m/d/Y")); + started = dt.toString (context.config.get ("dateformat")); table.addCell (row, columnCount, started); } } @@ -308,7 +308,7 @@ int runCustomReport ( if (started.length ()) { Date dt (::atoi (started.c_str ())); - started = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + started = dt.toStringWithTime (context.config.get ("dateformat")); table.addCell (row, columnCount, started); } } @@ -327,7 +327,7 @@ int runCustomReport ( if (started.length ()) { Date dt (::atoi (started.c_str ())); - started = dt.toString (context.config.get ("dateformat", "m/d/Y")); + started = dt.toString (context.config.get ("dateformat")); table.addCell (row, columnCount, started); } } @@ -339,6 +339,8 @@ int runCustomReport ( table.setColumnWidth (columnCount, Table::minimum); table.setColumnJustification (columnCount, Table::right); + std::string format = context.config.get ("dateformat"); + std::string started; for (unsigned int row = 0; row < tasks.size(); ++row) { @@ -346,7 +348,7 @@ int runCustomReport ( if (started.length ()) { Date dt (::atoi (started.c_str ())); - started = dt.toStringWithTime (context.config.get ("dateformat", "m/d/Y")); + started = dt.toStringWithTime (format); table.addCell (row, columnCount, started); } } @@ -358,13 +360,14 @@ int runCustomReport ( table.setColumnWidth (columnCount, Table::minimum); table.setColumnJustification (columnCount, Table::left); + std::string format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); + int row = 0; std::string due; foreach (task, tasks) - table.addCell (row++, columnCount, - getDueDate (*task, - context.config.get ("reportdateformat", - context.config.get ("dateformat", "m/d/Y")))); + table.addCell (row++, columnCount, getDueDate (*task, format)); dueColumn = columnCount; } @@ -511,7 +514,7 @@ int runCustomReport ( if (wait != "") { Date dt (::atoi (wait.c_str ())); - wait = dt.toString (context.config.get ("dateformat", "m/d/Y")); + wait = dt.toString (context.config.get ("dateformat")); table.addCell (row++, columnCount, wait); } } @@ -519,8 +522,8 @@ int runCustomReport ( // Common to all columns. // Add underline. - if ((context.config.get (std::string ("color"), true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) table.setColumnUnderline (columnCount); else table.setTableDashedUnderline (); @@ -581,8 +584,8 @@ int runCustomReport ( // Now auto colorize all rows. std::string due; - Color color_due (context.config.get ("color.due", "green")); - Color color_overdue (context.config.get ("color.overdue", "red")); + Color color_due (context.config.get ("color.due")); + Color color_overdue (context.config.get ("color.overdue")); bool imminent; bool overdue; @@ -602,7 +605,7 @@ int runCustomReport ( } } - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { Color c (tasks[row].get ("fg") + " " + tasks[row].get ("bg")); autoColorize (tasks[row], c); @@ -617,15 +620,15 @@ int runCustomReport ( } // If an alternating row color is specified, notify the table. - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { - Color alternate (context.config.get ("color.alternate", "")); + Color alternate (context.config.get ("color.alternate")); if (alternate.nontrivial ()) table.setTableAlternateColor (alternate); } // Limit the number of rows according to the report definition. - int maximum = context.config.get (std::string ("report.") + report + ".limit", (int)0); + int maximum = context.config.getInteger (std::string ("report.") + report + ".limit"); // If the custom report has a defined limit, then allow a numeric override. // This is an integer specified as a filter (limit:10). diff --git a/src/edit.cpp b/src/edit.cpp index 82ceea1a5..b61e81aa3 100644 --- a/src/edit.cpp +++ b/src/edit.cpp @@ -80,7 +80,7 @@ static std::string findDate ( if (value != "") { - Date dt (value, context.config.get ("dateformat", "m/d/Y")); + Date dt (value, context.config.get ("dateformat")); char epoch [16]; sprintf (epoch, "%d", (int)dt.toEpoch ()); return std::string (epoch); @@ -100,7 +100,7 @@ static std::string formatDate ( if (value.length ()) { Date dt (::atoi (value.c_str ())); - value = dt.toString (context.config.get ("dateformat", "m/d/Y")); + value = dt.toString (context.config.get ("dateformat")); } return value; @@ -162,7 +162,7 @@ static std::string formatTask (Task task) foreach (anno, annotations) { Date dt (::atoi (anno->name ().substr (11).c_str ())); - before << " Annotation: " << dt.toString (context.config.get ("dateformat", "m/d/Y")) + before << " Annotation: " << dt.toString (context.config.get ("dateformat")) << " " << anno->value () << std::endl; } @@ -499,7 +499,7 @@ static void parseTask (Task& task, const std::string& after) std::string::size_type gap = value.find (" "); if (gap != std::string::npos) { - Date when (value.substr (0, gap), context.config.get ("dateformat", "m/d/Y")); + Date when (value.substr (0, gap), context.config.get ("dateformat")); // This guarantees that if more than one annotation has the same date, // that the seconds will be different, thus unique, thus not squashed. @@ -534,7 +534,7 @@ void editFile (Task& task) spit (file.str (), before); // Determine correct editor: .taskrc:editor > $VISUAL > $EDITOR > vi - std::string editor = context.config.get ("editor", ""); + std::string editor = context.config.get ("editor"); char* peditor = getenv ("VISUAL"); if (editor == "" && peditor) editor = std::string (peditor); peditor = getenv ("EDITOR"); @@ -604,7 +604,7 @@ int handleEdit (std::string &outs) std::stringstream out; std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); Filter filter; context.tdb.loadPending (tasks, filter); diff --git a/src/import.cpp b/src/import.cpp index 2bba5e69f..926fe135e 100644 --- a/src/import.cpp +++ b/src/import.cpp @@ -168,12 +168,12 @@ static void decorateTask (Task& task) task.setStatus (Task::pending); // Override with default.project, if not specified. - std::string defaultProject = context.config.get ("default.project", ""); + std::string defaultProject = context.config.get ("default.project"); if (!task.has ("project") && defaultProject != "") task.set ("project", defaultProject); // Override with default.priority, if not specified. - std::string defaultPriority = context.config.get ("default.priority", ""); + std::string defaultPriority = context.config.get ("default.priority"); if (!task.has ("priority") && defaultPriority != "" && Att::validNameValue ("priority", "", defaultPriority)) @@ -185,7 +185,7 @@ static std::string importTask_1_4_3 (const std::vector & lines) { std::vector failed; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); std::vector ::const_iterator it; for (it = lines.begin (); it != lines.end (); ++it) @@ -342,7 +342,7 @@ static std::string importTask_1_5_0 (const std::vector & lines) { std::vector failed; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); std::vector ::const_iterator it; for (it = lines.begin (); it != lines.end (); ++it) @@ -504,7 +504,7 @@ static std::string importTask_1_6_0 (const std::vector & lines) { std::vector failed; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); std::vector ::const_iterator it; for (it = lines.begin (); it != lines.end (); ++it) @@ -715,7 +715,7 @@ static std::string importTodoSh_2_0 (const std::vector & lines) { std::vector failed; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); std::vector ::const_iterator it; for (it = lines.begin (); it != lines.end (); ++it) @@ -840,7 +840,7 @@ static std::string importText (const std::vector & lines) std::vector failed; int count = 0; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); std::vector ::const_iterator it; for (it = lines.begin (); it != lines.end (); ++it) @@ -904,7 +904,7 @@ static std::string importCSV (const std::vector & lines) { std::vector failed; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); // Set up mappings. Assume no fields match. std::map mapping; diff --git a/src/interactive.cpp b/src/interactive.cpp index 3f3b44c8a..241f9cecd 100644 --- a/src/interactive.cpp +++ b/src/interactive.cpp @@ -137,10 +137,10 @@ int Context::interactive () int Context::getWidth () { // Determine window size, and set table accordingly. - int width = config.get ("defaultwidth", (int) 80); + int width = config.getInteger ("defaultwidth"); #ifdef HAVE_LIBNCURSES - if (config.get ("curses", true)) + if (config.getBoolean ("curses")) { #ifdef FEATURE_NCURSES_COLS initscr (); diff --git a/src/recur.cpp b/src/recur.cpp index db4025b15..e4d31ac6f 100644 --- a/src/recur.cpp +++ b/src/recur.cpp @@ -390,7 +390,7 @@ int getDueState (const std::string& due) if (dt < thisDay) return 2; - int imminentperiod = context.config.get ("due", 7); + int imminentperiod = context.config.getInteger ("due"); if (imminentperiod == 0) return 1; @@ -406,7 +406,7 @@ int getDueState (const std::string& due) //////////////////////////////////////////////////////////////////////////////// bool nag (Task& task) { - std::string nagMessage = context.config.get ("nag", ""); + std::string nagMessage = context.config.get ("nag"); if (nagMessage != "") { // Load all pending tasks. diff --git a/src/report.cpp b/src/report.cpp index 1b87f2645..9e0598d4f 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -66,7 +66,7 @@ int shortUsage (std::string &outs) table.setColumnWidth (1, Table::minimum); table.setColumnWidth (2, Table::flexible); table.setTableWidth (context.getWidth ()); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); int row = table.addRow (); table.addCell (row, 0, "Usage:"); @@ -199,8 +199,9 @@ int shortUsage (std::string &outs) foreach (report, all) { std::string command = std::string ("task ") + *report + std::string (" [tags] [attrs] desc..."); - std::string description = context.config.get ( - std::string ("report.") + *report + ".description", std::string ("(missing description)")); + std::string description = context.config.get (std::string ("report.") + *report + ".description"); + if (description == "") + description = "(missing description)"; row = table.addRow (); table.addCell (row, 1, command); @@ -300,7 +301,7 @@ int handleInfo (std::string &outs) int rc = 0; // Get all the tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.loadPending (tasks, context.filter); context.tdb.commit (); @@ -315,13 +316,13 @@ int handleInfo (std::string &outs) { Table table; table.setTableWidth (context.getWidth ()); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); table.addColumn ("Name"); table.addColumn ("Value"); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -412,20 +413,24 @@ int handleInfo (std::string &outs) table.addCell (row, 0, "Due"); Date dt (atoi (task->get ("due").c_str ())); - std::string due = getDueDate (*task, context.config.get("reportdateformat", context.config.get("dateformat","m/d/Y"))); + std::string format = context.config.get ("reportdateformat"); + if (format == "") + format = context.config.get ("dateformat"); + + std::string due = getDueDate (*task, format); table.addCell (row, 1, due); overdue = (dt < now) ? true : false; - int imminentperiod = context.config.get ("due", 7); + int imminentperiod = context.config.getInteger ("due"); Date imminentDay = now + imminentperiod * 86400; imminent = dt < imminentDay ? true : false; - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { if (overdue) - table.setCellColor (row, 1, Color (context.config.get ("color.overdue", "red"))); + table.setCellColor (row, 1, Color (context.config.get ("color.overdue"))); else if (imminent) - table.setCellColor (row, 1, Color (context.config.get ("color.due", "green"))); + table.setCellColor (row, 1, Color (context.config.get ("color.due"))); } } @@ -435,7 +440,7 @@ int handleInfo (std::string &outs) row = table.addRow (); table.addCell (row, 0, "Waiting until"); Date dt (atoi (task->get ("wait").c_str ())); - table.addCell (row, 1, dt.toString (context.config.get ("dateformat", "m/d/Y"))); + table.addCell (row, 1, dt.toString (context.config.get ("dateformat"))); } // start @@ -444,7 +449,7 @@ int handleInfo (std::string &outs) row = table.addRow (); table.addCell (row, 0, "Start"); Date dt (atoi (task->get ("start").c_str ())); - table.addCell (row, 1, dt.toString (context.config.get ("dateformat", "m/d/Y"))); + table.addCell (row, 1, dt.toString (context.config.get ("dateformat"))); } // end @@ -453,7 +458,7 @@ int handleInfo (std::string &outs) row = table.addRow (); table.addCell (row, 0, "End"); Date dt (atoi (task->get ("end").c_str ())); - table.addCell (row, 1, dt.toString (context.config.get ("dateformat", "m/d/Y"))); + table.addCell (row, 1, dt.toString (context.config.get ("dateformat"))); } // tags ... @@ -478,7 +483,7 @@ int handleInfo (std::string &outs) row = table.addRow (); table.addCell (row, 0, "Entered"); Date dt (atoi (task->get ("entry").c_str ())); - std::string entry = dt.toString (context.config.get ("dateformat", "m/d/Y")); + std::string entry = dt.toString (context.config.get ("dateformat")); std::string age; std::string created = task->get ("entry"); @@ -531,7 +536,7 @@ int handleReportSummary (std::string &outs) int rc = 0; // Scan the pending tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -594,8 +599,8 @@ int handleReportSummary (std::string &outs) table.addColumn ("Complete"); table.addColumn ("0% 100%"); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -610,7 +615,7 @@ int handleReportSummary (std::string &outs) table.setColumnJustification (3, Table::right); table.sortOn (0, Table::ascendingCharacter); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); int barWidth = 30; foreach (i, allProjects) @@ -632,7 +637,7 @@ int handleReportSummary (std::string &outs) int completedBar = (c * barWidth) / (c + p); std::string bar; - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { bar = "\033[42m"; for (int b = 0; b < completedBar; ++b) @@ -726,7 +731,7 @@ int handleReportNext (std::string &outs) // Get all the tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -778,7 +783,7 @@ int handleReportHistory (std::string &outs) // Scan the pending tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -822,7 +827,7 @@ int handleReportHistory (std::string &outs) // Now build the table. Table table; - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); table.addColumn ("Year"); table.addColumn ("Month"); table.addColumn ("Added"); @@ -830,8 +835,8 @@ int handleReportHistory (std::string &outs) table.addColumn ("Deleted"); table.addColumn ("Net"); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -894,7 +899,7 @@ int handleReportHistory (std::string &outs) } table.addCell (row, 5, net); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && net) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && net) table.setCellColor (row, 5, net > 0 ? Color (Color::red) : Color (Color::green)); } @@ -905,7 +910,7 @@ int handleReportHistory (std::string &outs) row = table.addRow (); table.addCell (row, 1, "Average"); - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) table.setRowColor (row, Color (Color::nocolor, Color::nocolor, false, true, false)); table.addCell (row, 2, totalAdded / (table.rowCount () - 2)); table.addCell (row, 3, totalCompleted / (table.rowCount () - 2)); @@ -938,7 +943,7 @@ int handleReportGHistory (std::string &outs) // Scan the pending tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -984,13 +989,13 @@ int handleReportGHistory (std::string &outs) // Now build the table. Table table; - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); table.addColumn ("Year"); table.addColumn ("Month"); table.addColumn ("Number Added/Completed/Deleted"); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -1049,7 +1054,7 @@ int handleReportGHistory (std::string &outs) unsigned int deletedBar = (widthOfBar * deletedGroup[i->first]) / maxLine; std::string bar = ""; - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { char number[24]; std::string aBar = ""; @@ -1109,7 +1114,7 @@ int handleReportGHistory (std::string &outs) << table.render () << std::endl; - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) out << "Legend: " << color_added.colorize ("added") << ", " @@ -1135,7 +1140,7 @@ int handleReportTimesheet (std::string &outs) { // Scan the pending tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -1145,7 +1150,7 @@ int handleReportTimesheet (std::string &outs) int width = context.getWidth (); // What day of the week does the user consider the first? - int weekStart = Date::dayOfWeek (context.config.get ("weekstart", "Sunday")); + int weekStart = Date::dayOfWeek (context.config.get ("weekstart")); if (weekStart != 0 && weekStart != 1) throw std::string ("The 'weekstart' configuration variable may " "only contain 'Sunday' or 'Monday'."); @@ -1164,7 +1169,7 @@ int handleReportTimesheet (std::string &outs) if (context.sequence.size () == 1) quantity = context.sequence[0]; - bool color = context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false); + bool color = context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor"); std::stringstream out; for (int week = 0; week < quantity; ++week) @@ -1172,9 +1177,9 @@ int handleReportTimesheet (std::string &outs) Date endString (end); endString -= 86400; - std::string title = start.toString (context.config.get ("dateformat", "m/d/Y")) + std::string title = start.toString (context.config.get ("dateformat")) + " - " - + endString.toString (context.config.get ("dateformat", "m/d/Y")); + + endString.toString (context.config.get ("dateformat")); Color bold (Color::nocolor, Color::nocolor, false, true, false); out << std::endl @@ -1189,7 +1194,7 @@ int handleReportTimesheet (std::string &outs) completed.addColumn ("Due"); completed.addColumn ("Description"); - if (color && context.config.get (std::string ("fontunderline"), "true")) + if (color && context.config.getBoolean ("fontunderline")) { completed.setColumnUnderline (1); completed.setColumnUnderline (2); @@ -1218,7 +1223,7 @@ int handleReportTimesheet (std::string &outs) { int row = completed.addRow (); completed.addCell (row, 1, task->get ("project")); - completed.addCell (row, 2, getDueDate (*task,context.config.get("dateformat","m/d/Y"))); + completed.addCell (row, 2, getDueDate (*task, context.config.get("dateformat"))); completed.addCell (row, 3, getFullDescription (*task)); if (color) @@ -1245,7 +1250,7 @@ int handleReportTimesheet (std::string &outs) started.addColumn ("Due"); started.addColumn ("Description"); - if (color && context.config.get (std::string ("fontunderline"), "true")) + if (color && context.config.getBoolean ("fontunderline")) { completed.setColumnUnderline (1); completed.setColumnUnderline (2); @@ -1274,7 +1279,7 @@ int handleReportTimesheet (std::string &outs) { int row = started.addRow (); started.addCell (row, 1, task->get ("project")); - started.addCell (row, 2, getDueDate (*task,context.config.get("dateformat","m/d/Y"))); + started.addCell (row, 2, getDueDate (*task, context.config.get ("dateformat"))); started.addCell (row, 3, getFullDescription (*task)); if (color) @@ -1312,10 +1317,10 @@ std::string renderMonths ( int monthsPerLine) { Table table; - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); // What day of the week does the user consider the first? - int weekStart = Date::dayOfWeek (context.config.get ("weekstart", "Sunday")); + int weekStart = Date::dayOfWeek (context.config.get ("weekstart")); if (weekStart != 0 && weekStart != 1) throw std::string ("The 'weekstart' configuration variable may " "only contain 'Sunday' or 'Monday'."); @@ -1346,8 +1351,8 @@ std::string renderMonths ( table.addColumn ("Sa"); } - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (i + 1); table.setColumnUnderline (i + 2); @@ -1418,7 +1423,7 @@ std::string renderMonths ( int dow = temp.dayOfWeek (); int woy = temp.weekOfYear (weekStart); - if (context.config.get ("displayweeknumber", true)) + if (context.config.getBoolean ("displayweeknumber")) table.addCell (row, (8 * mpl), woy); // Calculate column id. @@ -1431,12 +1436,12 @@ std::string renderMonths ( table.addCell (row, thisCol, d); - Color color_today (context.config.get ("color.calendar.today", "black on cyan")); - Color color_due (context.config.get ("color.calendar.due", "black on green")); - Color color_overdue (context.config.get ("color.calendar.overdue", "black on red")); - Color color_weekend (context.config.get ("color.calendar.weekend", "black on white")); + Color color_today (context.config.get ("color.calendar.today")); + Color color_due (context.config.get ("color.calendar.due")); + Color color_overdue (context.config.get ("color.calendar.overdue")); + Color color_weekend (context.config.get ("color.calendar.weekend")); - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) { if (dow == 0 || dow == 6) table.setCellColor (row, thisCol, color_weekend); @@ -1454,7 +1459,7 @@ std::string renderMonths ( { Date due (atoi (task->get ("due").c_str ())); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && due.day () == d && due.month () == months[mpl] && due.year () == years[mpl]) @@ -1480,7 +1485,7 @@ int handleReportCalendar (std::string &outs) // Each month requires 28 text columns width. See how many will actually // fit. But if a preference is specified, and it fits, use it. int width = context.getWidth (); - int preferredMonthsPerLine = (context.config.get (std::string ("monthsperline"), 0)); + int preferredMonthsPerLine = (context.config.getInteger ("monthsperline")); int monthsThatFit = width / 26; int monthsPerLine = monthsThatFit; @@ -1490,7 +1495,7 @@ int handleReportCalendar (std::string &outs) // Get all the tasks. std::vector tasks; Filter filter; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.loadPending (tasks, filter); context.tdb.commit (); @@ -1640,12 +1645,12 @@ int handleReportCalendar (std::string &outs) } } - Color color_today (context.config.get ("color.calendar.today", "black on cyan")); - Color color_due (context.config.get ("color.calendar.due", "black on green")); - Color color_overdue (context.config.get ("color.calendar.overdue", "black on red")); - Color color_weekend (context.config.get ("color.calendar.weekend", "black on white")); + Color color_today (context.config.get ("color.calendar.today")); + Color color_due (context.config.get ("color.calendar.due")); + Color color_overdue (context.config.get ("color.calendar.overdue")); + Color color_weekend (context.config.get ("color.calendar.weekend")); - if (context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) + if (context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) out << "Legend: " << color_today.colorize ("today") << ", " @@ -1658,7 +1663,7 @@ int handleReportCalendar (std::string &outs) << optionalBlankLine () << std::endl; - if (context.config.get (std::string ("calendar.details"), false)) + if (context.config.getBoolean ("calendar.details")) { --details_mFrom; if (details_mFrom == 0) @@ -1676,12 +1681,12 @@ int handleReportCalendar (std::string &outs) } Date date_after (details_mFrom, details_dFrom, details_yFrom); - std::string after = date_after.toString (context.config.get ("dateformat", "m/d/Y")); - - Date date_before (mTo, 1, yTo); - std::string before = date_before.toString (context.config.get ("dateformat", "m/d/Y")); + std::string after = date_after.toString (context.config.get ("dateformat")); - std::string report = context.config.get ("calendar.details.report", "list"); + Date date_before (mTo, 1, yTo); + std::string before = date_before.toString (context.config.get ("dateformat")); + + std::string report = context.config.get ("calendar.details.report"); std::string report_filter = context.config.get ("report." + report + ".filter"); report_filter += " due.after:" + after + " due.before:" + before; @@ -1734,7 +1739,7 @@ int handleReportStats (std::string &outs) // Get all the tasks. std::vector tasks; - context.tdb.lock (context.config.get ("locking", true)); + context.tdb.lock (context.config.getBoolean ("locking")); handleRecurrence (); context.tdb.load (tasks, context.filter); context.tdb.commit (); @@ -1801,12 +1806,12 @@ int handleReportStats (std::string &outs) Table table; table.setTableWidth (context.getWidth ()); table.setTableIntraPadding (2); - table.setDateFormat (context.config.get ("dateformat", "m/d/Y")); + table.setDateFormat (context.config.get ("dateformat")); table.addColumn ("Category"); table.addColumn ("Data"); - if ((context.config.get ("color", true) || context.config.get (std::string ("_forcecolor"), false)) && - context.config.get (std::string ("fontunderline"), "true")) + if ((context.config.getBoolean ("color") || context.config.getBoolean ("_forcecolor")) && + context.config.getBoolean ("fontunderline")) { table.setColumnUnderline (0); table.setColumnUnderline (1); @@ -1879,12 +1884,12 @@ int handleReportStats (std::string &outs) Date e (earliest); row = table.addRow (); table.addCell (row, 0, "Oldest task"); - table.addCell (row, 1, e.toString (context.config.get ("dateformat", "m/d/Y"))); + table.addCell (row, 1, e.toString (context.config.get ("dateformat"))); Date l (latest); row = table.addRow (); table.addCell (row, 0, "Newest task"); - table.addCell (row, 1, l.toString (context.config.get ("dateformat", "m/d/Y"))); + table.addCell (row, 1, l.toString (context.config.get ("dateformat"))); row = table.addRow (); table.addCell (row, 0, "Task used for"); @@ -1946,7 +1951,7 @@ void gatherNextTasks (std::vector & tasks) Date now; // How many items per project? Default 2. - int limit = context.config.get ("next", 2); + int limit = context.config.getInteger ("next"); // due:< 1wk, pri:* foreach (task, tasks) @@ -2115,7 +2120,7 @@ std::string getFullDescription (Task& task) task.getAnnotations (annotations); if (annotations.size () != 0) - switch (context.config.get("annotation.details",2)) + switch (context.config.getInteger ("annotation.details")) { case 0: desc = "+" + desc; @@ -2126,7 +2131,7 @@ std::string getFullDescription (Task& task) desc = "+" + desc; Att anno (annotations.back()); Date dt (atoi (anno.name ().substr (11).c_str ())); - std::string when = dt.toString (context.config.get ("dateformat", "m/d/Y")); + std::string when = dt.toString (context.config.get ("dateformat")); desc += "\n" + when + " " + anno.value (); } break; @@ -2134,7 +2139,7 @@ std::string getFullDescription (Task& task) foreach (anno, annotations) { Date dt (atoi (anno->name ().substr (11).c_str ())); - std::string when = dt.toString (context.config.get ("dateformat", "m/d/Y")); + std::string when = dt.toString (context.config.get ("dateformat")); desc += "\n" + when + " " + anno->value (); } break; @@ -2151,8 +2156,6 @@ std::string getDueDate (Task& task, const std::string& format) { Date d (atoi (due.c_str ())); due = d.toString (format); - //due = d.toString (context.config.get ("dateformat", "m/d/Y")); - //due = d.toString (context.config.get ("reportdateformat", context.config.get ("dateformat", "m/d/Y"))); } return due; diff --git a/src/text.cpp b/src/text.cpp index 5388888a5..1da58de17 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -352,7 +352,7 @@ std::string ucFirst (const std::string& input) //////////////////////////////////////////////////////////////////////////////// const char* optionalBlankLine () { - if (context.config.get ("blanklines", true) == true) // no i18n + if (context.config.getBoolean ("blanklines") == true) // no i18n return newline; return noline; diff --git a/src/util.cpp b/src/util.cpp index f0e3ee543..28bff6d2b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -589,7 +589,7 @@ std::string renderAttribute (const std::string& name, const std::string& value) if (a.type (name) == "date") { Date d ((time_t)::atoi (value.c_str ())); - return d.toString (context.config.get ("dateformat", "m/d/Y")); + return d.toString (context.config.get ("dateformat")); } return value; From 8e47342a189482f24631f5d5f44b16d2dc4a40e2 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Fri, 15 Jan 2010 22:04:41 -0500 Subject: [PATCH 16/27] Unit Tests - Modified config.t.cpp according to the recent Config.cpp changes. --- src/tests/config.t.cpp | 76 +++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/src/tests/config.t.cpp b/src/tests/config.t.cpp index 40d6c6ecf..3191218e3 100644 --- a/src/tests/config.t.cpp +++ b/src/tests/config.t.cpp @@ -33,69 +33,47 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (18); + UnitTest t (11); - // void set (const std::string&, const int); - // int get (const std::string&, const int); Config c; - c.set ("int1", 0); - t.is (c.get ("int1", 9), 0, "Config::set/get int"); - - c.set ("int2", 3); - t.is (c.get ("int2", 9), 3, "Config::set/get int"); - - c.set ("int3", -9); - t.is (c.get ("int3", 9), -9, "Config::set/get int"); - - // void set (const std::string&, const double); - // double get (const std::string&, const double); - c.set ("double1", 0.0); - t.is (c.get ("double1", 9.0), 0.0, "Config::set/get double"); - - c.set ("double2", 3.0); - t.is (c.get ("double2", 9.0), 3.0, "Config::set/get double"); - - c.set ("double3", -9.0); - t.is (c.get ("double3", 9.0), -9.0, "Config::set/get double"); // void set (const std::string&, const std::string&); - c.set ("str1", "one"); - t.is (c.get ("str1", ""), "one", "Config::set/get std::string"); - - c.set ("str1", ""); - t.is (c.get ("str1", "no"), "", "Config::set/get std::string"); - - // const std::string get (const char*); - c.set ("str1", "one"); - t.is (c.get ((char*) "str1"), (char*)"one", "Config::set/get char*"); - - // const std::string get (const char*, const char*); - c.set ("str1", "one"); - t.is (c.get ((char*)"str1", (char*)""), "one", "Config::set/get char*"); - - c.set ("str1", ""); - t.is (c.get ((char*)"str1", (char*)"no"), "", "Config::set/get char*"); - // const std::string get (const std::string&); c.set ("str1", "one"); - t.is (c.get (std::string ("str1")), "one", "Config::set/get std::string"); + t.is (c.get ("str1"), "one", "Config::set/get std::string"); c.set ("str1", ""); - t.is (c.get (std::string ("str1")), "", "Config::set/get std::string"); + t.is (c.get ("str1"), "", "Config::set/get std::string"); - // const std::string get (const std::string&, const std::string&); - c.set ("str1", "one"); - t.is (c.get (std::string ("str1"), std::string ("no")), "one", "Config::set/get std::string"); + // void set (const std::string&, const int); + // const int getInteger (const std::string&); + c.set ("int1", 1); + t.is (c.getInteger ("int1"), 1, "Config::set/get int"); - c.set ("str1", ""); - t.is (c.get (std::string ("str1"), std::string ("no")), "", "Config::set/get std::string"); + c.set ("int2", 3); + t.is (c.getInteger ("int2"), 3, "Config::set/get int"); - // bool get (const std::string&, const bool); + c.set ("int3", -9); + t.is (c.getInteger ("int3"), -9, "Config::set/get int"); + + // void set (const std::string&, const double); + // const double getReal (const std::string&); + c.set ("double1", 1.0); + t.is (c.getReal ("double1"), 1.0, "Config::set/get double"); + + c.set ("double2", 3.0); + t.is (c.getReal ("double2"), 3.0, "Config::set/get double"); + + c.set ("double3", -9.0); + t.is (c.getReal ("double3"), -9.0, "Config::set/get double"); + + // void set (const std::string&, const bool); + // const bool getBoolean (const std::string&); c.set ("bool1", false); - t.is (c.get (std::string ("bool1"), (bool)true), false, "Config::set/get bool"); + t.is (c.getBoolean ("bool1"), false, "Config::set/get bool"); c.set ("bool1", true); - t.is (c.get (std::string ("bool1"), (bool)false), true, "Config::set/get bool"); + t.is (c.getBoolean ("bool1"), true, "Config::set/get bool"); // void all (std::vector &); std::vector all; From b596e96b43d68e76ee1bbcdb682fa840febc1890 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 10:27:31 -0500 Subject: [PATCH 17/27] Enhancement - Path, File, Directory integration - Replaced all access calls. - Replaced all stat calls. - Obsoleted util.cpp isAbsoluteDirectory calls. - Obsoleted util.cpp expandPath calls. --- src/Config.cpp | 8 +++---- src/Context.cpp | 54 ++++++++++++++++++++++++-------------------- src/TDB.cpp | 23 +++++++++---------- src/command.cpp | 7 ++++-- src/edit.cpp | 7 +++--- src/report.cpp | 27 ++++++++++------------ src/tests/util.t.cpp | 15 +----------- src/util.cpp | 43 ----------------------------------- src/util.h | 2 -- 9 files changed, 66 insertions(+), 120 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 2255f3255..0648d56d3 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -33,7 +33,7 @@ #include #include #include -#include "Path.h" +#include "Directory.h" #include "File.h" #include "Config.h" #include "text.h" @@ -354,9 +354,9 @@ void Config::createDefaultRC (const std::string& rc, const std::string& data) //////////////////////////////////////////////////////////////////////////////// void Config::createDefaultData (const std::string& data) { - Path p (data); - if (! p.exists ()) - mkdir (data.c_str (), S_IRWXU); + Directory d (data); + if (! d.exists ()) + d.create (); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/Context.cpp b/src/Context.cpp index 71028b4db..3d3d83494 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -32,6 +32,8 @@ #include #include #include "Context.h" +#include "Directory.h" +#include "File.h" #include "Timer.h" #include "text.h" #include "util.h" @@ -107,7 +109,7 @@ void Context::initialize () // Load appropriate stringtable as soon after the config file as possible, to // allow all subsequent messages to be localizable. - std::string location = expandPath (config.get ("data.location")); + Directory location (config.get ("data.location")); std::string locale = config.get ("locale"); // If there is a locale variant (en-US.), then strip it. @@ -116,16 +118,16 @@ void Context::initialize () locale = locale.substr (0, period); if (locale != "") - stringtable.load (location + "/strings." + locale); + stringtable.load (location.data + "/strings." + locale); // TODO Handle "--version, -v" right here? // init TDB. tdb.clear (); std::vector all; - split (all, location, ','); + split (all, location.data, ','); foreach (path, all) - tdb.location (expandPath (*path)); + tdb.location (*path); } //////////////////////////////////////////////////////////////////////////////// @@ -244,8 +246,8 @@ int Context::dispatch (std::string &out) void Context::shadow () { // Determine if shadow file is enabled. - std::string shadowFile = expandPath (config.get ("shadow.file")); - if (shadowFile != "") + File shadowFile (config.get ("shadow.file")); + if (shadowFile.data != "") { inShadow = true; // Prevents recursion in case shadow command writes. @@ -281,21 +283,21 @@ void Context::shadow () parse (); std::string result; (void)dispatch (result); - std::ofstream out (shadowFile.c_str ()); + std::ofstream out (shadowFile.data.c_str ()); if (out.good ()) { out << result; out.close (); } else - throw std::string ("Could not write file '") + shadowFile + "'"; + throw std::string ("Could not write file '") + shadowFile.data + "'"; config.set ("curses", oldCurses); config.set ("color", oldColor); // Optionally display a notification that the shadow file was updated. if (config.getBoolean ("shadow.notify")) - footnote (std::string ("[Shadow file '") + shadowFile + "' updated]"); + footnote (std::string ("[Shadow file '") + shadowFile.data + "' updated]"); inShadow = false; } @@ -355,8 +357,10 @@ void Context::loadCorrectConfigFile () "Could not read home directory from the passwd file.")); std::string home = pw->pw_dir; - std::string rc = home + "/.taskrc"; - std::string data = home + "/.task"; +// std::string rc = home + "/.taskrc"; +// std::string data = home + "/.task"; + File rc (home + "/.taskrc"); + Directory data (home + "./task"); // Is there an file_override for rc:? foreach (arg, args) @@ -366,17 +370,17 @@ void Context::loadCorrectConfigFile () else if (arg->substr (0, 3) == "rc:") { file_override = *arg; - rc = arg->substr (3); + rc = File (arg->substr (3)); - home = rc; - std::string::size_type last_slash = rc.rfind ("/"); + home = rc.data; + std::string::size_type last_slash = rc.data.rfind ("/"); if (last_slash != std::string::npos) - home = rc.substr (0, last_slash); + home = rc.data.substr (0, last_slash); else home = "."; args.erase (arg); - header ("Using alternate .taskrc file " + rc); // TODO i18n + header ("Using alternate .taskrc file " + rc.data); // TODO i18n break; } } @@ -384,10 +388,10 @@ void Context::loadCorrectConfigFile () // Load rc file. config.clear (); // Dump current values. config.setDefaults (); // Add in the custom reports. - config.load (rc); // Load new file. + config.load (rc.data); // Load new file. if (config.get ("data.location") != "") - data = config.get ("data.location"); + data = Directory (config.get ("data.location")); // Are there any var_overrides for data.location? foreach (arg, args) @@ -397,35 +401,35 @@ void Context::loadCorrectConfigFile () else if (arg->substr (0, 17) == "rc.data.location:" || arg->substr (0, 17) == "rc.data.location=") { - data = arg->substr (17); - header ("Using alternate data.location " + data); // TODO i18n + data = Directory (arg->substr (17)); + header ("Using alternate data.location " + data.data); // TODO i18n break; } } // Do we need to create a default rc? - if (access (rc.c_str (), F_OK)) + if (! rc.exists ()) { if (confirm ("A configuration file could not be found in " // TODO i18n + home + "\n\n" + "Would you like a sample " - + rc + + rc.data + " created, so task can proceed?")) { - config.createDefaultRC (rc, data); + config.createDefaultRC (rc.data, data.data); } else throw std::string ("Cannot proceed without rc file."); } // Create data location, if necessary. - config.createDefaultData (data); + config.createDefaultData (data.data); // Load rc file. config.clear (); // Dump current values. config.setDefaults (); // Add in the custom reports. - config.load (rc); // Load new file. + config.load (rc.data); // Load new file. // Apply overrides of type: "rc.name:value", or "rc.name=value". std::vector filtered; diff --git a/src/TDB.cpp b/src/TDB.cpp index 5e31bc2ca..c231d86ec 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -35,6 +35,7 @@ #include "text.h" #include "util.h" #include "TDB.h" +#include "Directory.h" #include "Table.h" #include "Timer.h" #include "Color.h" @@ -107,12 +108,13 @@ void TDB::clear () //////////////////////////////////////////////////////////////////////////////// void TDB::location (const std::string& path) { - if (access (expandPath (path).c_str (), F_OK)) + Directory d (path); + if (!d.exists ()) throw std::string ("Data location '") + path + "' does not exist, or is not readable and writable."; - mLocations.push_back (Location (path)); + mLocations.push_back (Location (d.data)); } //////////////////////////////////////////////////////////////////////////////// @@ -503,11 +505,11 @@ int TDB::nextId () //////////////////////////////////////////////////////////////////////////////// void TDB::undo () { - std::string location = expandPath (context.config.get ("data.location")); + Directory location (context.config.get ("data.location")); - std::string undoFile = location + "/undo.data"; - std::string pendingFile = location + "/pending.data"; - std::string completedFile = location + "/completed.data"; + std::string undoFile = location.data + "/undo.data"; + std::string pendingFile = location.data + "/pending.data"; + std::string completedFile = location.data + "/completed.data"; // load undo.data std::vector u; @@ -725,9 +727,10 @@ FILE* TDB::openAndLock (const std::string& file) // TODO Need provision here for read-only locations. // Check for access. - bool exists = access (file.c_str (), F_OK) ? false : true; + File f (file); + bool exists = f.exists (); if (exists) - if (access (file.c_str (), R_OK | W_OK)) + if (!f.readable () || !f.writable ()) throw std::string ("Task does not have the correct permissions for '") + file + "'."; @@ -755,8 +758,6 @@ FILE* TDB::openAndLock (const std::string& file) //////////////////////////////////////////////////////////////////////////////// void TDB::writeUndo (const Task& after, FILE* file) { - Timer t ("TDB::writeUndo"); - fprintf (file, "time %u\nnew %s---\n", (unsigned int) time (NULL), @@ -766,8 +767,6 @@ void TDB::writeUndo (const Task& after, FILE* file) //////////////////////////////////////////////////////////////////////////////// void TDB::writeUndo (const Task& before, const Task& after, FILE* file) { - Timer t ("TDB::writeUndo"); - fprintf (file, "time %u\nold %snew %s---\n", (unsigned int) time (NULL), diff --git a/src/command.cpp b/src/command.cpp index 3e9eca531..432fbf734 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -37,6 +37,7 @@ #include #include "Permission.h" +#include "Directory.h" #include "text.h" #include "util.h" #include "main.h" @@ -619,12 +620,14 @@ int handleConfig (std::string &outs) } else { - if (context.config.get ("data.location") == "") + Directory location (context.config.get ("data.location")); + + if (location.data == "") out << "Configuration error: data.location not specified in .taskrc " "file." << std::endl; - if (access (expandPath (context.config.get ("data.location")).c_str (), X_OK)) + if (! location.exists ()) out << "Configuration error: data.location contains a directory name" " that doesn't exist, or is unreadable." << std::endl; diff --git a/src/edit.cpp b/src/edit.cpp index b61e81aa3..59abc0c1b 100644 --- a/src/edit.cpp +++ b/src/edit.cpp @@ -33,6 +33,7 @@ #include #include #include +#include "Directory.h" #include "Date.h" #include "Duration.h" #include "text.h" @@ -521,13 +522,13 @@ static void parseTask (Task& task, const std::string& after) void editFile (Task& task) { // Check for file permissions. - std::string dataLocation = expandPath (context.config.get ("data.location")); - if (access (dataLocation.c_str (), X_OK)) + Directory location (context.config.get ("data.location")); + if (! location.writable ()) throw std::string ("Your data.location directory is not writable."); // Create a temp file name in data.location. std::stringstream file; - file << dataLocation << "/task." << getpid () << "." << task.id << ".task"; + file << location.data << "/task." << getpid () << "." << task.id << ".task"; // Format the contents, T -> text, write to a file. std::string before = formatTask (task); diff --git a/src/report.cpp b/src/report.cpp index 9e0598d4f..ca0198a23 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -37,6 +36,8 @@ #include #include "Context.h" +#include "Directory.h" +#include "File.h" #include "Date.h" #include "Table.h" #include "text.h" @@ -1716,24 +1717,20 @@ int handleReportStats (std::string &outs) // Go get the file sizes. size_t dataSize = 0; - struct stat s; - std::string location = expandPath (context.config.get ("data.location")); - std::string file = location + "/pending.data"; - if (!stat (file.c_str (), &s)) - dataSize += s.st_size; + Directory location (context.config.get ("data.location")); + File pending (location.data + "/pending.data"); + dataSize += pending.size (); - file = location + "/completed.data"; - if (!stat (file.c_str (), &s)) - dataSize += s.st_size; + File completed (location.data + "/completed.data"); + dataSize += completed.size (); - file = location + "/undo.data"; - if (!stat (file.c_str (), &s)) - dataSize += s.st_size; + File undo (location.data + "/undo.data"); + dataSize += undo.size (); - std::vector undo; - slurp (file, undo, false); + std::vector undoTxns; + slurp (undo.data, undoTxns, false); int undoCount = 0; - foreach (tx, undo) + foreach (tx, undoTxns) if (tx->substr (0, 3) == "---") ++undoCount; diff --git a/src/tests/util.t.cpp b/src/tests/util.t.cpp index 5003e25e2..ecc6b796c 100644 --- a/src/tests/util.t.cpp +++ b/src/tests/util.t.cpp @@ -34,7 +34,7 @@ Context context; //////////////////////////////////////////////////////////////////////////////// int main (int argc, char** argv) { - UnitTest t (439); + UnitTest t (430); // TODO bool confirm (const std::string&); // TODO int confirm3 (const std::string&); @@ -514,19 +514,6 @@ int main (int argc, char** argv) // TODO const std::string uuid (); - // std::string expandPath (const std::string&); - t.ok (expandPath ("foo") == "foo", "expandPath nop"); - t.ok (expandPath ("~/") != "~/", "expandPath ~/"); - t.ok (expandPath ("~") != "~", "expandPath ~"); - - // bool isAbsolutePath (const std::string&); - t.notok (isAbsolutePath ("."), "isAbsolutePath ."); - t.notok (isAbsolutePath ("~"), "isAbsolutePath ~"); - t.ok (isAbsolutePath (expandPath ("~")), "isAbsolutePath (expandPath ~)"); - t.ok (isAbsolutePath (expandPath ("~/")), "isAbsolutePath (expandPath ~/)"); - t.ok (isAbsolutePath ("/"), "isAbsolutePath /"); - t.ok (isAbsolutePath ("/tmp"), "isAbsolutePath /tmp"); - // TODO bool slurp (const std::string&, std::vector &, bool trimLines = false); // TODO bool slurp (const std::string&, std::string&, bool trimLines = false); // TODO void spit (const std::string&, const std::string&); diff --git a/src/util.cpp b/src/util.cpp index 28bff6d2b..69df5360b 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -341,49 +341,6 @@ const std::string uuid () } #endif -//////////////////////////////////////////////////////////////////////////////// -// no i18n -std::string expandPath (const std::string& in) -{ - std::string copy = in; - std::string::size_type tilde; - - if ((tilde = copy.find ("~/")) != std::string::npos) - { - struct passwd* pw = getpwuid (getuid ()); - copy.replace (tilde, 1, pw->pw_dir); - } - else if ((tilde = copy.find ("~")) != std::string::npos) - { - struct passwd* pw = getpwuid (getuid ()); - std::string home = pw->pw_dir; - home += "/"; - copy.replace (tilde, 1, home); - } - else if ((tilde = copy.find ("~")) != std::string::npos) - { - std::string::size_type slash; - if ((slash = copy.find ("/", tilde)) != std::string::npos) - { - std::string name = copy.substr (tilde + 1, slash - tilde - 1); - struct passwd* pw = getpwnam (name.c_str ()); - if (pw) - copy.replace (tilde, slash - tilde, pw->pw_dir); - } - } - - return copy; -} - -//////////////////////////////////////////////////////////////////////////////// -bool isAbsolutePath (const std::string& in) -{ - if (in.length () && in[0] == '/') - return true; - - return false; -} - //////////////////////////////////////////////////////////////////////////////// // On Solaris no flock function exists. #ifdef SOLARIS diff --git a/src/util.h b/src/util.h index b6ee6576f..3dbd8b2ea 100644 --- a/src/util.h +++ b/src/util.h @@ -60,8 +60,6 @@ std::string formatSecondsCompact (time_t); std::string formatBytes (size_t); int autoComplete (const std::string&, const std::vector&, std::vector&); const std::string uuid (); -std::string expandPath (const std::string&); -bool isAbsolutePath (const std::string&); #ifdef SOLARIS #define LOCK_SH 1 From 81acaa6ae0c5ca917ee12fff34fa262957970b87 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 10:35:18 -0500 Subject: [PATCH 18/27] Enhancement - Eliminated all unlink calls. --- src/edit.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/edit.cpp b/src/edit.cpp index 59abc0c1b..f24b920a0 100644 --- a/src/edit.cpp +++ b/src/edit.cpp @@ -34,6 +34,7 @@ #include #include #include "Directory.h" +#include "File.h" #include "Date.h" #include "Duration.h" #include "text.h" @@ -593,7 +594,7 @@ ARE_THESE_REALLY_HARMFUL: std::cout << "No edits were detected." << std::endl; // Cleanup. - unlink (file.str ().c_str ()); + File::remove (file.str ()); } //////////////////////////////////////////////////////////////////////////////// From 57cac493620417a3958190894fbf6839dc6084be Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 12:26:59 -0500 Subject: [PATCH 19/27] Documentation Update - Added more Qs the FAQ. --- doc/man/task-faq.5 | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/doc/man/task-faq.5 b/doc/man/task-faq.5 index 50ed9cdca..bf09aa7a3 100644 --- a/doc/man/task-faq.5 +++ b/doc/man/task-faq.5 @@ -36,14 +36,51 @@ Task will create 'new-file' if it doesn't already exist. Note that this is a good way to learn about new configuration settings, if your .taskrc file was created by an older version of task. +.TP +.B Q: Do I need to back up my task data? +Yes. You should back up your ~/.task directory, and probably your ~/.taskrc +file too. + .TP .B Q: Can I share my tasks between different machines? .TP .B Q: The undo.data file gets very large - do I need it? +You need it if you want the undo capability. But if it gets large, you can +certainly truncate it to save space, just be careful to delete lines from the +top of the file, up to and including a separator '---'. Note that it does not +slow down task, because task never reads it until you want to undo. Otherwise +task only appends to the file. .TP .B Q: How do I know whether my terminal support 256 colors? +The easiest way is to just try it! With task 1.9 or later, you simply run + + $ task color + +and a full color palette is displayed, if you look at it and see lots of +different colors, then your terminal supports 256 colors. If you see only +8 or 16 colors, many of them repeated, with blank areas then your terminal +does not support 256 colors. xterm does. iTerm does. + +.TP +.B Q: How can I make task put the command in the terminal window title? +You cannot. But you can make the shell do it, and you can make the shell +call the task program. Here is a Bash script that does this: + + #! /bin/bash + + printf "\033]0;task $*\a" + /usr/local/bin/task $* + +You just need to run the script, and let the script run task. Here is a Bash +function that does the same thing: + + t () + { + printf "\033]0;task $*\a" + /usr/local/bin/task $* + } .SH "CREDITS & COPYRIGHTS" task was written by P. Beckingham . From a6875ced6e83ff052cf5b6f064d31952dd76917b Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 13:51:11 -0500 Subject: [PATCH 20/27] Copyright etc - Added Federico Hernandez to the task copyright, based on the amount of work and commits to the project, in the areas of Date handling and the calendar report. - Bumped Cory Donnelly up to Contributing Author for the large quantity of ideas, suggestions and testing. --- AUTHORS | 2 +- src/Date.cpp | 2 +- src/Date.h | 2 +- src/command.cpp | 4 ++-- src/report.cpp | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/AUTHORS b/AUTHORS index 6e55fde6e..1a9c81146 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,6 +3,7 @@ The development of task was made possible by the significant contributions of th Federico Hernandez (Package Maintainer & Contributing Author) David J Patrick (Designer) John Florian (Contributing Author) + Cory Donnelly (Contributing Author) The following submitted code, packages or analysis, and deserve special thanks: Damian Glenny @@ -18,7 +19,6 @@ The following submitted code, packages or analysis, and deserve special thanks: Johan Friis Steven de Brouwer Pietro Cerutti - Cory Donnelly Thanks to the following, who submitted detailed bug reports and excellent suggestions: Eugene Kramer diff --git a/src/Date.cpp b/src/Date.cpp index d096852eb..35f53cfa9 100644 --- a/src/Date.cpp +++ b/src/Date.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // task - a command line task list manager. // -// Copyright 2006 - 2010, Paul Beckingham. +// Copyright 2006 - 2010, Paul Beckingham, Federico Hernandez. // All rights reserved. // // This program is free software; you can redistribute it and/or modify it under diff --git a/src/Date.h b/src/Date.h index 7f7f22914..27d542f69 100644 --- a/src/Date.h +++ b/src/Date.h @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // task - a command line task list manager. // -// Copyright 2006 - 2010, Paul Beckingham. +// Copyright 2006 - 2010, Paul Beckingham, Federico Hernandez. // All rights reserved. // // This program is free software; you can redistribute it and/or modify it under diff --git a/src/command.cpp b/src/command.cpp index 432fbf734..e4737dafa 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // task - a command line task list manager. // -// Copyright 2006 - 2010, Paul Beckingham. +// Copyright 2006 - 2010, Paul Beckingham, Federico Hernandez. // All rights reserved. // // This program is free software; you can redistribute it and/or modify it under @@ -490,7 +490,7 @@ int handleVersion (std::string &outs) #endif << std::endl - << "Copyright (C) 2006 - 2010, P. Beckingham." + << "Copyright (C) 2006 - 2010, P. Beckingham, F. Hernandez." << std::endl << disclaimer.render () << link.render () diff --git a/src/report.cpp b/src/report.cpp index ca0198a23..aaa719df4 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // task - a command line task list manager. // -// Copyright 2006 - 2010, Paul Beckingham. +// Copyright 2006 - 2010, Paul Beckingham, Federico Hernandez. // All rights reserved. // // This program is free software; you can redistribute it and/or modify it under From e53ba8110b960005dd03d8460fff28d463bf864f Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 14:42:36 -0500 Subject: [PATCH 21/27] Enhancement - Path integration - Implemented Path::operator (std::string) const, to provide an automatic cast to std::string for any Path, File or Directory. - Made use of new cast in various code. - Changed use of spaces in atoi () calls. - Switched from std::string::data () to std::string::c_str () calls. --- src/Config.cpp | 2 +- src/Context.cpp | 12 ++++++------ src/Path.cpp | 6 ++++++ src/Path.h | 2 ++ src/TDB.cpp | 2 +- src/report.cpp | 12 ++++++------ src/tests/directory.t.cpp | 5 ++++- src/tests/file.t.cpp | 5 ++++- src/tests/path.t.cpp | 7 +++++-- 9 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index 0648d56d3..eec6b148f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -321,7 +321,7 @@ void Config::parse (const std::string& input, int nest /* = 1 */) if (included.is_absolute ()) { if (included.readable ()) - this->load (included.data, nest + 1); + this->load (included, nest + 1); else throw std::string ("Could not read include file '") + included.data + "'"; } diff --git a/src/Context.cpp b/src/Context.cpp index 3d3d83494..15ac70e39 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -125,7 +125,7 @@ void Context::initialize () // init TDB. tdb.clear (); std::vector all; - split (all, location.data, ','); + split (all, location, ','); foreach (path, all) tdb.location (*path); } @@ -372,7 +372,7 @@ void Context::loadCorrectConfigFile () file_override = *arg; rc = File (arg->substr (3)); - home = rc.data; + home = rc; std::string::size_type last_slash = rc.data.rfind ("/"); if (last_slash != std::string::npos) home = rc.data.substr (0, last_slash); @@ -388,7 +388,7 @@ void Context::loadCorrectConfigFile () // Load rc file. config.clear (); // Dump current values. config.setDefaults (); // Add in the custom reports. - config.load (rc.data); // Load new file. + config.load (rc); // Load new file. if (config.get ("data.location") != "") data = Directory (config.get ("data.location")); @@ -417,19 +417,19 @@ void Context::loadCorrectConfigFile () + rc.data + " created, so task can proceed?")) { - config.createDefaultRC (rc.data, data.data); + config.createDefaultRC (rc, data); } else throw std::string ("Cannot proceed without rc file."); } // Create data location, if necessary. - config.createDefaultData (data.data); + config.createDefaultData (data); // Load rc file. config.clear (); // Dump current values. config.setDefaults (); // Add in the custom reports. - config.load (rc.data); // Load new file. + config.load (rc); // Load new file. // Apply overrides of type: "rc.name:value", or "rc.name=value". std::vector filtered; diff --git a/src/Path.cpp b/src/Path.cpp index 8bcd41342..b6613a0b8 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -65,6 +65,12 @@ Path& Path::operator= (const Path& other) return *this; } +//////////////////////////////////////////////////////////////////////////////// +Path::operator std::string () const +{ + return data; +} + //////////////////////////////////////////////////////////////////////////////// std::string Path::name () const { diff --git a/src/Path.h b/src/Path.h index 5f1ba2c1e..b934d0ba3 100644 --- a/src/Path.h +++ b/src/Path.h @@ -39,6 +39,8 @@ public: virtual ~Path (); Path& operator= (const Path&); + operator std::string () const; + std::string name () const; std::string parent () const; std::string extension () const; diff --git a/src/TDB.cpp b/src/TDB.cpp index c231d86ec..c2b9cabb7 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -114,7 +114,7 @@ void TDB::location (const std::string& path) path + "' does not exist, or is not readable and writable."; - mLocations.push_back (Location (d.data)); + mLocations.push_back (Location (d)); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/report.cpp b/src/report.cpp index aaa719df4..f13018295 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1535,7 +1535,7 @@ int handleReportCalendar (std::string &outs) // task cal 2010 monthsToDisplay = 12; mFrom = 1; - yFrom = atoi( context.args[1].data()); + yFrom = atoi (context.args[1].c_str ()); } } else if (numberOfArgs == 3) { @@ -1547,15 +1547,15 @@ int handleReportCalendar (std::string &outs) else { // task cal 8 2010 monthsToDisplay = monthsPerLine; - mFrom = atoi( context.args[1].data()); - yFrom = atoi( context.args[2].data()); + mFrom = atoi (context.args[1].c_str ()); + yFrom = atoi (context.args[2].c_str ()); } } else if (numberOfArgs == 4) { // task cal 8 2010 y monthsToDisplay = 12; - mFrom = atoi( context.args[1].data()); - yFrom = atoi( context.args[2].data()); + mFrom = atoi (context.args[1].c_str ()); + yFrom = atoi (context.args[2].c_str ()); } int countDueDates = 0; @@ -1728,7 +1728,7 @@ int handleReportStats (std::string &outs) dataSize += undo.size (); std::vector undoTxns; - slurp (undo.data, undoTxns, false); + slurp (undo, undoTxns, false); int undoCount = 0; foreach (tx, undoTxns) if (tx->substr (0, 3) == "---") diff --git a/src/tests/directory.t.cpp b/src/tests/directory.t.cpp index 26418b969..d6e5cde56 100644 --- a/src/tests/directory.t.cpp +++ b/src/tests/directory.t.cpp @@ -33,7 +33,7 @@ Context context; int main (int argc, char** argv) { - UnitTest t (20); + UnitTest t (21); // Directory (const File&); // Directory (const Path&); @@ -55,6 +55,9 @@ int main (int argc, char** argv) Directory d5 = d4; t.is (d5.data, "/tmp/test_directory", "Directory::operator="); + // operator (std::string) const; + t.is ((std::string) d3, "/tmp", "Directory::operator (std::string) const"); + // virtual bool create (); t.ok (d5.create (), "Directory::create /tmp/test_directory"); t.ok (d5.exists (), "Directory::exists /tmp/test_directory"); diff --git a/src/tests/file.t.cpp b/src/tests/file.t.cpp index 905aa265e..af0b3b9a5 100644 --- a/src/tests/file.t.cpp +++ b/src/tests/file.t.cpp @@ -33,7 +33,7 @@ Context context; int main (int argc, char** argv) { - UnitTest t (5); + UnitTest t (6); File::write ("/tmp/file.t.txt", "This is a test\n"); File f6 ("/tmp/file.t.txt"); @@ -41,6 +41,9 @@ int main (int argc, char** argv) t.ok (f6.mode () & S_IRUSR, "File::mode /tmp/file.t.txt good"); t.ok (File::remove ("/tmp/file.t.txt"), "File::remove /tmp/file.t.txt good"); + // operator (std::string) const; + t.is ((std::string) f6, "/tmp/file.t.txt", "File::operator (std::string) const"); + t.ok (File::create ("/tmp/file.t.create"), "File::create /tmp/file.t.create good"); t.ok (File::remove ("/tmp/file.t.create"), "File::remove /tmp/file.t.create good"); diff --git a/src/tests/path.t.cpp b/src/tests/path.t.cpp index aaaed861b..462598fac 100644 --- a/src/tests/path.t.cpp +++ b/src/tests/path.t.cpp @@ -1,7 +1,7 @@ //////////////////////////////////////////////////////////////////////////////// // task - a command line task list manager. // -// Copyright 2006 - 2009, Paul Beckingham. +// Copyright 2006 - 2010, Paul Beckingham. // All rights reserved. // // This program is free software; you can redistribute it and/or modify it under @@ -33,7 +33,7 @@ Context context; int main (int argc, char** argv) { - UnitTest t (31); + UnitTest t (32); // Path (); Path p0; @@ -54,6 +54,9 @@ int main (int argc, char** argv) Path p3_copy (p3); t.is (p3.data, p3_copy.data, "Path::Path (Path&)"); + // operator (std::string) const; + t.is ((std::string) p3, "/tmp", "Path::operator (std::string) const"); + // std::string name () const; Path p4 ("/a/b/c/file.ext"); t.is (p4.name (), "file.ext", "/a/b/c/file.ext name is file.ext"); From 0b67dfa38c0afdf3dbf4bb8d1dbf6cfa8c419635 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 14:58:50 -0500 Subject: [PATCH 22/27] Review - Config defaults - Pass 1 of the review, identifying variables that may need to be changed. --- src/Config.cpp | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Config.cpp b/src/Config.cpp index eec6b148f..cefde2359 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -62,17 +62,17 @@ std::string Config::defaults = "annotation.details=2 # Level of verbosity for annotations in reports\n" "next=2 # How many tasks per project in next report\n" "bulk=2 # > 2 tasks considered 'a lot', for confirmation\n" - "nag=You have higher priority tasks. # Nag message to keep you honest\n" + "nag=You have higher priority tasks. # Nag message to keep you honest\n" // TODO "\n" "# Dates\n" "dateformat=m/d/Y # Preferred input and display date format\n" - "#reportdateformat=m/d/Y # Preferred input and display date format\n" - "weekstart=Sunday # Sunday or Monday only\n" - "displayweeknumber=yes # Show week numbers on calendar\n" + "#reportdateformat=m/d/Y # Preferred display date format for repors\n" + "weekstart=Sunday # Sunday or Monday only\n" // TODO + "displayweeknumber=yes # Show week numbers on calendar\n" // TODO "due=7 # Task is considered due in 7 days\n" "#calendar.details=yes # Calendar shows information for tasks w/due dates\n" - "#calendar.details.report=list # Report to use when showing task information in cal\n" - "#monthsperline=3 # Number of calendar months on a line\n" + "#calendar.details.report=list # Report to use when showing task information in cal\n" // TODO + "#monthsperline=3 # Number of calendar months on a line\n" // TODO "\n" "# Color controls.\n" "color=on # Enable color\n" @@ -101,21 +101,22 @@ std::string Config::defaults = "color.history.delete=on yellow # Color of deleted tasks in the history reports\n" "color.history.done=on green # Color of completed tasks in the history reports\n" "\n" + "# Shadow file support\n" "#shadow.file=/tmp/shadow.txt # Location of shadow file\n" "#shadow.command=list # Task command for shadow file\n" "#shadow.notify=on # Footnote when updated\n" "\n" "#default.project=foo # Default project for 'add' command\n" "#default.priority=M # Default priority for 'add' command\n" - "default.command=list # When no arguments are specified\n" + "default.command=list # When no arguments are specified\n" // TODO "\n" "_forcecolor=no # Forces color to be on, even for non TTY output\n" "blanklines=true # Use more whitespace in output\n" - "complete.all.projects=no # Include old project names in 'projects' command\n" - "complete.all.tags=no # Include old tag names in 'tags' command\n" + "complete.all.projects=no # Include old project names in 'projects' command\n" // TODO + "complete.all.tags=no # Include old tag names in 'tags' command\n" // TODO "debug=no # Display diagnostics\n" "fontunderline=yes # Uses underlines rather than -------\n" - "shell.prompt=task> # Prompt used by the shell command\n" + "shell.prompt=task> # Prompt used by the shell command\n" // TODO "\n" "# Import heuristics - alternate names for fields (comma-separated list of names)\n" "#import.synonym.bg=?\n" @@ -133,12 +134,13 @@ std::string Config::defaults = "#import.synonym.tags=?\n" "#import.synonym.uuid=?\n" "\n" + "# Aliases - alternate names for commands\n" "alias.rm=delete # Alias for the delete command\n" "\n" - "# Fields: id,uuid,project,priority,priority_long,entry,entry_time,\n" - "# start,entry_time,due,recur,recurrence_indicator,age,\n" - "# age_compact,active,tags,tag_indicator,description,\n" - "# description_only,end,end_time\n" + "# Fields: id,uuid,project,priority,priority_long,entry,entry_time,\n" // TODO + "# start,entry_time,due,recur,recurrence_indicator,age,\n" // TODO + "# age_compact,active,tags,tag_indicator,description,\n" // TODO + "# description_only,end,end_time\n" // TODO "# Description: This report is ...\n" "# Sort: due+,priority-,project+\n" "# Filter: pro:x pri:H +bug limit:10\n" From a8f03679ed964e46e0fd20dea779a50ef90977f4 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 16:05:28 -0500 Subject: [PATCH 23/27] Demo - color - Preparing a script for a movie demonstrating color. --- doc/misc/script-color.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 doc/misc/script-color.txt diff --git a/doc/misc/script-color.txt b/doc/misc/script-color.txt new file mode 100644 index 000000000..2d669ed09 --- /dev/null +++ b/doc/misc/script-color.txt @@ -0,0 +1,34 @@ + Hello. This is a demonstration of the + task program color capabilities coming + in version 1.9. + +task color The color command shows the various + supported colors. For this you will + need an xterm with 256-color support, + or an equivalent. + + This demo uses iTerm running on Snow + Leopard. + +task add Prepare 1.9 for release Let's create a few tasks, to illustrate +task add Update the various docs the features. Five should be enough. +task add Run the regression tests +task add Make the packages +task add Upload to distributions + +--- NOTES + +16-color mode +upgrade +blending +alternate lines + +--- NOTES + +task ls Okay, let's color any tasks that + mention tests a nice medium blue. + +echo 'color.keyword.test=color23' >> ~/.taskrc + + + From 720f28c09c7eb9d4d2bb1d58c2fe1f5724bb7985 Mon Sep 17 00:00:00 2001 From: Federico Hernandez Date: Sat, 16 Jan 2010 23:37:37 +0100 Subject: [PATCH 24/27] Clean up in Context.cpp --- src/Context.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 3d3d83494..683b08284 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -387,7 +387,6 @@ void Context::loadCorrectConfigFile () // Load rc file. config.clear (); // Dump current values. - config.setDefaults (); // Add in the custom reports. config.load (rc.data); // Load new file. if (config.get ("data.location") != "") @@ -426,10 +425,11 @@ void Context::loadCorrectConfigFile () // Create data location, if necessary. config.createDefaultData (data.data); + // TODO find out why this was done twice - see tw #355 // Load rc file. - config.clear (); // Dump current values. - config.setDefaults (); // Add in the custom reports. - config.load (rc.data); // Load new file. + //config.clear (); // Dump current values. + //config.setDefaults (); // Add in the custom reports. + //config.load (rc.data); // Load new file. // Apply overrides of type: "rc.name:value", or "rc.name=value". std::vector filtered; From abffaa184b37dfe62c6ba631c9dc7f9daac74dd5 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 17:45:45 -0500 Subject: [PATCH 25/27] Enhancement - Path integration - Obsoleted util.cpp spit, slurp calls --- src/TDB.cpp | 21 ++++----- src/edit.cpp | 6 +-- src/import.cpp | 3 +- src/report.cpp | 2 +- src/tests/stringtable.t.cpp | 3 +- src/tests/util.t.cpp | 4 -- src/util.cpp | 86 ------------------------------------- src/util.h | 4 -- 8 files changed, 19 insertions(+), 110 deletions(-) diff --git a/src/TDB.cpp b/src/TDB.cpp index c2b9cabb7..f2c8d4a4a 100644 --- a/src/TDB.cpp +++ b/src/TDB.cpp @@ -36,6 +36,7 @@ #include "util.h" #include "TDB.h" #include "Directory.h" +#include "File.h" #include "Table.h" #include "Timer.h" #include "Color.h" @@ -513,7 +514,7 @@ void TDB::undo () // load undo.data std::vector u; - slurp (undoFile, u); + File::read (undoFile, u); if (u.size () < 3) throw std::string ("There are no recorded transactions to undo."); @@ -645,7 +646,7 @@ void TDB::undo () // load pending.data std::vector p; - slurp (pendingFile, p); + File::read (pendingFile, p); // is 'current' in pending? foreach (task, p) @@ -667,15 +668,15 @@ void TDB::undo () } // Rewrite files. - spit (pendingFile, p); - spit (undoFile, u); + File::write (pendingFile, p); + File::write (undoFile, u); return; } } // load completed.data std::vector c; - slurp (completedFile, c); + File::read (completedFile, c); // is 'current' in completed? foreach (task, c) @@ -691,17 +692,17 @@ void TDB::undo () { c.erase (task); p.push_back (prior); - spit (completedFile, c); - spit (pendingFile, p); - spit (undoFile, u); + File::write (completedFile, c); + File::write (pendingFile, p); + File::write (undoFile, u); std::cout << "Modified task reverted." << std::endl; context.debug ("TDB::undo - task belongs in pending.data"); } else { *task = prior; - spit (completedFile, c); - spit (undoFile, u); + File::write (completedFile, c); + File::write (undoFile, u); std::cout << "Modified task reverted." << std::endl; context.debug ("TDB::undo - task belongs in completed.data"); } diff --git a/src/edit.cpp b/src/edit.cpp index f24b920a0..14ec35bd0 100644 --- a/src/edit.cpp +++ b/src/edit.cpp @@ -533,7 +533,7 @@ void editFile (Task& task) // Format the contents, T -> text, write to a file. std::string before = formatTask (task); - spit (file.str (), before); + File::write (file.str (), before); // Determine correct editor: .taskrc:editor > $VISUAL > $EDITOR > vi std::string editor = context.config.get ("editor"); @@ -557,7 +557,7 @@ ARE_THESE_REALLY_HARMFUL: // Slurp file. std::string after; - slurp (file.str (), after, false); + File::read (file.str (), after); // Update task based on what can be parsed back out of the file, but only // if changes were made. @@ -584,7 +584,7 @@ ARE_THESE_REALLY_HARMFUL: // Preserve the edits. before = after; - spit (file.str (), before); + File::write (file.str (), before); if (confirm ("Task couldn't handle your edits. Would you like to try again?")) goto ARE_THESE_REALLY_HARMFUL; diff --git a/src/import.cpp b/src/import.cpp index 926fe135e..c6389ae63 100644 --- a/src/import.cpp +++ b/src/import.cpp @@ -28,6 +28,7 @@ #include #include #include +#include "File.h" #include "Date.h" #include "text.h" #include "util.h" @@ -1155,7 +1156,7 @@ int handleImport (std::string &outs) { // Load the file. std::vector all; - slurp (file, all, true); + File::read (file, all); std::vector lines; std::vector ::iterator it; diff --git a/src/report.cpp b/src/report.cpp index f13018295..1333d5cec 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -1728,7 +1728,7 @@ int handleReportStats (std::string &outs) dataSize += undo.size (); std::vector undoTxns; - slurp (undo, undoTxns, false); + File::read (undo, undoTxns); int undoCount = 0; foreach (tx, undoTxns) if (tx->substr (0, 3) == "---") diff --git a/src/tests/stringtable.t.cpp b/src/tests/stringtable.t.cpp index 625d283b7..b97c72811 100644 --- a/src/tests/stringtable.t.cpp +++ b/src/tests/stringtable.t.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -39,7 +40,7 @@ int main (int argc, char** argv) // Create a string file. std::string file = "./strings.xx-XX"; - spit (file, "# comment\n1 found"); + File::write (file, "# comment\n1 found"); t.is (access (file.c_str (), F_OK), 0, "strings.xx-XX created."); // Load the string file. diff --git a/src/tests/util.t.cpp b/src/tests/util.t.cpp index ecc6b796c..946eddbdc 100644 --- a/src/tests/util.t.cpp +++ b/src/tests/util.t.cpp @@ -514,10 +514,6 @@ int main (int argc, char** argv) // TODO const std::string uuid (); - // TODO bool slurp (const std::string&, std::vector &, bool trimLines = false); - // TODO bool slurp (const std::string&, std::string&, bool trimLines = false); - // TODO void spit (const std::string&, const std::string&); - // std::string taskDiff (const Task&, const Task&); Task left; left.set ("zero", "0"); diff --git a/src/util.cpp b/src/util.cpp index 69df5360b..a10e9289c 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -375,92 +375,6 @@ int flock (int fd, int operation) } #endif -//////////////////////////////////////////////////////////////////////////////// -bool slurp ( - const std::string& file, - std::vector & 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; -} - -//////////////////////////////////////////////////////////////////////////////// -bool slurp ( - const std::string& file, - std::string& contents, - bool trimLines /* = false */) -{ - contents = ""; - - std::ifstream in (file.c_str ()); - if (in.good ()) - { - std::string line; - while (getline (in, line)) - { - if (trimLines) line = trim (line); - contents += line + "\n"; - } - - in.close (); - return true; - } - - return false; -} - -//////////////////////////////////////////////////////////////////////////////// -void spit (const std::string& file, const std::string& contents) -{ - std::ofstream out (file.c_str ()); - if (out.good ()) - { - out << contents; - out.close (); - } - else - throw std::string ("Could not write file '") + file + "'"; // TODO i18n -} - -//////////////////////////////////////////////////////////////////////////////// -void spit ( - const std::string& file, - const std::vector & lines, - bool addNewlines /* = true */) -{ - std::ofstream out (file.c_str ()); - if (out.good ()) - { - foreach (line, lines) - { - out << *line; - - if (addNewlines) - out << "\n"; - } - - out.close (); - } - else - throw std::string ("Could not write file '") + file + "'"; // TODO i18n -} - //////////////////////////////////////////////////////////////////////////////// bool taskDiff (const Task& before, const Task& after) { diff --git a/src/util.h b/src/util.h index 3dbd8b2ea..50fef817e 100644 --- a/src/util.h +++ b/src/util.h @@ -70,10 +70,6 @@ const std::string uuid (); int flock (int, int); #endif -bool slurp (const std::string&, std::vector &, bool trimLines = false); -bool slurp (const std::string&, std::string&, bool trimLines = false); -void spit (const std::string&, const std::string&); -void spit (const std::string&, const std::vector &, bool addNewlines = true); bool taskDiff (const Task&, const Task&); std::string taskDifferences (const Task&, const Task&); std::string renderAttribute (const std::string&, const std::string&); From 5a886f6e5845decd600c060fc39d85a7e6852847 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sat, 16 Jan 2010 17:46:32 -0500 Subject: [PATCH 26/27] Unit Tests - Now sorts the results of a glob, so that the results are in a consistent order on all platforms. --- src/tests/directory.t.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tests/directory.t.cpp b/src/tests/directory.t.cpp index d6e5cde56..584a45935 100644 --- a/src/tests/directory.t.cpp +++ b/src/tests/directory.t.cpp @@ -25,6 +25,7 @@ // //////////////////////////////////////////////////////////////////////////////// +#include #include #include #include @@ -70,12 +71,14 @@ int main (int argc, char** argv) // std::vector list (); std::vector files = d5.list (); + std::sort (files.begin (), files.end ()); t.is ((int)files.size (), 2, "Directory::list 1 file"); t.is (files[0], "/tmp/test_directory/dir", "file[0] is /tmp/test_directory/dir"); t.is (files[1], "/tmp/test_directory/f0", "file[1] is /tmp/test_directory/f0"); // std::vector listRecursive (); files = d5.listRecursive (); + std::sort (files.begin (), files.end ()); t.is ((int)files.size (), 2, "Directory::list 1 file"); t.is (files[0], "/tmp/test_directory/dir/f1", "file is /tmp/test_directory/dir/f1"); t.is (files[1], "/tmp/test_directory/f0", "file is /tmp/test_directory/f0"); From 229a3d309c2ab26877223181bf44571bd6589d65 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 17 Jan 2010 00:02:17 -0500 Subject: [PATCH 27/27] Feature - #296 Setting configuration variables in .taskrc - Now supports 'task config name value', 'task config name ""', and 'task config name' to directly modify the .taskrc file. - Updated man page. - Added unit tests. - Modified existing config command to also display configuration variables that have no values. --- ChangeLog | 3 ++ doc/man/task.1 | 24 +++++++++-- src/Config.cpp | 3 ++ src/Config.h | 4 ++ src/command.cpp | 104 +++++++++++++++++++++++++++++++++++++++++++++--- src/report.cpp | 4 +- src/tests/rc.t | 19 ++++++++- 7 files changed, 149 insertions(+), 12 deletions(-) diff --git a/ChangeLog b/ChangeLog index 10f9b45c1..bcf84f960 100644 --- a/ChangeLog +++ b/ChangeLog @@ -26,6 +26,9 @@ information. + The 'config' command now complains about use of deprecated color names in your .taskrc file. + + Added feature #296, that allows the 'config' command to modify your .taskrc + settings directly, with the command 'task config ', or + 'task config ' to remove the setting. + Task now supports nested .taskrc files using the "include /path" directive. + The 'entry', 'start' and 'end' columns now have equivalents that include the time, and are called 'entry_time', 'start_time', and 'end_time', for use in diff --git a/doc/man/task.1 b/doc/man/task.1 index ef639bd5c..065c2c8d9 100644 --- a/doc/man/task.1 +++ b/doc/man/task.1 @@ -129,8 +129,22 @@ Displays all possible colors, or a sample. Shows the task version number .TP -.B config -Shows the current settings in the task configuration file. +.B config [name [value | '']] +Shows the current settings in the task configuration file. Also supports +directly modifying the .taskrc file. This command either modifies +the 'name' setting with a new value of 'value', or adds a new entry that +is equivalent to 'name=value': + + task config name value + +This command sets a blank value. This has the effect of suppressing any +default value: + + task config name '' + +Finally, this command removes any 'name=...' entry from the .taskrc file: + + task config name .TP .B help @@ -157,9 +171,13 @@ Shows all tasks matching the specified criteria that are completed. .TP -.B ls [tags] [attrs] [description] +.B minimal [tags] [attrs] [description] Provides a minimal listing of tasks with specified criteria. +.TP +.B ls [tags] [attrs] [description] +Provides a short listing of tasks with specified criteria. + .TP .B list [tags] [attrs] [description] Provides a more detailed listing of tasks with specified criteria. diff --git a/src/Config.cpp b/src/Config.cpp index cefde2359..6d4a2e0b2 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -271,7 +271,10 @@ void Config::load (const std::string& file, int nest /* = 1 */) // First time in, load the default values. if (nest == 1) + { setDefaults (); + original_file = File (file); + } // Read the file, then parse the contents. std::string contents; diff --git a/src/Config.h b/src/Config.h index 866ee5450..925b72c21 100644 --- a/src/Config.h +++ b/src/Config.h @@ -30,6 +30,7 @@ #include #include #include +#include "File.h" class Config : public std::map { @@ -60,6 +61,9 @@ public: std::string checkForDeprecatedColor (); +public: + File original_file; + private: static std::string defaults; }; diff --git a/src/command.cpp b/src/command.cpp index e4737dafa..cfadb7a36 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -505,6 +505,101 @@ int handleConfig (std::string &outs) { int rc = 0; std::stringstream out; + + // Support: + // task config name value # set name to value + // task config name "" # set name to blank + // task config name # remove name + if (context.args.size () >= 2) + { + std::string name = context.args[1]; + std::string value = ""; + + if (context.args.size () >= 3) + value = context.args[2]; + + if (name != "") + { + bool change = false; + + // Read .taskrc (or equivalent) + std::string contents; + File::read (context.config.original_file, contents); + + // task config name value + // task config name "" + if (context.args.size () >= 3) + { + // Find existing entry & overwrite + std::string::size_type pos = contents.find (name + "="); + if (pos != std::string::npos) + { + std::string::size_type eol = contents.find_first_of ("\r\f\n", pos); + if (eol == std::string::npos) + throw std::string ("Cannot find EOL after entry '") + name + "'"; + + if (confirm (std::string ("Are you sure you want to overwrite the value of '") + name + "' with '" + value + "'?")) + { + contents = contents.substr (0, pos) + + name + "=" + value + + contents.substr (eol); + change = true; + } + } + + // Not found, so append instead. + else + { + if (confirm (std::string ("Are you sure you want to add '") + name + "' with a value of '" + value + "'?")) + { + contents = contents + + "\n" + + name + "=" + value + + "\n"; + change = true; + } + } + } + + // task config name + else + { + // Remove name + std::string::size_type pos = contents.find (name + "="); + if (pos == std::string::npos) + throw std::string ("No entry named '") + name + "' found"; + + std::string::size_type eol = contents.find_first_of ("\r\f\n", pos); + if (eol == std::string::npos) + throw std::string ("Cannot find EOL after entry '") + name + "'"; + + if (confirm (std::string ("Are you sure you want to remove '") + name + "'?")) + { + contents = contents.substr (0, pos) + contents.substr (eol + 1); + change = true; + } + } + + // Write .taskrc (or equivalent) + if (change) + { + File::write (context.config.original_file, contents); + out << "Config file " + << context.config.original_file.data + << " modified." + << std::endl; + } + else + out << "No changes made." << std::endl; + } + else + throw std::string ("Specify the name of a config variable to modify."); + + outs = out.str (); + return rc; + } + + // No arguments - display config values instead. int width = context.getWidth (); std::vector all; @@ -534,12 +629,9 @@ int handleConfig (std::string &outs) foreach (i, all) { std::string value = context.config.get (*i); - if (value != "") - { - int row = table.addRow (); - table.addCell (row, 0, *i); - table.addCell (row, 1, value); - } + int row = table.addRow (); + table.addCell (row, 0, *i); + table.addCell (row, 1, value); } Color bold ("bold"); diff --git a/src/report.cpp b/src/report.cpp index 1333d5cec..69708aa15 100644 --- a/src/report.cpp +++ b/src/report.cpp @@ -187,8 +187,8 @@ int shortUsage (std::string &outs) table.addCell (row, 2, "Shows the task version number."); row = table.addRow (); - table.addCell (row, 1, "task config"); - table.addCell (row, 2, "Shows the task configuration."); + table.addCell (row, 1, "task config [name [value | '']]"); + table.addCell (row, 2, "Shows the task configuration, or can add, modify and remove settings."); row = table.addRow (); table.addCell (row, 1, "task help"); diff --git a/src/tests/rc.t b/src/tests/rc.t index 9b05079e4..38faee75b 100755 --- a/src/tests/rc.t +++ b/src/tests/rc.t @@ -29,7 +29,7 @@ use strict; use warnings; use File::Path; -use Test::More tests => 8; +use Test::More tests => 12; # Create the rc file, using rc.name:value. unlink 'foo.rc'; @@ -51,6 +51,23 @@ qx{echo 'y'|../task rc:foo.rc rc.data.location:foo}; ok (-r 'foo.rc', 'Created default rc file'); ok (-d 'foo', 'Created default data directory'); +# Add a setting. +qx{echo 'y'|../task rc:foo.rc config must_be_unique old}; +my $output = qx{../task rc:foo.rc config}; +like ($output, qr/^must_be_unique\s+old$/ms, 'config setting a new value'); + +qx{echo 'y'|../task rc:foo.rc config must_be_unique new}; +$output = qx{../task rc:foo.rc config}; +like ($output, qr/^must_be_unique\s+new$/ms, 'config overwriting an existing value'); + +qx{echo 'y'|../task rc:foo.rc config must_be_unique ''}; +$output = qx{../task rc:foo.rc config}; +like ($output, qr/^must_be_unique$/ms, 'config setting a blank value'); + +qx{echo 'y'|../task rc:foo.rc config must_be_unique}; +$output = qx{../task rc:foo.rc config}; +unlike ($output, qr/^must_be_unique/ms, 'config removing a value'); + rmtree 'foo', 0, 0; ok (!-r 'foo', 'Removed foo');