Merging in latest from upstream (TM/task:refs/heads/2.4.0)

* commit 'd8b7d914ac': (31 commits)
  TW-5
  TW-306
  Code Cleanup
  Documentation
  TW-285
  Unit Tests
  Unit Tests
  Code Cleanup
  TW-115
  TW-1257
  TW-1300
  Code Cleanup
  Bug
  ChangeLog
  TW-1301
  Bug TW-1302
  Diagnostics
  Bug TW-1254
  Documentation
  Bug TW-1295
  ...
This commit is contained in:
Renato Alves 2014-04-13 18:23:10 +00:00
commit 0b1732fcef
39 changed files with 1557 additions and 609 deletions

View file

@ -101,6 +101,8 @@ The following submitted code, packages or analysis, and deserve special thanks:
Marton Suranyi Marton Suranyi
Nicolas Appriou Nicolas Appriou
Jochen Sprickerhof Jochen Sprickerhof
Alexander Sulfrian
David Binderman
Thanks to the following, who submitted detailed bug reports and excellent Thanks to the following, who submitted detailed bug reports and excellent
suggestions: suggestions:
@ -203,3 +205,4 @@ suggestions:
jck jck
Michele Vetturi Michele Vetturi
Jeremiah Marks Jeremiah Marks
Profpatsch

View file

@ -1,21 +1,48 @@
2.4.0 () - 2.4.0 () -
Features - TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks
to Michele Vetturi).
- TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof).
- #1255 l10n translation utility improvements (thanks to Renato Alves). - #1255 l10n translation utility improvements (thanks to Renato Alves).
- #1473 Make TASK_RCDIR customizable (thanks to Elias Probst).
- #1486 Truncated sentence in task-sync(5) manpage (thanks to Jakub Wilk).
- #1487 `tasksh` segmentation fault (thanks to Hector Arciga).
- #1492 task show to display default values when appropriate (thanks to Renato - #1492 task show to display default values when appropriate (thanks to Renato
Alves). Alves).
- #1501 info report streamlining - partially implemented. - #1501 info report streamlining - partially implemented.
- #1503 build failure with musl libc due to undefined GLOB_BRACE and GLOB_TILDE
(thanks to Natanael Copa).
- #1508 Show command highlight configuration (thanks to Nicolas Appriou).
- #1511 sync init crashes if client certification file is empty or invalid
(thanks to Marton Suranyi).
- TW-5 color.due.today does not work (thanks to Max Muller).
- TW-115 allow "0day" durations for UDAs.
- TW-197 New virtual tag READY. - TW-197 New virtual tag READY.
- TW-255 'Mask' instead of 'iMask' shown in info report (thanks to Benjamin - TW-255 'Mask' instead of 'iMask' shown in info report (thanks to Benjamin
Weber) Weber)
- TW-261 Easy to create "not deletable" task (thanks to Jan Kunder). - TW-261 Easy to create "not deletable" task (thanks to Jan Kunder).
- TW-278 Cygwin throws warnings building mk_wcwidth() in wcwidth6.c. - TW-278 Cygwin throws warnings building mk_wcwidth() in wcwidth6.c.
- TW-285 DUETODAY doesn't give any output (thanks to Jostein Berntsen).
- TW-306 Wrong date format in burndown view (thanks to Michele Santullo).
- TW-1254 Calc command can segfault on negative numbers (thanks to Renato
Alves).
- TW-1255 New testing framework (thanks to Renato Alves). - TW-1255 New testing framework (thanks to Renato Alves).
- TW-1257 The 'Syncing with <host>:<port>' message ignores verbosity tokens.
- TW-1258 Portuguese Localization (thanks to Renato Alves). - TW-1258 Portuguese Localization (thanks to Renato Alves).
- TW-1260 New virtual tags YESTERDAY, TOMORROW. - TW-1260 New virtual tags YESTERDAY, TOMORROW.
- TW-1261 Migrate test bug.360.t to new unit testing framework (thanks to - TW-1261 Migrate test bug.360.t to new unit testing framework (thanks to
Renato Alves). Renato Alves).
- TW-1274 Map 'modification' attribute to 'modified' (thanks to jck). - TW-1274 Map 'modification' attribute to 'modified' (thanks to jck).
- TW-1282 incorrect URLs in man task-sync (thanks to Jeremiah Marks).
- TW-1288 Added missing locking for task modifications (thanks to Kosta H,
Ralph Bean, Adam Coddington).
- TW-1295 test/time.t fails on the last day of the month (thanks to Jakub
Wilk).
- TW-1296 make test/run_all exit with non-zero code if a test fail (thanks to
Jakub Wilk).
- TW-1300 _get could use return codes (thanks to Scott Kostyshak).
- TW-1301 Virtual tag +PENDING (thanks to Profpatsch).
- TW-1302 CmdShow.cpp:244: bad length in substr ? (thanks to David Binderman).
- Removed deprecated 'echo.command' setting, in favor of the 'header' and - Removed deprecated 'echo.command' setting, in favor of the 'header' and
'affected' verbosity tokens. 'affected' verbosity tokens.
- Removed deprecated 'edit.verbose' setting, in favor of the 'edit' verbosity - Removed deprecated 'edit.verbose' setting, in favor of the 'edit' verbosity
@ -29,20 +56,9 @@ Features
- Old-style color names including underscores are no longer supported. - Old-style color names including underscores are no longer supported.
- Removed priority counts from the 'projects' report. - Removed priority counts from the 'projects' report.
- New themes: dark-default-16.theme, dark-gray-blue-256.theme - New themes: dark-default-16.theme, dark-gray-blue-256.theme
- Added certificate verification to GnuTLS versions < 2.9.10 (thanks to Alexander
Bugs Sulfrian).
- TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks - Added certificate hostname verification (thanks to Alexander Sulfrian).
to Michele Vetturi).
- TD-45 Fix preprocessor define (thanks to Jochen Sprickerhof).
- TW-1282 incorrect URLs in man task-sync (thanks to Jeremiah Marks).
- #1511 sync init crashes if client certification file is empty or invalid
(thanks to Marton Suranyi).
- #1508 Show command highlight configuration (thanks to Nicolas Appriou).
- #1503 build failure with musl libc due to undefined GLOB_BRACE and GLOB_TILDE
(thanks to Natanael Copa)
- #1473 Make TASK_RCDIR customizable (thanks to Elias Probst).
- #1486 Truncated sentence in task-sync(5) manpage (thanks to Jakub Wilk).
- #1487 `tasksh` segmentation fault (thanks to Hector Arciga).
- Removed debugging code. - Removed debugging code.
------ current release --------------------------- ------ current release ---------------------------

View file

@ -13,6 +13,10 @@ General Statement
suggestions, testing and discussions have taken place there. It is also the suggestions, testing and discussions have taken place there. It is also the
quickest way to get help, or confirm a bug. quickest way to get help, or confirm a bug.
- Join https://answers.tasktools.org and help us by asking, answering and
voting on questions and answers, directly helping those who ask, and
indirectly helping those who search.
- Review documentation: there are man pages, online articles, tutorials and - Review documentation: there are man pages, online articles, tutorials and
so on, and these may contain errors, or they may not convey ideas in the so on, and these may contain errors, or they may not convey ideas in the
best way. Perhaps you can help improve it. Contact us - documentation is best way. Perhaps you can help improve it. Contact us - documentation is
@ -68,6 +72,8 @@ Deprecated Code
- Priorities in core. This will be migrated to become a UDA as soon as we - Priorities in core. This will be migrated to become a UDA as soon as we
have the right support in place for custom sorting. have the right support in place for custom sorting.
- Attribute modifiers.
New Code Needs New Code Needs
This is code that needs to be written, usually down at the C++ function/method This is code that needs to be written, usually down at the C++ function/method
level. level.
@ -95,7 +101,7 @@ New Code Needs
outside the core code, we want to make it be that way. outside the core code, we want to make it be that way.
- Take a look at: - Take a look at:
http://taskwarrior.org/versions/show/42 https://bug.tasktools.org/browse/EX
This 'extension' release is a collection of all the requested features that This 'extension' release is a collection of all the requested features that
lie outside of the core product, and will be implemented as external scripts lie outside of the core product, and will be implemented as external scripts
@ -112,9 +118,9 @@ Documentation Needed
- Tutorials - Tutorials
- Cookbook - Cookbook
This is all in the new git://tasktools.git/docs.git repository, where all the This is all in the new https://git.tasktools.org/projects/ST/repos/tw.org
documents are sourced as markdown. We welcome everyone with writing skills to repository, where all the documents are sourced as markdown. We welcome
help us improve this critical resource. everyone with writing skills to help us improve this critical resource.
Unit Tests Needed Unit Tests Needed
There are always more unit tests needed. More specifically, better unit tests There are always more unit tests needed. More specifically, better unit tests
@ -211,3 +217,4 @@ Current Codebase Condition
2012-05-12 Added general statement about how to contribute. 2012-05-12 Added general statement about how to contribute.
2013-09-09 Updated branch info. 2013-09-09 Updated branch info.
2014-01-19 Updated for 2.4.0. 2014-01-19 Updated for 2.4.0.
2014-04-13 Added answers.tasktools.org, corrected URLs.

5
NEWS
View file

@ -5,7 +5,8 @@ New Features in taskwarrior 2.4.0
- Removed deprecated commands 'push', 'pull' and 'merge'. - Removed deprecated commands 'push', 'pull' and 'merge'.
- Portuguese (por-PRT) localization. - Portuguese (por-PRT) localization.
- Better handling for deletion of recurring tasks. - Better handling for deletion of recurring tasks.
- New virtual tags: YESTERDAY, TOMORROW, READY. - New virtual tags: YESTERDAY, TOMORROW, READY, PENDING, COMPLETED, DELETED.
- The '_get' command properly uses exit codes.
New commands in taskwarrior 2.4.0 New commands in taskwarrior 2.4.0
@ -13,6 +14,8 @@ New commands in taskwarrior 2.4.0
New configuration options in taskwarrior 2.4.0 New configuration options in taskwarrior 2.4.0
- The 'taskd.trust' setting is now a tri-state, supporting values 'strict',
'ignore hostname' and 'allow all', for server certificate validation.
- New themes: dark-default-16.theme, dark-gray-blue-256.theme - New themes: dark-default-16.theme, dark-gray-blue-256.theme
Newly deprecated features in taskwarrior 2.4.0 Newly deprecated features in taskwarrior 2.4.0

View file

@ -25,7 +25,11 @@ Your contributions are especially welcome. Whether it comes in the form of
code patches, ideas, discussion, bug reports, encouragement or criticism, your code patches, ideas, discussion, bug reports, encouragement or criticism, your
input is needed. input is needed.
Please send your support questions and code patches to: For support options, take a look at:
http://taskwarrior.org/support
Please send your code patches to:
support@taskwarrior.org support@taskwarrior.org

View file

@ -537,6 +537,9 @@ from tasks, or the system. Supported DOM references are:
Note that the 'rc.<name>' reference may need to be escaped using '--' to prevent Note that the 'rc.<name>' reference may need to be escaped using '--' to prevent
the reference from being interpreted as an override. the reference from being interpreted as an override.
Note that if the DOM reference is not valid, or the reference evaluates to a
missing value, the command exits with 1.
.SH ATTRIBUTES AND METADATA .SH ATTRIBUTES AND METADATA
.TP .TP
@ -588,6 +591,9 @@ are:
UNTIL Matches if the task expires UNTIL Matches if the task expires
WAITING Matches if the task is waiting WAITING Matches if the task is waiting
ANNOTATED Matches if the task has annotations ANNOTATED Matches if the task has annotations
PENDING Matches if the task has pending status
COMPLETED Matches if the task has completed status
DELETED Matches if the task has deleted status
You can use +BLOCKED to filter blocked tasks, or -BLOCKED for unblocked tasks. You can use +BLOCKED to filter blocked tasks, or -BLOCKED for unblocked tasks.
Similarly, -BLOCKED is equivalent to +UNBLOCKED. Similarly, -BLOCKED is equivalent to +UNBLOCKED.

View file

@ -253,7 +253,7 @@ control specific occasions when output is generated. This list may contain:
edit Used the verbose template for the 'edit' command edit Used the verbose template for the 'edit' command
special Feedback when applying special tags special Feedback when applying special tags
project Feedback about project status changes project Feedback about project status changes
sync Feedback about the need for sync sync Feedback about sync
Note that the "on" setting is equivalent to all the tokens being specified, Note that the "on" setting is equivalent to all the tokens being specified,
and the "nothing" setting is equivalent to none of the tokens being specified. and the "nothing" setting is equivalent to none of the tokens being specified.
@ -1358,11 +1358,13 @@ using a self-signed certificate. Optional.
.RE .RE
.TP .TP
.B taskd.trust=yes|no .B taskd.trust=strict|ignore hostname|allow all
.RS .RS
If you do not specify a CA certificate when your Taskserver is using a self- This settings allows you to override the trust level when server certificates
signed certificate, you can override the certificate validation by setting this are validated. With "allow all", the server certificate is trusted
value to 'yes'. Default is not to trust a server certificate. automatically. With "ignore hostname", the server certificate is verified but
the hostname is ignored. With "strict", the server certificate is verified.
Default is "strict", which requires full validation.
.RE .RE
.TP .TP

View file

@ -157,9 +157,9 @@ const std::string DOM::get (const std::string& name, const Task& task)
std::string canonical; std::string canonical;
// <attr> // <attr>
if (name == "id") return format (task.id); if (task.size () && name == "id") return format (task.id);
else if (name == "urgency") return format (task.urgency_c ()); else if (task.size () && name == "urgency") return format (task.urgency_c ());
else if (A3::is_attribute (name, canonical)) return task.get (canonical); else if (task.size () && A3::is_attribute (name, canonical)) return task.get (canonical);
// <id>.<name> // <id>.<name>
if (n.getInt (id)) if (n.getInt (id))

View file

@ -78,15 +78,8 @@ static bool isDay (const std::string& name, int& i)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static bool leapYear (int year) static bool leapYear (int year)
{ {
bool ly = false; return ((!(year % 4)) && (year % 100)) ||
(!(year % 400));
// (year % 4 == 0) && (year % 100 !=0) OR
// (year % 400 == 0)
// are leapyears
if (((!(year % 4)) && (year % 100)) || (!(year % 400))) ly = true;
return ly;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -99,6 +92,39 @@ static int daysInMonth (int year, int month)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// now = current date/time.
// today = previous midnight.
// sod = previous midnight.
// yesterday = 2nd previous midnight.
// tomorrow = next midnight.
// eod = next midnight.
// <day> = midnight at start of next <day>.
// <month> = midnight on the 1st of <month>.
// soy = midnight on January 1st, <year>.
// eoy = midnight on December 31st, <year>.
// socm = midnight on the 1st of current month.
// som = midnight on the 1st of next month.
// eom = midnight on the 1st of the next month.
// eocm = midnight on the 1st of the next month.
// sow =
// eow =
// eocw =
// socw =
// soww =
// eoww =
// soq =
// eoq =
// later = midnight, Jan 18th, 2038.
// someday = midnight, Jan 18th, 2038.
// easter =
// eastermonday =
// ascension =
// pentecost =
// goodfriday =
// midsommar =
// midsommarafton =
// Nth =
bool namedDates (const std::string& name, Variant& value) bool namedDates (const std::string& name, Variant& value)
{ {
time_t now = time (NULL); time_t now = time (NULL);
@ -209,6 +235,69 @@ bool namedDates (const std::string& name, Variant& value)
value = Variant (mktime (t), Variant::type_date); value = Variant (mktime (t), Variant::type_date);
} }
else if (name == "sow")
{
/*
Date sow (_t);
sow -= (dayOfWeek () * 86400);
return Date (sow.month (), sow.day (), sow.year (), 0, 0, 0);
*/
}
else if (name == "eow" || name == "eocw")
{
/*
if (found == "eow" || found == "eoww")
dow = 5;
*/
}
else if (name == "socw")
{
/*
Date sow (_t);
sow -= (dayOfWeek () * 86400);
return Date (sow.month (), sow.day (), sow.year (), 0, 0, 0);
*/
}
else if (name == "soww")
{
/*
Date sow (_t);
sow -= (dayOfWeek () * 86400);
return Date (sow.month (), sow.day (), sow.year (), 0, 0, 0);
*/
}
else if (name == "eoww")
{
/*
if (found == "eow" || found == "eoww")
dow = 5;
*/
}
else if (name == "soq" || name == "eoq")
{
struct tm* t = localtime (&now);
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mon += 3 - (t->tm_mon % 3);
if (t->tm_mon > 11)
{
t->tm_mon -= 12;
++t->tm_year;
}
// TODO eoq: should be 24:00:00
// t->tm_mday = daysInMonth (t->tm_year + 1900, t->tm_mon + 1);
t->tm_mday = 1;
value = Variant (mktime (t), Variant::type_date);
}
else if (name == "later" || name == "someday") else if (name == "later" || name == "someday")
{ {
struct tm* t = localtime (&now); struct tm* t = localtime (&now);
@ -256,19 +345,97 @@ bool namedDates (const std::string& name, Variant& value)
value = Variant (mktime (t), Variant::type_date); value = Variant (mktime (t), Variant::type_date);
} }
// TODO else if (name == "midsommar")
{
/* /*
{s,e}o{w,q,ww,cw} for (int midsommar = 20; midsommar <= 26; midsommar++)
{
midsommar Date then (6, midsommar, today.year ());
midsommarafton if (6 == then.dayOfWeek ())
23rd {
_t = then._t;
return true;
}
}
*/ */
}
else if (name == "midsommarafton")
{
/*
for (int midsommar = 19; midsommar <= 25; midsommar++)
{
Date then (6, midsommar, today.year ());
if (5 == then.dayOfWeek ())
{
_t = then._t;
return true;
}
}
*/
}
// 1st
// 2nd
// 3rd
else if (name == "????")
{
/*
int number;
std::string ordinal;
if (isdigit (in[1]))
{
number = atoi (in.substr (0, 2).c_str ());
ordinal = lowerCase (in.substr (2));
}
else
{
number = atoi (in.substr (0, 2).c_str ());
ordinal = lowerCase (in.substr (1));
}
// Sanity check.
if (number <= 31)
{
if (ordinal == "st" ||
ordinal == "nd" ||
ordinal == "rd" ||
ordinal == "th")
{
int m = today.month ();
int d = today.day ();
int y = today.year ();
// If it is this month.
if (d < number &&
number <= Date::daysInMonth (m, y))
{
Date then (m, number, y);
_t = then._t;
return true;
}
do
{
m++;
if (m > 12)
{
m = 1;
y++;
}
}
while (number > Date::daysInMonth (m, y));
Date then (m, number, y);
_t = then._t;
return true;
}
}
*/
}
// Constants.
else if (name == "pi") { value = Variant (3.14159165); }
else if (name == "true") { value = Variant (true); }
else if (name == "false") { value = Variant (false); }
else else
return false; return false;

View file

@ -146,10 +146,6 @@ void E9::eval (const Task& task, std::vector <Arg>& value_stack)
else if (arg->_raw == "=") operator_equal (result, left, right, case_sensitive); else if (arg->_raw == "=") operator_equal (result, left, right, case_sensitive);
else if (arg->_raw == "~") operator_match (result, left, right, case_sensitive, task); else if (arg->_raw == "~") operator_match (result, left, right, case_sensitive, task);
else if (arg->_raw == "!~") operator_nomatch (result, left, right, case_sensitive, task); else if (arg->_raw == "!~") operator_nomatch (result, left, right, case_sensitive, task);
else if (arg->_raw == "*") operator_multiply (result, left, right);
else if (arg->_raw == "/") operator_divide (result, left, right);
else if (arg->_raw == "+") operator_add (result, left, right);
else if (arg->_raw == "-") operator_subtract (result, left, right);
else if (arg->_raw == "_hastag_") operator_hastag (result, right, false, task); else if (arg->_raw == "_hastag_") operator_hastag (result, right, false, task);
else if (arg->_raw == "_notag_") operator_hastag (result, right, true, task); else if (arg->_raw == "_notag_") operator_hastag (result, right, true, task);
else else
@ -252,8 +248,6 @@ void E9::operator_not (Arg& result, Arg& right)
result._value = "false"; result._value = "false";
else else
result._value = "true"; result._value = "true";
// std::cout << "# <operator_not> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -261,8 +255,6 @@ void E9::operator_negate (Arg& result, Arg& right)
{ {
result = coerce (right, Arg::type_number); result = coerce (right, Arg::type_number);
result._value = format (- strtod (result._value.c_str (), NULL)); result._value = format (- strtod (result._value.c_str (), NULL));
// std::cout << "# <operator_negate> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -276,8 +268,6 @@ void E9::operator_and (Arg& result, Arg& left, Arg& right)
{ {
result._value = "true"; result._value = "true";
} }
// std::cout << "# " << left << " <operator_and> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -291,8 +281,6 @@ void E9::operator_or (Arg& result, Arg& left, Arg& right)
{ {
result._value = "true"; result._value = "true";
} }
// std::cout << "# " << left << " <operator_or> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -309,8 +297,6 @@ void E9::operator_xor (Arg& result, Arg& left, Arg& right)
{ {
result._value = "true"; result._value = "true";
} }
// std::cout << "# " << left << " <operator_xor> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -373,8 +359,6 @@ void E9::operator_lt (Arg& result, Arg& left, Arg& right)
} }
result._type = Arg::type_bool; result._type = Arg::type_bool;
// std::cout << "# " << left << " <operator_lt> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -438,8 +422,6 @@ void E9::operator_lte (Arg& result, Arg& left, Arg& right)
} }
result._type = Arg::type_bool; result._type = Arg::type_bool;
// std::cout << "# " << left << " <operator_lte> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -503,8 +485,6 @@ void E9::operator_gte (Arg& result, Arg& left, Arg& right)
} }
result._type = Arg::type_bool; result._type = Arg::type_bool;
// std::cout << "# " << left << " <operator_gte> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -567,8 +547,6 @@ void E9::operator_gt (Arg& result, Arg& left, Arg& right)
} }
result._type = Arg::type_bool; result._type = Arg::type_bool;
// std::cout << "# " << left << " <operator_gt> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -582,8 +560,6 @@ void E9::operator_inequal (
result._value = result._value == "false" result._value = result._value == "false"
? "true" ? "true"
: "false"; : "false";
// std::cout << "# " << left << " <operator_inequal> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -656,8 +632,6 @@ void E9::operator_equal (
? "true" ? "true"
: "false"; : "false";
} }
// std::cout << "# " << left << " <operator_equal> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -696,8 +670,6 @@ void E9::operator_match (
} }
else else
result._value = "false"; result._value = "false";
// std::cout << "# " << left << " <operator_match> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -735,32 +707,6 @@ void E9::operator_nomatch (
} }
} }
} }
// std::cout << "# " << left << " <operator_nomatch> " << right << " --> " << result << "\n";
}
////////////////////////////////////////////////////////////////////////////////
void E9::operator_multiply (Arg& result, Arg& left, Arg& right)
{
// std::cout << "# " << left << " <operator_multiply> " << right << " --> " << result << "\n";
}
////////////////////////////////////////////////////////////////////////////////
void E9::operator_divide (Arg& result, Arg& left, Arg& right)
{
// std::cout << "# " << left << " <operator_divide> " << right << " --> " << result << "\n";
}
////////////////////////////////////////////////////////////////////////////////
void E9::operator_add (Arg& result, Arg& left, Arg& right)
{
// std::cout << "# " << left << " <operator_add> " << right << " --> " << result << "\n";
}
////////////////////////////////////////////////////////////////////////////////
void E9::operator_subtract (Arg& result, Arg& left, Arg& right)
{
// std::cout << "# " << left << " <operator_subtract> " << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -776,8 +722,6 @@ void E9::operator_hastag (
result._value = invert ? "false" : "true"; result._value = invert ? "false" : "true";
else else
result._value = invert ? "true" : "false"; result._value = invert ? "true" : "false";
// std::cout << "# tags" << (invert ? " <operator_notag> " : " <operator_hastag> ") << right << " --> " << result << "\n";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -61,10 +61,6 @@ private:
void operator_equal (Arg&, Arg&, Arg&, bool); void operator_equal (Arg&, Arg&, Arg&, bool);
void operator_match (Arg&, Arg&, Arg&, bool, const Task&); void operator_match (Arg&, Arg&, Arg&, bool, const Task&);
void operator_nomatch (Arg&, Arg&, Arg&, bool, const Task&); void operator_nomatch (Arg&, Arg&, Arg&, bool, const Task&);
void operator_multiply (Arg&, Arg&, Arg&);
void operator_divide (Arg&, Arg&, Arg&);
void operator_add (Arg&, Arg&, Arg&);
void operator_subtract (Arg&, Arg&, Arg&);
void operator_hastag (Arg&, Arg&, bool, const Task&); void operator_hastag (Arg&, Arg&, bool, const Task&);
const Arg coerce (const Arg&, const Arg::type); const Arg coerce (const Arg&, const Arg::type);

View file

@ -28,6 +28,7 @@
#include <time.h> #include <time.h>
#include <Eval.h> #include <Eval.h>
////////////////////////////////////////////////////////////////////////////////
// Supported operators, borrowed from C++, particularly the precedence. // Supported operators, borrowed from C++, particularly the precedence.
// Note: table is sorted by length of operator string, so searches match // Note: table is sorted by length of operator string, so searches match
// longest first. // longest first.
@ -40,43 +41,63 @@ static struct
} operators[] = } operators[] =
{ {
// Operator Precedence Type Associativity // Operator Precedence Type Associativity
{ "and", 5, 'b', 'l' }, // Conjunction
{ "xor", 4, 'b', 'l' }, // Disjunction
{ "or", 3, 'b', 'l' }, // Disjunction
{ "<=", 10, 'b', 'l' }, // Less than or equal
{ ">=", 10, 'b', 'l' }, // Greater than or equal
{ "!~", 9, 'b', 'l' }, // Regex non-match
{ "!=", 9, 'b', 'l' }, // Inequal
{ "==", 9, 'b', 'l' }, // Equal
{ "=", 9, 'b', 'l' }, // Equal
{ "^", 16, 'b', 'r' }, // Exponent { "^", 16, 'b', 'r' }, // Exponent
{ ">", 10, 'b', 'l' }, // Greater than
{ "~", 9, 'b', 'l' }, // Regex match
{ "!", 15, 'u', 'r' }, // Not
{ "_hastag_", 9, 'b', 'l'}, // +tag [Pseudo-op] { "!", 15, 'u', 'r' }, // Unary not
{ "_notag_", 9, 'b', 'l'}, // -tag [Pseudo-op] { "_neg_", 15, 'u', 'r' }, // Unary minus
{ "_pos_", 15, 'u', 'r' }, // Unary plus
{ "_hastag_", 14, 'b', 'l'}, // +tag [Pseudo-op]
{ "_notag_", 14, 'b', 'l'}, // -tag [Pseudo-op]
// { "-", 15, 'u', 'r' }, // Unary minus
{ "*", 13, 'b', 'l' }, // Multiplication { "*", 13, 'b', 'l' }, // Multiplication
{ "/", 13, 'b', 'l' }, // Division { "/", 13, 'b', 'l' }, // Division
{ "%", 13, 'b', 'l' }, // Modulus { "%", 13, 'b', 'l' }, // Modulus
{ "+", 12, 'b', 'l' }, // Addition { "+", 12, 'b', 'l' }, // Addition
{ "-", 12, 'b', 'l' }, // Subtraction { "-", 12, 'b', 'l' }, // Subtraction
{ "<=", 10, 'b', 'l' }, // Less than or equal
{ ">=", 10, 'b', 'l' }, // Greater than or equal
{ ">", 10, 'b', 'l' }, // Greater than
{ "<", 10, 'b', 'l' }, // Less than { "<", 10, 'b', 'l' }, // Less than
{ "=", 9, 'b', 'l' }, // Equal
{ "==", 9, 'b', 'l' }, // Equal
{ "!=", 9, 'b', 'l' }, // Inequal
{ "~", 8, 'b', 'l' }, // Regex match
{ "!~", 8, 'b', 'l' }, // Regex non-match
{ "and", 5, 'b', 'l' }, // Conjunction
{ "or", 4, 'b', 'l' }, // Disjunction
{ "xor", 3, 'b', 'l' }, // Disjunction
{ "(", 0, 'b', 'l' }, // Precedence start { "(", 0, 'b', 'l' }, // Precedence start
{ ")", 0, 'b', 'l' }, // Precedence end { ")", 0, 'b', 'l' }, // Precedence end
}; };
#define NUM_OPERATORS (sizeof (operators) / sizeof (operators[0])) #define NUM_OPERATORS (sizeof (operators) / sizeof (operators[0]))
////////////////////////////////////////////////////////////////////////////////
// Built-in support for some named constants.
static bool namedConstants (const std::string& name, Variant& value)
{
if (name == "true") value = Variant (true);
else if (name == "false") value = Variant (false);
else if (name == "pi") value = Variant (3.14159165);
else
return false;
return true;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Eval::Eval () Eval::Eval ()
: _ambiguity (true) : _ambiguity (true)
, _debug (false) , _debug (false)
{ {
addSource (namedConstants);
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -100,10 +121,15 @@ void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const
std::string token; std::string token;
Lexer::Type type; Lexer::Type type;
while (l.token (token, type)) while (l.token (token, type))
{
tokens.push_back (std::pair <std::string, Lexer::Type> (token, type)); tokens.push_back (std::pair <std::string, Lexer::Type> (token, type));
// Parse for syntax checking and operator replacement.
infixParse (tokens);
if (_debug) if (_debug)
std::cout << "# token infix '" << token << "' " << Lexer::type_name (type) << "\n"; {
std::vector <std::pair <std::string, Lexer::Type> >::iterator i;
for (i = tokens.begin (); i != tokens.end (); ++i)
std::cout << "# token infix '" << i->first << "' " << Lexer::type_name (i->second) << "\n";
} }
// Convert infix --> postfix. // Convert infix --> postfix.
@ -163,7 +189,7 @@ void Eval::evaluatePostfixStack (
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token; std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token;
for (token = tokens.begin (); token != tokens.end (); ++token) for (token = tokens.begin (); token != tokens.end (); ++token)
{ {
// Unary operator. // Unary operators.
if (token->second == Lexer::typeOperator && if (token->second == Lexer::typeOperator &&
token->first == "!") token->first == "!")
{ {
@ -171,6 +197,18 @@ void Eval::evaluatePostfixStack (
values.pop_back (); values.pop_back ();
values.push_back (! right); values.push_back (! right);
} }
else if (token->second == Lexer::typeOperator &&
token->first == "_neg_")
{
Variant right = values.back ();
values.pop_back ();
values.push_back (Variant (0) - right);
}
else if (token->second == Lexer::typeOperator &&
token->first == "_pos_")
{
// NOP?
}
// Binary operators. // Binary operators.
else if (token->second == Lexer::typeOperator) else if (token->second == Lexer::typeOperator)
@ -210,6 +248,8 @@ void Eval::evaluatePostfixStack (
values.push_back (left); values.push_back (left);
} }
// Literals and identifiers.
else else
{ {
Variant v (token->first); Variant v (token->first);
@ -230,6 +270,7 @@ void Eval::evaluatePostfixStack (
case Lexer::typeIdentifier: case Lexer::typeIdentifier:
{ {
bool found = false;
std::vector <bool (*)(const std::string&, Variant&)>::const_iterator source; std::vector <bool (*)(const std::string&, Variant&)>::const_iterator source;
for (source = _sources.begin (); source != _sources.end (); ++source) for (source = _sources.begin (); source != _sources.end (); ++source)
{ {
@ -237,12 +278,13 @@ void Eval::evaluatePostfixStack (
{ {
if (_debug) if (_debug)
std::cout << "# [" << values.size () << "] eval source '" << token->first << "' --> '" << (std::string) v << "'\n"; std::cout << "# [" << values.size () << "] eval source '" << token->first << "' --> '" << (std::string) v << "'\n";
found = true;
break; break;
} }
} }
// An identifier that fails lookup is a string. // An identifier that fails lookup is a string.
if (source == _sources.end ()) if (!found)
{ {
v.cast (Variant::type_string); v.cast (Variant::type_string);
if (_debug) if (_debug)
@ -279,6 +321,373 @@ void Eval::evaluatePostfixStack (
result = values[0]; result = values[0];
} }
////////////////////////////////////////////////////////////////////////////////
//
// Grammar:
// Logical --> Regex {( "and" | "or" | "xor" ) Regex}
// Regex --> Equality {( "~" | "!~" ) Equality}
// Equality --> Comparative {( "==" | "=" | "!=" ) Comparative}
// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic}
// Arithmetic --> Geometric {( "+" | "-" ) Geometric}
// Geometric --> Tag {( "*" | "/" | "%" ) Tag}
// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary}
// Unary --> [( "-" | "+" | "!" )] Exponent
// Exponent --> Primitive ["^" Primitive]
// Primitive --> "(" Logical ")" | Variant
//
void Eval::infixParse (
std::vector <std::pair <std::string, Lexer::Type> >& infix) const
{
if (_debug)
std::cout << "# infixParse\n";
try
{
int i = 0;
if (parseLogical (infix, i))
if (_debug)
std::cout << "# no errors.\n";
}
catch (const std::string& error)
{
std::cerr << error << "\n";
}
catch (...)
{
std::cerr << "Unknown error.\n";
}
}
////////////////////////////////////////////////////////////////////////////////
// Logical --> Regex {( "and" | "or" | "xor" ) Regex}
bool Eval::parseLogical (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseLogical\n";
if (i < infix.size () &&
parseRegex (infix, i))
{
while ((infix[i].first == "and" ||
infix[i].first == "or" ||
infix[i].first == "xor") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseRegex (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Regex --> Equality {( "~" | "!~" ) Equality}
bool Eval::parseRegex (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseRegex\n";
if (i < infix.size () &&
parseEquality (infix, i))
{
while ((infix[i].first == "~" ||
infix[i].first == "!~") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseEquality (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Equality --> Comparative {( "==" | "=" | "!=" ) Comparative}
bool Eval::parseEquality (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseEquality\n";
if (i < infix.size () &&
parseComparative (infix, i))
{
while ((infix[i].first == "==" ||
infix[i].first == "=" ||
infix[i].first == "!=") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseComparative (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Comparative --> Arithmetic {( "<=" | "<" | ">=" | ">" ) Arithmetic}
bool Eval::parseComparative (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseComparative\n";
if (i < infix.size () &&
parseArithmetic (infix, i))
{
while ((infix[i].first == "<=" ||
infix[i].first == "<" ||
infix[i].first == ">=" ||
infix[i].first == ">") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseArithmetic (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Arithmetic --> Geometric {( "+" | "-" ) Geometric}
bool Eval::parseArithmetic (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseArithmetic\n";
if (i < infix.size () &&
parseGeometric (infix, i))
{
while ((infix[i].first == "+" ||
infix[i].first == "-") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseGeometric (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Geometric --> Tag {( "*" | "/" | "%" ) Tag}
bool Eval::parseGeometric (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseGeometric\n";
if (i < infix.size () &&
parseTag (infix, i))
{
while ((infix[i].first == "*" ||
infix[i].first == "/" ||
infix[i].first == "%") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseTag (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Tag --> Unary {( "_hastag_" | "_notag_" ) Unary}
bool Eval::parseTag (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseTag\n";
if (i < infix.size () &&
parseUnary (infix, i))
{
while ((infix[i].first == "_hastag_" ||
infix[i].first == "_notag_") &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parseUnary (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Unary --> [( "-" | "+" | "!" )] Exponent
bool Eval::parseUnary (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseUnary\n";
if (i < infix.size ())
{
if (infix[i].first == "-")
{
if (_debug)
std::cout << "# '-' --> '_neg_'\n";
infix[i].first = "_neg_";
++i;
}
else if (infix[i].first == "+")
{
if (_debug)
std::cout << "# '+' --> '_pos_'\n";
infix[i].first = "_pos_";
++i;
}
else if (infix[i].first == "!")
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
}
}
return parseExponent (infix, i);
}
////////////////////////////////////////////////////////////////////////////////
// Exponent --> Primitive ["^" Primitive]
bool Eval::parseExponent (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseExponent\n";
if (i < infix.size () &&
parsePrimitive (infix, i))
{
while (infix[i].first == "^" &&
infix[i].second == Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (! parsePrimitive (infix, i))
return false;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Primitive --> "(" Logical ")" | Variant
bool Eval::parsePrimitive (
std::vector <std::pair <std::string, Lexer::Type> >& infix,
int &i) const
{
if (_debug)
std::cout << "# parseVariant\n";
if (i < infix.size ())
{
if (infix[i].first == "(")
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
if (parseLogical (infix, i))
{
if (i < infix.size () &&
infix[i].first == ")")
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
return true;
}
}
}
else
{
if (infix[i].second != Lexer::typeOperator)
{
if (_debug)
std::cout << "# " << infix[i].first << "\n";
++i;
return true;
}
}
}
return false;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Dijkstra Shunting Algorithm. // Dijkstra Shunting Algorithm.
// http://en.wikipedia.org/wiki/Shunting-yard_algorithm // http://en.wikipedia.org/wiki/Shunting-yard_algorithm

View file

@ -50,6 +50,17 @@ public:
private: private:
void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type> >&, Variant&) const; void evaluatePostfixStack (const std::vector <std::pair <std::string, Lexer::Type> >&, Variant&) const;
void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type> >&) const; void infixToPostfix (std::vector <std::pair <std::string, Lexer::Type> >&) const;
void infixParse (std::vector <std::pair <std::string, Lexer::Type> >&) const;
bool parseLogical (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseRegex (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseEquality (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseComparative (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseArithmetic (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseGeometric (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseTag (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseUnary (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parseExponent (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool parsePrimitive (std::vector <std::pair <std::string, Lexer::Type> >&, int &) const;
bool identifyOperator (const std::string&, char&, int&, char&) const; bool identifyOperator (const std::string&, char&, int&, char&) const;
private: private:

View file

@ -25,6 +25,7 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <cmake.h> #include <cmake.h>
//#include <iostream> // TODO Remove
#include <ISO8601.h> #include <ISO8601.h>
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -603,6 +604,8 @@ bool ISO8601d::validate ()
// long tm_gmtoff; offset from UTC in seconds // long tm_gmtoff; offset from UTC in seconds
void ISO8601d::resolve () void ISO8601d::resolve ()
{ {
//std::cout << "# start ------------------------\n";
// Don't touch the original values. // Don't touch the original values.
int year = _year; int year = _year;
int month = _month; int month = _month;
@ -613,55 +616,69 @@ void ISO8601d::resolve ()
int seconds = _seconds; int seconds = _seconds;
int offset = _offset; int offset = _offset;
bool utc = _utc; bool utc = _utc;
//std::cout << "# input\n"
// << "# year=" << year << "\n"
// << "# month=" << month << "\n"
// << "# week=" << week << "\n"
// << "# weekday=" << weekday << "\n"
// << "# julian=" << julian << "\n"
// << "# day=" << day << "\n"
// << "# seconds=" << seconds << "\n"
// << "# offset=" << offset << "\n"
// << "# utc=" << utc << "\n";
struct tm t = {0}; // Get current time.
// Requests that mktime determine summer time effect.
t.tm_isdst = -1;
// Determine local time.
time_t now = time (NULL); time_t now = time (NULL);
struct tm* local_now = localtime (&now); //std::cout << "# now=" << now << "\n";
struct tm* utc_now = gmtime (&now);
// What is a complete TZ? // A UTC offset needs to be accommodated. Once the offset is subtracted,
// utc // only local and UTC times remain.
// offset if (offset)
// local (get default)
if (utc)
offset = 0;
else if (! offset)
{ {
#ifdef HAVE_TM_GMTOFF seconds -= offset;
offset = local_now->tm_gmtoff; now -= offset;
#else utc = true;
// TODO Umm...
#endif
} }
// Subtract the offset, to project local to UTC. // Get 'now' in the relevant location.
seconds -= offset; struct tm* t_now = utc ? gmtime (&now) : localtime (&now);
//std::cout << "# t_now\n"
// << "# tm_year=" << t_now->tm_year << "\n"
// << "# tm_mon=" << t_now->tm_mon << "\n"
// << "# tm_mday=" << t_now->tm_mday << "\n"
// << "# tm_hour=" << t_now->tm_hour << "\n"
// << "# tm_min=" << t_now->tm_min << "\n"
// << "# tm_sec=" << t_now->tm_sec << "\n"
// << "# tm_isdst=" << t_now->tm_isdst << "\n";
// If the time is specified without a date, if it is earlier than 'now', then int seconds_now = (t_now->tm_hour * 3600) +
// it refers to tomorrow. (t_now->tm_min * 60) +
int seconds_utc_now = utc_now->tm_hour * 3600 + t_now->tm_sec;
utc_now->tm_min * 60 + //std::cout << "# seconds_now=" << seconds_now << "\n";
utc_now->tm_sec;
// Project forward one day if the specified seconds are earlier in the day
// than the current seconds.
if (year == 0 && if (year == 0 &&
month == 0 && month == 0 &&
day == 0 && day == 0 &&
week == 0 && week == 0 &&
weekday == 0 && weekday == 0 &&
seconds < seconds_utc_now) seconds < seconds_now)
{ {
//std::cout << "# earlier today, therefore seconds += 86400\n"
// << "# seconds=" << seconds << "\n";
seconds += 86400; seconds += 86400;
} }
// Conversion of week + weekday to julian. // Convert week + weekday --> julian.
if (week) if (week)
{ {
julian = (week * 7) + weekday - dayOfWeek (year, 1, 4) - 3; julian = (week * 7) + weekday - dayOfWeek (year, 1, 4) - 3;
//std::cout << "# week=" << week << " weekday=" << weekday << " specified\n"
// << "# julian=" << julian << "\n";
} }
// Provide default values for year, month, day.
else else
{ {
// Default values for year, month, day: // Default values for year, month, day:
@ -673,9 +690,9 @@ void ISO8601d::resolve ()
// //
if (year == 0) if (year == 0)
{ {
year = local_now->tm_year + 1900; year = t_now->tm_year + 1900;
month = local_now->tm_mon + 1; month = t_now->tm_mon + 1;
day = local_now->tm_mday; day = t_now->tm_mday;
} }
else else
{ {
@ -688,41 +705,73 @@ void ISO8601d::resolve ()
day = 1; day = 1;
} }
} }
//std::cout << "# Applied default y m d\n"
// << "# year=" << year << "\n"
// << "# month=" << month << "\n"
// << "# day=" << day << "\n";
if (julian) if (julian)
{ {
month = 1; month = 1;
day = julian; day = julian;
//std::cout << "# julian=" << julian << " specified\n"
// << "# month=" << month << "\n"
// << "# day=" << day << "\n";
} }
struct tm t = {0};
t.tm_isdst = -1; // Requests that mktime/gmtime determine summer time effect.
t.tm_year = year - 1900; t.tm_year = year - 1900;
t.tm_mon = month - 1; t.tm_mon = month - 1;
t.tm_mday = day; t.tm_mday = day;
// What is a complete time spec?
// seconds
if (seconds)
{
if (seconds > 86400) if (seconds > 86400)
{ {
//std::cout << "# seconds=" << seconds << " is more than a day\n";
int days = seconds / 86400; int days = seconds / 86400;
t.tm_mday += days; t.tm_mday += days;
seconds -= days * 86400; seconds %= 86400;
//std::cout << "# t.tm_mday=" << t.tm_mday << "\n"
// << "# seconds=" << seconds << "\n";
} }
t.tm_hour = seconds / 3600; t.tm_hour = seconds / 3600;
t.tm_min = (seconds % 3600) / 60; t.tm_min = (seconds % 3600) / 60;
t.tm_sec = seconds % 60; t.tm_sec = seconds % 60;
}
else //std::cout << "# Final t\n"
{ // << "# tm_year=" << t.tm_year << "\n"
// User-provided default. // << "# tm_mon=" << t.tm_mon << "\n"
t.tm_hour = _default_seconds / 3600; // << "# tm_mday=" << t.tm_mday << "\n"
t.tm_min = (_default_seconds % 3600) / 60; // << "# tm_hour=" << t.tm_hour << "\n"
t.tm_sec = _default_seconds % 60; // << "# tm_min=" << t.tm_min << "\n"
// << "# tm_sec=" << t.tm_sec << "\n"
// << "# tm_isdst=" << t.tm_isdst << "\n";
_value = utc ? timegm (&t) : timelocal (&t);
//std::cout << "# _value " << _value << "\n";
//std::cout << "# end --------------------------\n";
//dump ();
} }
_value = timegm (&t); ////////////////////////////////////////////////////////////////////////////////
void ISO8601d::dump ()
{
/*
std::cout << "# Y=" << _year
<< " M=" << _month
<< " W=" << _week
<< " WD=" << _weekday
<< " J=" << _julian
<< " d=" << _day
<< " s=" << _seconds
<< " o=" << _offset
<< " Z=" << _utc
<< " ambi=" << _ambiguity
<< " --> " << _value
<< "\n";
*/
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -59,6 +59,7 @@ private:
int dayOfWeek (int, int, int); int dayOfWeek (int, int, int);
bool validate (); bool validate ();
void resolve (); void resolve ();
void dump ();
public: public:
bool _ambiguity; bool _ambiguity;

View file

@ -233,6 +233,9 @@ void TF2::commit ()
{ {
if (_file.open ()) if (_file.open ())
{ {
if (context.config.getBoolean ("locking"))
_file.waitForLock ();
// Truncate the file and rewrite. // Truncate the file and rewrite.
_file.truncate (); _file.truncate ();

View file

@ -45,13 +45,12 @@
#include <TLSClient.h> #include <TLSClient.h>
#include <text.h> #include <text.h>
#include <i18n.h> #include <i18n.h>
#include <gnutls/x509.h>
#define MAX_BUF 16384 #define MAX_BUF 16384
static int verify_certificate_callback (gnutls_session_t); static int verify_certificate_callback (gnutls_session_t);
static bool trust_override = false;
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static void gnutls_log_function (int level, const char* message) static void gnutls_log_function (int level, const char* message)
{ {
@ -61,40 +60,8 @@ static void gnutls_log_function (int level, const char* message)
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
static int verify_certificate_callback (gnutls_session_t session) static int verify_certificate_callback (gnutls_session_t session)
{ {
if (trust_override) const TLSClient* client = (TLSClient*) gnutls_session_get_ptr (session);
return 0; return client->verify_certificate();
// Get the hostname from the session.
const char* hostname = (const char*) gnutls_session_get_ptr (session);
// This verification function uses the trusted CAs in the credentials
// structure. So you must have installed one or more CA certificates.
unsigned int status = 0;
#if GNUTLS_VERSION_NUMBER >= 0x030104
int ret = gnutls_certificate_verify_peers3 (session, NULL, &status);
#else
int ret = gnutls_certificate_verify_peers2 (session, &status);
#endif
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
#if GNUTLS_VERSION_NUMBER >= 0x030105
gnutls_certificate_type_t type = gnutls_certificate_type_get (session);
gnutls_datum_t out;
ret = gnutls_certificate_verification_status_print (status, type, &out, 0);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
//std::cout << "c: INFO " << out.data << "\n";
gnutls_free (out.data);
#endif
if (status != 0)
return GNUTLS_E_CERTIFICATE_ERROR;
// Continue handshake.
return 0;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -102,10 +69,13 @@ TLSClient::TLSClient ()
: _ca ("") : _ca ("")
, _cert ("") , _cert ("")
, _key ("") , _key ("")
, _host ("")
, _port ("")
, _session(0) , _session(0)
, _socket (0) , _socket (0)
, _limit (0) , _limit (0)
, _debug (false) , _debug (false)
, _trust(strict)
{ {
} }
@ -142,13 +112,15 @@ void TLSClient::debug (int level)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TLSClient::trust (bool value) void TLSClient::trust (const enum trust_level value)
{ {
trust_override = value; _trust = value;
if (_debug) if (_debug)
{ {
if (trust_override) if (_trust == allow_all)
std::cout << "c: INFO Server certificate trusted automatically.\n"; std::cout << "c: INFO Server certificate trusted automatically.\n";
else if (_trust == ignore_hostname)
std::cout << "c: INFO Server certificate trust verified but hostname ignored.\n";
else else
std::cout << "c: INFO Server certificate trust verified.\n"; std::cout << "c: INFO Server certificate trust verified.\n";
} }
@ -183,6 +155,10 @@ void TLSClient::init (
throw std::string ("Missing CERT file."); throw std::string ("Missing CERT file.");
#if GNUTLS_VERSION_NUMBER >= 0x02090a #if GNUTLS_VERSION_NUMBER >= 0x02090a
// The automatic verification for the server certificate with
// gnutls_certificate_set_verify_function only works with gnutls
// >=2.9.10. So with older versions we should call the verify function
// manually after the gnutls handshake.
gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback); gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback);
#endif #endif
gnutls_init (&_session, GNUTLS_CLIENT); gnutls_init (&_session, GNUTLS_CLIENT);
@ -208,9 +184,12 @@ void TLSClient::init (
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TLSClient::connect (const std::string& host, const std::string& port) void TLSClient::connect (const std::string& host, const std::string& port)
{ {
// Store the host name, so the verification callback can access it during the _host = host;
// handshake below. _port = port;
gnutls_session_set_ptr (_session, (void*) host.c_str ());
// Store the TLSClient instance, so that the verification callback can access
// it during the handshake below and call the verifcation method.
gnutls_session_set_ptr (_session, (void*) this);
// use IPv4 or IPv6, does not matter. // use IPv4 or IPv6, does not matter.
struct addrinfo hints = {0}; struct addrinfo hints = {0};
@ -254,7 +233,7 @@ void TLSClient::connect (const std::string& host, const std::string& port)
#if GNUTLS_VERSION_NUMBER >= 0x030109 #if GNUTLS_VERSION_NUMBER >= 0x030109
gnutls_transport_set_int (_session, _socket); gnutls_transport_set_int (_session, _socket);
#else #else
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (long) _socket); gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) _socket);
#endif #endif
// Perform the TLS handshake // Perform the TLS handshake
@ -267,6 +246,20 @@ void TLSClient::connect (const std::string& host, const std::string& port)
if (ret < 0) if (ret < 0)
throw format (STRING_CMD_SYNC_HANDSHAKE, gnutls_strerror (ret)); throw format (STRING_CMD_SYNC_HANDSHAKE, gnutls_strerror (ret));
#if GNUTLS_VERSION_NUMBER < 0x02090a
// The automatic verification for the server certificate with
// gnutls_certificate_set_verify_function does only work with gnutls
// >=2.9.10. So with older versions we should call the verify function
// manually after the gnutls handshake.
ret = verify_certificate();
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR Certificate verification failed.\n";
throw std::string (STRING_TLS_INIT_FAIL);
}
#endif
if (_debug) if (_debug)
{ {
#if GNUTLS_VERSION_NUMBER >= 0x03010a #if GNUTLS_VERSION_NUMBER >= 0x03010a
@ -285,6 +278,76 @@ void TLSClient::bye ()
gnutls_bye (_session, GNUTLS_SHUT_RDWR); gnutls_bye (_session, GNUTLS_SHUT_RDWR);
} }
////////////////////////////////////////////////////////////////////////////////
int TLSClient::verify_certificate () const
{
if (_trust == TLSClient::allow_all)
return 0;
// This verification function uses the trusted CAs in the credentials
// structure. So you must have installed one or more CA certificates.
unsigned int status = 0;
const char* hostname = _host.c_str();
#if GNUTLS_VERSION_NUMBER >= 0x030104
if (_trust == TLSClient::ignore_hostname)
hostname = NULL;
int ret = gnutls_certificate_verify_peers3 (_session, hostname, &status);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
#else
int ret = gnutls_certificate_verify_peers2 (_session, &status);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
if ((status == 0) && (_trust != TLSClient::ignore_hostname))
{
if (gnutls_certificate_type_get (_session) == GNUTLS_CRT_X509)
{
const gnutls_datum* cert_list;
unsigned int cert_list_size;
gnutls_x509_crt cert;
cert_list = gnutls_certificate_get_peers (_session, &cert_list_size);
if (cert_list_size == 0)
return GNUTLS_E_CERTIFICATE_ERROR;
ret = gnutls_x509_crt_init (&cert);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
if (ret < 0)
gnutls_x509_crt_deinit(cert);
status = GNUTLS_E_CERTIFICATE_ERROR;
if (gnutls_x509_crt_check_hostname (cert, hostname) == 0)
gnutls_x509_crt_deinit(cert);
return GNUTLS_E_CERTIFICATE_ERROR;
}
else
return GNUTLS_E_CERTIFICATE_ERROR;
}
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030105
gnutls_certificate_type_t type = gnutls_certificate_type_get (_session);
gnutls_datum_t out;
ret = gnutls_certificate_verification_status_print (status, type, &out, 0);
if (ret < 0)
return GNUTLS_E_CERTIFICATE_ERROR;
gnutls_free (out.data);
#endif
if (status != 0)
return GNUTLS_E_CERTIFICATE_ERROR;
// Continue handshake.
return 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void TLSClient::send (const std::string& data) void TLSClient::send (const std::string& data)
{ {

View file

@ -34,15 +34,18 @@
class TLSClient class TLSClient
{ {
public: public:
enum trust_level { strict, ignore_hostname, allow_all };
TLSClient (); TLSClient ();
~TLSClient (); ~TLSClient ();
void limit (int); void limit (int);
void debug (int); void debug (int);
void trust (bool); void trust (const enum trust_level);
void ciphers (const std::string&); void ciphers (const std::string&);
void init (const std::string&, const std::string&, const std::string&); void init (const std::string&, const std::string&, const std::string&);
void connect (const std::string&, const std::string&); void connect (const std::string&, const std::string&);
void bye (); void bye ();
int verify_certificate() const;
void send (const std::string&); void send (const std::string&);
void recv (std::string&); void recv (std::string&);
@ -52,11 +55,14 @@ private:
std::string _cert; std::string _cert;
std::string _key; std::string _key;
std::string _ciphers; std::string _ciphers;
std::string _host;
std::string _port;
gnutls_certificate_credentials_t _credentials; gnutls_certificate_credentials_t _credentials;
gnutls_session_t _session; gnutls_session_t _session;
int _socket; int _socket;
int _limit; int _limit;
bool _debug; bool _debug;
enum trust_level _trust;
}; };
#endif #endif

View file

@ -24,6 +24,7 @@
// //
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
#include <iostream> // TODO Remove.
#include <cmake.h> #include <cmake.h>
#include <sstream> #include <sstream>
#include <stdlib.h> #include <stdlib.h>
@ -323,6 +324,40 @@ void Task::setStatus (Task::status status)
recalc_urgency = true; recalc_urgency = true;
} }
////////////////////////////////////////////////////////////////////////////////
// Determines status of a date attribute.
Task::dateState Task::getDateState (const std::string& name) const
{
std::string value = get (name);
if (value.length ())
{
Date reference (value);
Date now;
Date today ("today");
if (reference < today)
return dateBeforeToday;
if (reference.sameDay (now))
{
if (reference < now)
return dateEarlierToday;
else
return dateLaterToday;
}
int imminentperiod = context.config.getInteger ("due");
if (imminentperiod == 0)
return dateAfterToday;
Date imminentDay = today + imminentperiod * 86400;
if (reference < imminentDay)
return dateAfterToday;
}
return dateNotDue;
}
#ifdef PRODUCT_TASKWARRIOR #ifdef PRODUCT_TASKWARRIOR
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Ready means pending, not blocked and either not scheduled or scheduled before // Ready means pending, not blocked and either not scheduled or scheduled before
@ -345,7 +380,10 @@ bool Task::is_due () const
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted) status != Task::deleted)
{ {
if (getDueState (get ("due")) == 1) Task::dateState state = getDateState ("due");
if (state == dateAfterToday ||
state == dateEarlierToday ||
state == dateLaterToday)
return true; return true;
} }
} }
@ -381,7 +419,9 @@ bool Task::is_duetoday () const
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted) status != Task::deleted)
{ {
if (getDueState (get ("due")) == 2) Task::dateState state = getDateState ("due");
if (state == dateEarlierToday ||
state == dateLaterToday)
return true; return true;
} }
} }
@ -479,7 +519,9 @@ bool Task::is_overdue () const
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted) status != Task::deleted)
{ {
if (getDueState (get ("due")) == 3) Task::dateState state = getDateState ("due");
if (state == dateEarlierToday ||
state == dateBeforeToday)
return true; return true;
} }
} }
@ -1144,6 +1186,16 @@ int Task::getTagCount () const
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//
// OVERDUE YESTERDAY DUE TODAY TOMORROW WEEK MONTH YEAR
// due:-1week Y - - - - ? ? ?
// due:-1day Y Y - - - ? ? ?
// due:today Y - Y Y - ? ? ?
// due:tomorrow - - Y - Y ? ? ?
// due:3days - - Y - - ? ? ?
// due:1month - - - - - - - ?
// due:1year - - - - - - - -
//
bool Task::hasTag (const std::string& tag) const bool Task::hasTag (const std::string& tag) const
{ {
// Synthetic tags - dynamically generated, but do not occupy storage space. // Synthetic tags - dynamically generated, but do not occupy storage space.
@ -1169,6 +1221,9 @@ bool Task::hasTag (const std::string& tag) const
if (tag == "WAITING") return has ("wait"); if (tag == "WAITING") return has ("wait");
if (tag == "ANNOTATED") return hasAnnotations (); if (tag == "ANNOTATED") return hasAnnotations ();
if (tag == "PARENT") return has ("mask"); if (tag == "PARENT") return has ("mask");
if (tag == "PENDING") return get ("status") == "pending";
if (tag == "COMPLETED") return get ("status") == "completed";
if (tag == "DELETED") return get ("status") == "deleted";
// Concrete tags. // Concrete tags.
std::vector <std::string> tags; std::vector <std::string> tags;

View file

@ -72,6 +72,9 @@ public:
// Status values. // Status values.
enum status {pending, completed, deleted, recurring, waiting}; enum status {pending, completed, deleted, recurring, waiting};
// Date state values.
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
// Public data. // Public data.
int id; int id;
float urgency_value; float urgency_value;
@ -117,6 +120,8 @@ public:
status getStatus () const; status getStatus () const;
void setStatus (status); void setStatus (status);
dateState getDateState (const std::string&) const;
int getTagCount () const; int getTagCount () const;
bool hasTag (const std::string&) const; bool hasTag (const std::string&) const;
void addTag (const std::string&); void addTag (const std::string&);

View file

@ -1302,6 +1302,8 @@ Variant::operator std::string () const
{ {
time_t t = _duration; time_t t = _duration;
if (t)
{
int seconds = t % 60; t /= 60; int seconds = t % 60; t /= 60;
int minutes = t % 60; t /= 60; int minutes = t % 60; t /= 60;
int hours = t % 24; t /= 24; int hours = t % 24; t /= 24;
@ -1325,6 +1327,11 @@ Variant::operator std::string () const
return s.str (); return s.str ();
} }
else
{
return "P0S";
}
}
case type_unknown: case type_unknown:
throw std::string ("Cannot render an unknown type."); throw std::string ("Cannot render an unknown type.");

View file

@ -41,7 +41,7 @@
extern Context context; extern Context context;
// Helper macro. // Helper macro.
#define LOC(y,x) (((y) * (width + 1)) + (x)) #define LOC(y,x) (((y) * (_width + 1)) + (x))
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class Bar class Bar
@ -53,26 +53,26 @@ public:
~Bar (); ~Bar ();
public: public:
int offset; // from left of chart int _offset; // from left of chart
std::string major_label; // x-axis label, major (year/-/month) std::string _major_label; // x-axis label, major (year/-/month)
std::string minor_label; // x-axis label, minor (month/week/day) std::string _minor_label; // x-axis label, minor (month/week/day)
int pending; // Number of pending tasks in period int _pending; // Number of pending tasks in period
int started; // Number of started tasks in period int _started; // Number of started tasks in period
int done; // Number of done tasks in period int _done; // Number of done tasks in period
int added; // Number added in period int _added; // Number added in period
int removed; // Number removed in period int _removed; // Number removed in period
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Bar::Bar () Bar::Bar ()
: offset (0) : _offset (0)
, major_label ("") , _major_label ("")
, minor_label ("") , _minor_label ("")
, pending (0) , _pending (0)
, started (0) , _started (0)
, done (0) , _done (0)
, added (0) , _added (0)
, removed (0) , _removed (0)
{ {
} }
@ -87,14 +87,14 @@ Bar& Bar::operator= (const Bar& other)
{ {
if (this != &other) if (this != &other)
{ {
offset = other.offset; _offset = other._offset;
major_label = other.major_label; _major_label = other._major_label;
minor_label = other.minor_label; _minor_label = other._minor_label;
pending = other.pending; _pending = other._pending;
started = other.started; _started = other._started;
done = other.done; _done = other._done;
added = other.added; _added = other._added;
removed = other.removed; _removed = other._removed;
} }
return *this; return *this;
@ -159,25 +159,25 @@ private:
void calculateRates (std::vector <time_t>&); void calculateRates (std::vector <time_t>&);
public: public:
int width; // Terminal width int _width; // Terminal width
int height; // Terminal height int _height; // Terminal height
int graph_width; // Width of plot area int _graph_width; // Width of plot area
int graph_height; // Height of plot area int _graph_height; // Height of plot area
int max_value; // Largest combined bar value int _max_value; // Largest combined bar value
int max_label; // Longest y-axis label int _max_label; // Longest y-axis label
std::vector <int> labels; // Y-axis labels std::vector <int> _labels; // Y-axis labels
int estimated_bars; // Estimated bar count int _estimated_bars; // Estimated bar count
int actual_bars; // Calculated bar count int _actual_bars; // Calculated bar count
std::map <time_t, Bar> bars; // Epoch-indexed set of bars std::map <time_t, Bar> _bars; // Epoch-indexed set of bars
Date earliest; // Date of earliest estimated bar Date _earliest; // Date of earliest estimated bar
int carryover_done; // Number of 'done' tasks prior to chart range int _carryover_done; // Number of 'done' tasks prior to chart range
char period; // D, W, M char _period; // D, W, M
std::string title; // Additional description std::string _title; // Additional description
std::string grid; // String representing grid of characters std::string _grid; // String representing grid of characters
float find_rate; // Calculated find rate float _find_rate; // Calculated find rate
float fix_rate; // Calculated fix rate float _fix_rate; // Calculated fix rate
std::string completion; // Estimated completion date std::string _completion; // Estimated completion date
}; };
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -185,27 +185,27 @@ Chart::Chart (char type)
{ {
// How much space is there to render in? This chart will occupy the // How much space is there to render in? This chart will occupy the
// maximum space, and the width drives various other parameters. // maximum space, and the width drives various other parameters.
width = context.getWidth (); _width = context.getWidth ();
height = context.getHeight () - 1; // Allow for new line with prompt. _height = context.getHeight () - 1; // Allow for new line with prompt.
max_value = 0; _max_value = 0;
max_label = 1; _max_label = 1;
graph_height = height - 7; _graph_height = _height - 7;
graph_width = width - max_label - 14; _graph_width = _width - _max_label - 14;
// Estimate how many 'bars' can be dsplayed. This will help subset a // Estimate how many 'bars' can be dsplayed. This will help subset a
// potentially enormous data set. // potentially enormous data set.
estimated_bars = (width - 1 - 14) / 3; _estimated_bars = (_width - 1 - 14) / 3;
actual_bars = 0; _actual_bars = 0;
period = type; _period = type;
carryover_done = 0; _carryover_done = 0;
// Rates are calculated last. // Rates are calculated last.
find_rate = 0.0; _find_rate = 0.0;
fix_rate = 0.0; _fix_rate = 0.0;
// Set the title. // Set the title.
title = "("; _title = "(";
std::vector <Arg>::const_iterator arg; std::vector <Arg>::const_iterator arg;
for (arg = context.a3.begin (); arg != context.a3.end (); ++arg) for (arg = context.a3.begin (); arg != context.a3.end (); ++arg)
{ {
@ -217,13 +217,13 @@ Chart::Chart (char type)
; ;
else else
{ {
if (title.length () > 1) if (_title.length () > 1)
title += " "; _title += " ";
title += arg->_raw; _title += arg->_raw;
} }
} }
title += ")"; _title += ")";
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -247,8 +247,8 @@ void Chart::scan (std::vector <Task>& tasks)
Date from = quantize (Date (task->get_date ("entry"))); Date from = quantize (Date (task->get_date ("entry")));
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) if (_bars.find (epoch) != _bars.end ())
++bars[epoch].added; ++_bars[epoch]._added;
// e--> e--s--> // e--> e--s-->
// ppp> pppsss> // ppp> pppsss>
@ -262,14 +262,14 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < start) while (from < start)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
while (from < now) while (from < now)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
from = increment (from); from = increment (from);
} }
} }
@ -278,7 +278,7 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < now) while (from < now)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
} }
@ -292,14 +292,14 @@ void Chart::scan (std::vector <Task>& tasks)
Date end = quantize (Date (task->get_date ("end"))); Date end = quantize (Date (task->get_date ("end")));
epoch = end.toEpoch (); epoch = end.toEpoch ();
if (bars.find (epoch) != bars.end ()) if (_bars.find (epoch) != _bars.end ())
++bars[epoch].removed; ++_bars[epoch]._removed;
// Maintain a running total of 'done' tasks that are off the left of the // Maintain a running total of 'done' tasks that are off the left of the
// chart. // chart.
if (end < earliest) if (end < _earliest)
{ {
++carryover_done; ++_carryover_done;
continue; continue;
} }
@ -309,21 +309,21 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < start) while (from < start)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
while (from < end) while (from < end)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
from = increment (from); from = increment (from);
} }
while (from < now) while (from < now)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].done; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._done;
from = increment (from); from = increment (from);
} }
} }
@ -333,14 +333,14 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < end) while (from < end)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
while (from < now) while (from < now)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].done; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._done;
from = increment (from); from = increment (from);
} }
} }
@ -353,10 +353,10 @@ void Chart::scan (std::vector <Task>& tasks)
// Skip old deleted tasks. // Skip old deleted tasks.
Date end = quantize (Date (task->get_date ("end"))); Date end = quantize (Date (task->get_date ("end")));
epoch = end.toEpoch (); epoch = end.toEpoch ();
if (bars.find (epoch) != bars.end ()) if (_bars.find (epoch) != _bars.end ())
++bars[epoch].removed; ++_bars[epoch]._removed;
if (end < earliest) if (end < _earliest)
continue; continue;
if (task->has ("start")) if (task->has ("start"))
@ -365,14 +365,14 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < start) while (from < start)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
while (from < end) while (from < end)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
from = increment (from); from = increment (from);
} }
} }
@ -382,7 +382,7 @@ void Chart::scan (std::vector <Task>& tasks)
while (from < end) while (from < end)
{ {
epoch = from.toEpoch (); epoch = from.toEpoch ();
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending; if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
from = increment (from); from = increment (from);
} }
} }
@ -414,23 +414,23 @@ void Chart::scan (std::vector <Task>& tasks)
// +---------------------------------------------------------------------+ // +---------------------------------------------------------------------+
std::string Chart::render () std::string Chart::render ()
{ {
if (graph_height < 5 || // a 4-line graph is essentially unreadable. if (_graph_height < 5 || // a 4-line graph is essentially unreadable.
graph_width < 2) // A single-bar graph is useless. _graph_width < 2) // A single-bar graph is useless.
{ {
return std::string (STRING_CMD_BURN_TOO_SMALL) + "\n"; return std::string (STRING_CMD_BURN_TOO_SMALL) + "\n";
} }
if (max_value == 0) if (_max_value == 0)
context.footnote (STRING_FEEDBACK_NO_MATCH); context.footnote (STRING_FEEDBACK_NO_MATCH);
// Create a grid, folded into a string. // Create a grid, folded into a string.
grid = ""; _grid = "";
for (int i = 0; i < height; ++i) for (int i = 0; i < _height; ++i)
grid += std::string (width, ' ') + "\n"; _grid += std::string (_width, ' ') + "\n";
// Title. // Title.
std::string full_title; std::string full_title;
switch (period) switch (_period)
{ {
case 'D': full_title = STRING_CMD_BURN_DAILY; break; case 'D': full_title = STRING_CMD_BURN_DAILY; break;
case 'W': full_title = STRING_CMD_BURN_WEEKLY; break; case 'W': full_title = STRING_CMD_BURN_WEEKLY; break;
@ -439,118 +439,118 @@ std::string Chart::render ()
full_title += std::string (" ") + STRING_CMD_BURN_TITLE; full_title += std::string (" ") + STRING_CMD_BURN_TITLE;
if (title.length ()) if (_title.length ())
{ {
if (full_title.length () + 1 + title.length () < (unsigned) width) if (full_title.length () + 1 + _title.length () < (unsigned) _width)
{ {
full_title += " " + title; full_title += " " + _title;
grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); _grid.replace (LOC (0, (_width - full_title.length ()) / 2), full_title.length (), full_title);
} }
else else
{ {
grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); _grid.replace (LOC (0, (_width - full_title.length ()) / 2), full_title.length (), full_title);
grid.replace (LOC (1, (width - title.length ()) / 2), title.length (), title); _grid.replace (LOC (1, (_width - _title.length ()) / 2), _title.length (), _title);
} }
} }
else else
{ {
grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title); _grid.replace (LOC (0, (_width - full_title.length ()) / 2), full_title.length (), full_title);
} }
// Legend. // Legend.
grid.replace (LOC (graph_height / 2 - 1, width - 10), 10, "DD " + leftJustify (STRING_CMD_BURN_DONE, 7)); _grid.replace (LOC (_graph_height / 2 - 1, _width - 10), 10, "DD " + leftJustify (STRING_CMD_BURN_DONE, 7));
grid.replace (LOC (graph_height / 2, width - 10), 10, "SS " + leftJustify (STRING_CMD_BURN_STARTED, 7)); _grid.replace (LOC (_graph_height / 2, _width - 10), 10, "SS " + leftJustify (STRING_CMD_BURN_STARTED, 7));
grid.replace (LOC (graph_height / 2 + 1, width - 10), 10, "PP " + leftJustify (STRING_CMD_BURN_PENDING, 7)); _grid.replace (LOC (_graph_height / 2 + 1, _width - 10), 10, "PP " + leftJustify (STRING_CMD_BURN_PENDING, 7));
// Determine y-axis labelling. // Determine y-axis labelling.
std::vector <int> labels; std::vector <int> _labels;
yLabels (labels); yLabels (_labels);
max_label = (int) log10 ((double) labels[2]) + 1; _max_label = (int) log10 ((double) _labels[2]) + 1;
// Draw y-axis. // Draw y-axis.
for (int i = 0; i < graph_height; ++i) for (int i = 0; i < _graph_height; ++i)
grid.replace (LOC (i + 1, max_label + 1), 1, "|"); _grid.replace (LOC (i + 1, _max_label + 1), 1, "|");
// Draw y-axis labels. // Draw y-axis labels.
char label [12]; char label [12];
sprintf (label, "%*d", max_label, labels[2]); sprintf (label, "%*d", _max_label, _labels[2]);
grid.replace (LOC (1, max_label - strlen (label)), strlen (label), label); _grid.replace (LOC (1, _max_label - strlen (label)), strlen (label), label);
sprintf (label, "%*d", max_label, labels[1]); sprintf (label, "%*d", _max_label, _labels[1]);
grid.replace (LOC (1 + (graph_height / 2), max_label - strlen (label)), strlen (label), label); _grid.replace (LOC (1 + (_graph_height / 2), _max_label - strlen (label)), strlen (label), label);
grid.replace (LOC (graph_height + 1, max_label - 1), 1, "0"); _grid.replace (LOC (_graph_height + 1, _max_label - 1), 1, "0");
// Draw x-axis. // Draw x-axis.
grid.replace (LOC (height - 6, max_label + 1), 1, "+"); _grid.replace (LOC (_height - 6, _max_label + 1), 1, "+");
grid.replace (LOC (height - 6, max_label + 2), graph_width, std::string (graph_width, '-')); _grid.replace (LOC (_height - 6, _max_label + 2), _graph_width, std::string (_graph_width, '-'));
// Draw x-axis labels. // Draw x-axis labels.
std::vector <time_t> bars_in_sequence; std::vector <time_t> bars_in_sequence;
std::map <time_t, Bar>::iterator it; std::map <time_t, Bar>::iterator it;
for (it = bars.begin (); it != bars.end (); ++it) for (it = _bars.begin (); it != _bars.end (); ++it)
bars_in_sequence.push_back (it->first); bars_in_sequence.push_back (it->first);
std::sort (bars_in_sequence.begin (), bars_in_sequence.end ()); std::sort (bars_in_sequence.begin (), bars_in_sequence.end ());
std::vector <time_t>::iterator seq; std::vector <time_t>::iterator seq;
std::string major_label; std::string _major_label;
for (seq = bars_in_sequence.begin (); seq != bars_in_sequence.end (); ++seq) for (seq = bars_in_sequence.begin (); seq != bars_in_sequence.end (); ++seq)
{ {
Bar bar = bars[*seq]; Bar bar = _bars[*seq];
// If it fits within the allowed space. // If it fits within the allowed space.
if (bar.offset < actual_bars) if (bar._offset < _actual_bars)
{ {
grid.replace (LOC (height - 5, max_label + 3 + ((actual_bars - bar.offset - 1) * 3)), bar.minor_label.length (), bar.minor_label); _grid.replace (LOC (_height - 5, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), bar._minor_label.length (), bar._minor_label);
if (major_label != bar.major_label) if (_major_label != bar._major_label)
grid.replace (LOC (height - 4, max_label + 2 + ((actual_bars - bar.offset - 1) * 3)), bar.major_label.length (), " " + bar.major_label); _grid.replace (LOC (_height - 4, _max_label + 2 + ((_actual_bars - bar._offset - 1) * 3)), bar._major_label.length (), " " + bar._major_label);
major_label = bar.major_label; _major_label = bar._major_label;
} }
} }
// Draw bars. // Draw bars.
for (seq = bars_in_sequence.begin (); seq != bars_in_sequence.end (); ++seq) for (seq = bars_in_sequence.begin (); seq != bars_in_sequence.end (); ++seq)
{ {
Bar bar = bars[*seq]; Bar bar = _bars[*seq];
// If it fits within the allowed space. // If it fits within the allowed space.
if (bar.offset < actual_bars) if (bar._offset < _actual_bars)
{ {
int pending = ( bar.pending * graph_height) / labels[2]; int pending = ( bar._pending * _graph_height) / _labels[2];
int started = ((bar.pending + bar.started) * graph_height) / labels[2]; int started = ((bar._pending + bar._started) * _graph_height) / _labels[2];
int done = ((bar.pending + bar.started + bar.done + carryover_done) * graph_height) / labels[2]; int done = ((bar._pending + bar._started + bar._done + _carryover_done) * _graph_height) / _labels[2];
for (int b = 0; b < pending; ++b) for (int b = 0; b < pending; ++b)
grid.replace (LOC (graph_height - b, max_label + 3 + ((actual_bars - bar.offset - 1) * 3)), 2, "PP"); _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "PP");
for (int b = pending; b < started; ++b) for (int b = pending; b < started; ++b)
grid.replace (LOC (graph_height - b, max_label + 3 + ((actual_bars - bar.offset - 1) * 3)), 2, "SS"); _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "SS");
for (int b = started; b < done; ++b) for (int b = started; b < done; ++b)
grid.replace (LOC (graph_height - b, max_label + 3 + ((actual_bars - bar.offset - 1) * 3)), 2, "DD"); _grid.replace (LOC (_graph_height - b, _max_label + 3 + ((_actual_bars - bar._offset - 1) * 3)), 2, "DD");
} }
} }
// Draw rates. // Draw rates.
calculateRates (bars_in_sequence); calculateRates (bars_in_sequence);
char rate[12]; char rate[12];
if (find_rate != 0.0) if (_find_rate != 0.0)
sprintf (rate, "%.1f/d", find_rate); sprintf (rate, "%.1f/d", _find_rate);
else else
strcpy (rate, "-"); strcpy (rate, "-");
grid.replace (LOC (height - 2, max_label + 3), 18 + strlen (rate), std::string ("Add rate: ") + rate); _grid.replace (LOC (_height - 2, _max_label + 3), 18 + strlen (rate), std::string ("Add rate: ") + rate);
if (fix_rate != 0.0) if (_fix_rate != 0.0)
sprintf (rate, "%.1f/d", fix_rate); sprintf (rate, "%.1f/d", _fix_rate);
else else
strcpy (rate, "-"); strcpy (rate, "-");
grid.replace (LOC (height - 1, max_label + 3), 18 + strlen (rate), std::string ("Done/Delete rate: ") + rate); _grid.replace (LOC (_height - 1, _max_label + 3), 18 + strlen (rate), std::string ("Done/Delete rate: ") + rate);
// Draw completion date. // Draw completion date.
if (completion.length ()) if (_completion.length ())
grid.replace (LOC (height - 2, max_label + 32), 22 + completion.length (), "Estimated completion: " + completion); _grid.replace (LOC (_height - 2, _max_label + 32), 22 + _completion.length (), "Estimated completion: " + _completion);
optimizeGrid (); optimizeGrid ();
@ -563,53 +563,53 @@ std::string Chart::render ()
// Replace DD, SS, PP with colored strings. // Replace DD, SS, PP with colored strings.
std::string::size_type i; std::string::size_type i;
while ((i = grid.find ("PP")) != std::string::npos) while ((i = _grid.find ("PP")) != std::string::npos)
grid.replace (i, 2, color_pending.colorize (" ")); _grid.replace (i, 2, color_pending.colorize (" "));
while ((i = grid.find ("SS")) != std::string::npos) while ((i = _grid.find ("SS")) != std::string::npos)
grid.replace (i, 2, color_started.colorize (" ")); _grid.replace (i, 2, color_started.colorize (" "));
while ((i = grid.find ("DD")) != std::string::npos) while ((i = _grid.find ("DD")) != std::string::npos)
grid.replace (i, 2, color_done.colorize (" ")); _grid.replace (i, 2, color_done.colorize (" "));
} }
else else
{ {
// Replace DD, SS, PP with ./+/X strings. // Replace DD, SS, PP with ./+/X strings.
std::string::size_type i; std::string::size_type i;
while ((i = grid.find ("PP")) != std::string::npos) while ((i = _grid.find ("PP")) != std::string::npos)
grid.replace (i, 2, " X"); _grid.replace (i, 2, " X");
while ((i = grid.find ("SS")) != std::string::npos) while ((i = _grid.find ("SS")) != std::string::npos)
grid.replace (i, 2, " +"); _grid.replace (i, 2, " +");
while ((i = grid.find ("DD")) != std::string::npos) while ((i = _grid.find ("DD")) != std::string::npos)
grid.replace (i, 2, " ."); _grid.replace (i, 2, " .");
} }
return grid; return _grid;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// grid =~ /\s+$//g // _grid =~ /\s+$//g
void Chart::optimizeGrid () void Chart::optimizeGrid ()
{ {
std::string::size_type ws; std::string::size_type ws;
while ((ws = grid.find (" \n")) != std::string::npos) while ((ws = _grid.find (" \n")) != std::string::npos)
{ {
std::string::size_type non_ws = ws; std::string::size_type non_ws = ws;
while (grid[non_ws] == ' ') while (_grid[non_ws] == ' ')
--non_ws; --non_ws;
grid.replace (non_ws + 1, ws - non_ws + 1, "\n"); _grid.replace (non_ws + 1, ws - non_ws + 1, "\n");
} }
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
Date Chart::quantize (const Date& input) Date Chart::quantize (const Date& input)
{ {
if (period == 'D') return input.startOfDay (); if (_period == 'D') return input.startOfDay ();
if (period == 'W') return input.startOfWeek (); if (_period == 'W') return input.startOfWeek ();
if (period == 'M') return input.startOfMonth (); if (_period == 'M') return input.startOfMonth ();
return input; return input;
} }
@ -624,7 +624,7 @@ Date Chart::increment (const Date& input)
int days; int days;
switch (period) switch (_period)
{ {
case 'D': case 'D':
if (++d > Date::daysInMonth (m, y)) if (++d > Date::daysInMonth (m, y))
@ -675,7 +675,7 @@ Date Chart::decrement (const Date& input)
int m = input.month (); int m = input.month ();
int y = input.year (); int y = input.year ();
switch (period) switch (_period)
{ {
case 'D': case 'D':
if (--d == 0) if (--d == 0)
@ -718,14 +718,14 @@ Date Chart::decrement (const Date& input)
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Do 'bars[epoch] = Bar' for every bar that may appear on a chart. // Do '_bars[epoch] = Bar' for every bar that may appear on a chart.
void Chart::generateBars () void Chart::generateBars ()
{ {
Bar bar; Bar bar;
// Determine the last bar date. // Determine the last bar date.
Date cursor; Date cursor;
switch (period) switch (_period)
{ {
case 'D': cursor = Date ().startOfDay (); break; case 'D': cursor = Date ().startOfDay (); break;
case 'W': cursor = Date ().startOfWeek (); break; case 'W': cursor = Date ().startOfWeek (); break;
@ -734,43 +734,43 @@ void Chart::generateBars ()
// Iterate and determine all the other bar dates. // Iterate and determine all the other bar dates.
char str[12]; char str[12];
for (int i = 0; i < estimated_bars; ++i) for (int i = 0; i < _estimated_bars; ++i)
{ {
// Create the major and minor labels. // Create the major and minor labels.
switch (period) switch (_period)
{ {
case 'D': // month/day case 'D': // month/day
{ {
std::string month = Date::monthName (cursor.month ()); std::string month = Date::monthName (cursor.month ());
bar.major_label = month.substr (0, 3); bar._major_label = month.substr (0, 3);
sprintf (str, "%02d", cursor.day ()); sprintf (str, "%02d", cursor.day ());
bar.minor_label = str; bar._minor_label = str;
} }
break; break;
case 'W': // year/week case 'W': // year/week
sprintf (str, "%d", cursor.year ()); sprintf (str, "%d", cursor.year ());
bar.major_label = str; bar._major_label = str;
sprintf (str, "%02d", cursor.weekOfYear (0)); sprintf (str, "%02d", cursor.weekOfYear (0));
bar.minor_label = str; bar._minor_label = str;
break; break;
case 'M': // year/month case 'M': // year/month
sprintf (str, "%d", cursor.year ()); sprintf (str, "%d", cursor.year ());
bar.major_label = str; bar._major_label = str;
sprintf (str, "%02d", cursor.month ()); sprintf (str, "%02d", cursor.month ());
bar.minor_label = str; bar._minor_label = str;
break; break;
} }
bar.offset = i; bar._offset = i;
bars[cursor.toEpoch ()] = bar; _bars[cursor.toEpoch ()] = bar;
// Record the earliest date, for use as a cutoff when scanning data. // Record the earliest date, for use as a cutoff when scanning data.
earliest = cursor; _earliest = cursor;
// Move to the previous period. // Move to the previous period.
cursor = decrement (cursor); cursor = decrement (cursor);
@ -780,39 +780,39 @@ void Chart::generateBars ()
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
void Chart::maxima () void Chart::maxima ()
{ {
max_value = 0; _max_value = 0;
max_label = 1; _max_label = 1;
std::map <time_t, Bar>::iterator it; std::map <time_t, Bar>::iterator it;
for (it = bars.begin (); it != bars.end (); it++) for (it = _bars.begin (); it != _bars.end (); it++)
{ {
// Determine max_label. // Determine _max_label.
int total = it->second.pending + int total = it->second._pending +
it->second.started + it->second._started +
it->second.done + it->second._done +
carryover_done; _carryover_done;
// Determine max_value. // Determine _max_value.
if (total > max_value) if (total > _max_value)
max_value = total; _max_value = total;
int length = (int) log10 ((double) total) + 1; int length = (int) log10 ((double) total) + 1;
if (length > max_label) if (length > _max_label)
max_label = length; _max_label = length;
} }
// How many bars can be shown? // How many bars can be shown?
actual_bars = (width - max_label - 14) / 3; _actual_bars = (_width - _max_label - 14) / 3;
graph_width = width - max_label - 14; _graph_width = _width - _max_label - 14;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Given the vertical chart area size (graph_height), the largest value // Given the vertical chart area size (graph_height), the largest value
// (max_value), populate a vector of labels for the y axis. // (_max_value), populate a vector of labels for the y axis.
void Chart::yLabels (std::vector <int>& labels) void Chart::yLabels (std::vector <int>& labels)
{ {
// Calculate may Y using a nice algorithm that rounds the data. // Calculate may Y using a nice algorithm that rounds the data.
int high = burndown_size (max_value); int high = burndown_size (_max_value);
int half = high / 2; int half = high / 2;
labels.push_back (0); labels.push_back (0);
@ -825,7 +825,7 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
{ {
// If there are no current pending tasks, then it is meaningless to find // If there are no current pending tasks, then it is meaningless to find
// rates or estimated completion date. // rates or estimated completion date.
if (bars[sequence.back ()].pending == 0) if (_bars[sequence.back ()]._pending == 0)
return; return;
// Calculate how many items we have. // Calculate how many items we have.
@ -844,7 +844,7 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
// How many days do these sums represent? // How many days do these sums represent?
int half_days = 1; int half_days = 1;
int quarter_days = 1; int quarter_days = 1;
switch (period) switch (_period)
{ {
case 'D': case 'D':
half_days = half; half_days = half;
@ -869,14 +869,14 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
for (unsigned int i = half; i < sequence.size (); ++i) for (unsigned int i = half; i < sequence.size (); ++i)
{ {
total_added_50 += bars[sequence[i]].added; total_added_50 += _bars[sequence[i]]._added;
total_removed_50 += bars[sequence[i]].removed; total_removed_50 += _bars[sequence[i]]._removed;
} }
for (unsigned int i = half + quarter; i < sequence.size (); ++i) for (unsigned int i = half + quarter; i < sequence.size (); ++i)
{ {
total_added_75 += bars[sequence[i]].added; total_added_75 += _bars[sequence[i]]._added;
total_removed_75 += bars[sequence[i]].removed; total_removed_75 += _bars[sequence[i]]._removed;
} }
float find_rate_50 = 1.0 * total_added_50 / half_days; float find_rate_50 = 1.0 * total_added_50 / half_days;
@ -887,8 +887,8 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
// Make configurable. // Make configurable.
float bias = (float) context.config.getReal ("burndown.bias"); float bias = (float) context.config.getReal ("burndown.bias");
find_rate = (find_rate_50 * (1.0 - bias) + find_rate_75 * bias); _find_rate = (find_rate_50 * (1.0 - bias) + find_rate_75 * bias);
fix_rate = (fix_rate_50 * (1.0 - bias) + fix_rate_75 * bias); _fix_rate = (fix_rate_50 * (1.0 - bias) + fix_rate_75 * bias);
// Q: Why is this equation written out as a debug message? // Q: Why is this equation written out as a debug message?
// A: People are going to want to know how the rates and the completion date // A: People are going to want to know how the rates and the completion date
@ -908,7 +908,7 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
<< " days) * " << " days) * "
<< bias << bias
<< ") = " << ") = "
<< find_rate << _find_rate
<< "\nChart::calculateRates fix rate: " << "\nChart::calculateRates fix rate: "
<< "(" << "("
<< total_removed_50 << total_removed_50
@ -923,20 +923,25 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
<< " days) * " << " days) * "
<< bias << bias
<< ") = " << ") = "
<< fix_rate; << _fix_rate;
context.debug (rates.str ()); context.debug (rates.str ());
// Estimate completion // Estimate completion
if (fix_rate > find_rate) if (_fix_rate > _find_rate)
{ {
int current_pending = bars[sequence.back ()].pending; int current_pending = _bars[sequence.back ()]._pending;
int remaining_days = (int) (current_pending / (fix_rate - find_rate)); int remaining_days = (int) (current_pending / (_fix_rate - _find_rate));
Date now; Date now;
OldDuration delta (remaining_days * 86400); OldDuration delta (remaining_days * 86400);
now += delta; now += delta;
completion = now.toString (context.config.get ("dateformat")) // Prefer dateformat.report over dateformat.
std::string format = context.config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
_completion = now.toString (format)
+ " (" + " ("
+ delta.format () + delta.format ()
+ ")"; + ")";
@ -945,18 +950,18 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
est << "Chart::calculateRates Completion: " est << "Chart::calculateRates Completion: "
<< current_pending << current_pending
<< " tasks / (" << " tasks / ("
<< fix_rate << _fix_rate
<< " - " << " - "
<< find_rate << _find_rate
<< ") = " << ") = "
<< remaining_days << remaining_days
<< " days = " << " days = "
<< completion; << _completion;
context.debug (est.str ()); context.debug (est.str ());
} }
else else
{ {
completion = STRING_CMD_BURN_NO_CONVERGE; _completion = STRING_CMD_BURN_NO_CONVERGE;
} }
} }

View file

@ -561,23 +561,24 @@ std::string CmdCalendar::renderMonths (
duedmy.month () == months[mpl] && duedmy.month () == months[mpl] &&
duedmy.year () == years[mpl]) duedmy.year () == years[mpl])
{ {
switch (getDueState (due)) switch (task->getDateState ("due"))
{ {
case 1: // imminent case Task::dateNotDue:
break;
case Task::dateAfterToday:
cellColor.blend (color_due); cellColor.blend (color_due);
break; break;
case 2: // today case Task::dateEarlierToday:
case Task::dateLaterToday:
cellColor.blend (color_duetoday);
cellColor.blend (color_duetoday); cellColor.blend (color_duetoday);
break; break;
case 3: // overdue case Task::dateBeforeToday:
cellColor.blend (color_overdue); cellColor.blend (color_overdue);
break; break;
case 0: // not due at all
default:
break;
} }
} }
} }

View file

@ -232,8 +232,12 @@ int CmdDiagnostics::execute (std::string& output)
? " (readable)" : " (not readable)") ? " (readable)" : " (not readable)")
<< "\n"; << "\n";
if (context.config.get ("taskd.trust") != "") if (context.config.get ("taskd.trust") == "allow all")
out << " Trust: override\n"; out << " Trust: allow all\n";
else if (context.config.get ("taskd.trust") == "ignore hostname")
out << " Trust: ignore hostanme\n";
else
out << " Trust: strict\n";
out << " Cert: " out << " Cert: "
<< context.config.get ("taskd.certificate") << context.config.get ("taskd.certificate")

View file

@ -52,17 +52,23 @@ int CmdGet::execute (std::string& output)
if (words.size () == 0) if (words.size () == 0)
throw std::string (STRING_CMD_GET_NO_DOM); throw std::string (STRING_CMD_GET_NO_DOM);
bool found = false;
std::vector <std::string> results; std::vector <std::string> results;
std::vector <std::string>::iterator word; std::vector <std::string>::iterator word;
for (word = words.begin (); word != words.end (); ++word) for (word = words.begin (); word != words.end (); ++word)
{ {
Task t; Task t;
results.push_back (context.dom.get (*word, t)); std::string result = context.dom.get (*word, t);
results.push_back (result);
if (result != "" &&
result != *word)
found = true;
} }
join (output, " ", results); join (output, " ", results);
output += "\n"; output += "\n";
return 0; return found ? 0 : 1;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////

View file

@ -232,7 +232,7 @@ int CmdShow::execute (std::string& output)
i->first.substr (0, 6) != "alias." && i->first.substr (0, 6) != "alias." &&
i->first.substr (0, 5) != "hook." && i->first.substr (0, 5) != "hook." &&
i->first.substr (0, 4) != "uda." && i->first.substr (0, 4) != "uda." &&
i->first.substr (0, 4) != "default." && i->first.substr (0, 8) != "default." &&
i->first.substr (0, 21) != "urgency.user.project." && i->first.substr (0, 21) != "urgency.user.project." &&
i->first.substr (0, 17) != "urgency.user.tag." && i->first.substr (0, 17) != "urgency.user.tag." &&
i->first.substr (0, 12) != "urgency.uda.") i->first.substr (0, 12) != "urgency.uda.")

View file

@ -29,7 +29,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <signal.h> #include <signal.h>
#include <Context.h> #include <Context.h>
#include <TLSClient.h>
#include <Color.h> #include <Color.h>
#include <text.h> #include <text.h>
#include <util.h> #include <util.h>
@ -87,14 +86,18 @@ int CmdSync::execute (std::string& output)
if (credentials.size () != 3) if (credentials.size () != 3)
throw std::string (STRING_CMD_SYNC_BAD_CRED); throw std::string (STRING_CMD_SYNC_BAD_CRED);
bool trust = context.config.getBoolean ("taskd.trust"); enum TLSClient::trust_level trust = TLSClient::strict;
if (context.config.get ("taskd.trust") == "allow all")
trust = TLSClient::allow_all;
else if (context.config.get ("taskd.trust") == "ignore hostname")
trust = TLSClient::ignore_hostname;
// CA must exist, if provided. // CA must exist, if provided.
File ca (context.config.get ("taskd.ca")); File ca (context.config.get ("taskd.ca"));
if (ca._data != "" && ! ca.exists ()) if (ca._data != "" && ! ca.exists ())
throw std::string (STRING_CMD_SYNC_BAD_CA); throw std::string (STRING_CMD_SYNC_BAD_CA);
if (trust && ca._data != "") if (trust == TLSClient::allow_all && ca._data != "")
throw std::string (STRING_CMD_SYNC_TRUST_CA); throw std::string (STRING_CMD_SYNC_TRUST_CA);
File certificate (context.config.get ("taskd.certificate")); File certificate (context.config.get ("taskd.certificate"));
@ -146,6 +149,7 @@ int CmdSync::execute (std::string& output)
request.setPayload (payload); request.setPayload (payload);
if (context.verbose ("sync"))
out << format (STRING_CMD_SYNC_PROGRESS, connection) out << format (STRING_CMD_SYNC_PROGRESS, connection)
<< "\n"; << "\n";
@ -193,6 +197,7 @@ int CmdSync::execute (std::string& output)
Task dummy; Task dummy;
if (context.tdb2.get (uuid, dummy)) if (context.tdb2.get (uuid, dummy))
{ {
if (context.verbose ("sync"))
out << " " out << " "
<< colorChanged.colorize ( << colorChanged.colorize (
format (STRING_CMD_SYNC_MOD, format (STRING_CMD_SYNC_MOD,
@ -203,6 +208,7 @@ int CmdSync::execute (std::string& output)
} }
else else
{ {
if (context.verbose ("sync"))
out << " " out << " "
<< colorAdded.colorize ( << colorAdded.colorize (
format (STRING_CMD_SYNC_ADD, format (STRING_CMD_SYNC_ADD,
@ -294,6 +300,7 @@ int CmdSync::execute (std::string& output)
status = 1; status = 1;
} }
if (context.verbose ("sync"))
out << "\n"; out << "\n";
output = out.str (); output = out.str ();
@ -319,7 +326,7 @@ bool CmdSync::send (
const std::string& ca, const std::string& ca,
const std::string& certificate, const std::string& certificate,
const std::string& key, const std::string& key,
bool trust, const enum TLSClient::trust_level trust,
const Msg& request, const Msg& request,
Msg& response) Msg& response)
{ {

View file

@ -30,6 +30,7 @@
#include <string> #include <string>
#include <Command.h> #include <Command.h>
#include <Msg.h> #include <Msg.h>
#include <TLSClient.h>
class CmdSync : public Command class CmdSync : public Command
{ {
@ -38,7 +39,7 @@ public:
int execute (std::string&); int execute (std::string&);
private: private:
bool send (const std::string&, const std::string&, const std::string&, const std::string&, bool, const Msg&, Msg&); bool send (const std::string&, const std::string&, const std::string&, const std::string&, const enum TLSClient::trust_level, const Msg&, Msg&);
}; };
#endif #endif

View file

@ -43,8 +43,6 @@ void handleRecurrence ();
Date getNextRecurrence (Date&, std::string&); Date getNextRecurrence (Date&, std::string&);
bool generateDueDates (Task&, std::vector <Date>&); bool generateDueDates (Task&, std::vector <Date>&);
void updateRecurrenceMask (Task&); void updateRecurrenceMask (Task&);
int getDueState (const std::string&);
int getDueState (const Date&);
bool nag (Task&); bool nag (Task&);
// rules.cpp // rules.cpp

View file

@ -382,71 +382,6 @@ void updateRecurrenceMask (Task& task)
} }
} }
////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue. Returns
// 0 = not due at all
// 1 = imminent
// 2 = today
// 3 = overdue
int getDueState (const std::string& due)
{
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
// rightNow is the current date + time.
static Date rightNow;
Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());
if (dt < rightNow)
return 3;
if (rightNow.sameDay (dt))
return 2;
int imminentperiod = context.config.getInteger ("due");
if (imminentperiod == 0)
return 1;
Date imminentDay = thisDay + imminentperiod * 86400;
if (dt < imminentDay)
return 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue. Returns
// 0 = not due at all
// 1 = imminent
// 2 = today
// 3 = overdue
int getDueState (const Date& due)
{
// rightNow is the current date + time.
static Date rightNow;
Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());
if (due < rightNow)
return 3;
if (rightNow.sameDay (due))
return 2;
int imminentperiod = context.config.getInteger ("due");
if (imminentperiod == 0)
return 1;
Date imminentDay = thisDay + imminentperiod * 86400;
if (due < imminentDay)
return 1;
return 0;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Returns a Boolean indicator as to whether a nag message was generated, so // Returns a Boolean indicator as to whether a nag message was generated, so
// that commands can control the number of nag messages displayed (ie one is // that commands can control the number of nag messages displayed (ie one is
@ -477,7 +412,7 @@ bool nag (Task& task)
{ {
if (t->id == task.id) if (t->id == task.id)
{ {
if (getDueState (t->get ("due")) == 3) if (t->getDateState ("due") == Task::dateBeforeToday)
isOverdue = true; isOverdue = true;
std::string priority = t->get ("priority"); std::string priority = t->get ("priority");
@ -486,7 +421,7 @@ bool nag (Task& task)
} }
else if (t->getStatus () == Task::pending) else if (t->getStatus () == Task::pending)
{ {
if (getDueState (t->get ("due")) == 3) if (t->getDateState ("due") == Task::dateBeforeToday)
overdue++; overdue++;
std::string priority = t->get ("priority"); std::string priority = t->get ("priority");

View file

@ -222,7 +222,7 @@ static void colorizeDue (Task& task, const Color& base, Color& c)
Task::status status = task.getStatus (); Task::status status = task.getStatus ();
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted && status != Task::deleted &&
getDueState (task.get ("due")) == 1) task.getDateState ("due") == Task::dateAfterToday)
c.blend (base); c.blend (base);
} }
} }
@ -233,9 +233,10 @@ static void colorizeDueToday (Task& task, const Color& base, Color& c)
if (task.has ("due")) if (task.has ("due"))
{ {
Task::status status = task.getStatus (); Task::status status = task.getStatus ();
Task::dateState dateState = task.getDateState ("due");
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted && status != Task::deleted &&
getDueState (task.get ("due")) == 2) (dateState == Task::dateLaterToday || dateState == Task::dateEarlierToday))
c.blend (base); c.blend (base);
} }
} }
@ -248,7 +249,7 @@ static void colorizeOverdue (Task& task, const Color& base, Color& c)
Task::status status = task.getStatus (); Task::status status = task.getStatus ();
if (status != Task::completed && if (status != Task::completed &&
status != Task::deleted && status != Task::deleted &&
getDueState (task.get ("due")) == 3) task.getDateState ("due") == Task::dateBeforeToday)
c.blend (base); c.blend (base);
} }
} }

View file

@ -27,7 +27,7 @@
use strict; use strict;
use warnings; use warnings;
use Test::More tests => 14; use Test::More tests => 22;
# '15min' is seen as '15', 'min', not '15min' duration. # '15min' is seen as '15', 'min', not '15min' duration.
my $output = qx{../src/calc --debug --noambiguous '12 * 3600 + 34 * 60 + 56'}; my $output = qx{../src/calc --debug --noambiguous '12 * 3600 + 34 * 60 + 56'};
@ -36,6 +36,7 @@ like ($output, qr/token infix '3600' Number/, 'Number 3600');
like ($output, qr/token infix '34' Number/, 'Number 60'); like ($output, qr/token infix '34' Number/, 'Number 60');
like ($output, qr/token infix '60' Number/, 'Number 60'); like ($output, qr/token infix '60' Number/, 'Number 60');
like ($output, qr/token infix '56' Number/, 'Number 56'); like ($output, qr/token infix '56' Number/, 'Number 56');
like ($output, qr/no errors/ms, 'No syntax errors');
like ($output, qr/^45296$/ms, 'Result 45296'); like ($output, qr/^45296$/ms, 'Result 45296');
unlike ($output, qr/Error/, 'No errors'); unlike ($output, qr/Error/, 'No errors');
@ -48,5 +49,14 @@ like ($output, qr/token postfix '56' Number/, 'Number 56');
like ($output, qr/^45296$/ms, 'Result 45296'); like ($output, qr/^45296$/ms, 'Result 45296');
unlike ($output, qr/Error/, 'No errors'); unlike ($output, qr/Error/, 'No errors');
$output = qx{../src/calc --debug --noambiguous '2--3'};
like ($output, qr/token infix '2' Number/ms, 'Number 2');
like ($output, qr/token infix '-' Operator/ms, 'Operator -');
like ($output, qr/token infix '_neg_' Operator/ms, 'operator _neg_');
like ($output, qr/token infix '3' Number/ms, 'Number 3');
like ($output, qr/no errors/ms, 'No syntax errors');
like ($output, qr/^5$/ms, 'Result 5');
unlike ($output, qr/Error/, 'No errors');
exit 0; exit 0;

View file

@ -27,6 +27,7 @@
use strict; use strict;
use warnings; use warnings;
use POSIX qw(mktime);
use Test::More tests => 7; use Test::More tests => 7;
# Ensure environment has no influence. # Ensure environment has no influence.
@ -49,6 +50,13 @@ qx{../src/task rc:count.rc add three 2>&1};
qx{../src/task rc:count.rc 2 delete 2>&1}; qx{../src/task rc:count.rc 2 delete 2>&1};
qx{../src/task rc:count.rc add four wait:eom 2>&1}; qx{../src/task rc:count.rc add four wait:eom 2>&1};
TODO: {
my @today = localtime;
my @tomorrow = @today;
$tomorrow[3] += 1;
@tomorrow = localtime(mktime(@tomorrow));
local $TODO = 'can fail when today == eom' if $today[4] != $tomorrow[4];
# TODO This fails when today == eom. For example, on 2013-04-30 at 8:00:00, the # TODO This fails when today == eom. For example, on 2013-04-30 at 8:00:00, the
# value for 'eom' is 2013-04-30 0:00:00, which is already past due, which # value for 'eom' is 2013-04-30 0:00:00, which is already past due, which
# means a second child task is generated. This would be fixed by 'eom' # means a second child task is generated. This would be fixed by 'eom'
@ -73,6 +81,7 @@ like ($output, qr/^2\n/ms, 'count description.startswith:f');
diag ("Problem: the next test fails at EOM"); diag ("Problem: the next test fails at EOM");
$output = qx{../src/task rc:count.rc count due.any: 2>&1}; $output = qx{../src/task rc:count.rc count due.any: 2>&1};
like ($output, qr/^1\n/ms, 'count due.any:'); like ($output, qr/^1\n/ms, 'count due.any:');
}
# Cleanup. # Cleanup.
unlink qw(pending.data completed.data undo.data backlog.data count.rc); unlink qw(pending.data completed.data undo.data backlog.data count.rc);

View file

@ -35,16 +35,18 @@ Context context;
// A few hard-coded symbols. // A few hard-coded symbols.
bool get (const std::string& name, Variant& value) bool get (const std::string& name, Variant& value)
{ {
if (name == "pi") {value = Variant (3.14159165); return true;} if (name == "x")
else if (name == "x") {value = Variant (true); return true;} value = Variant (true);
else
return false; return false;
return true;
} }
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
int main (int argc, char** argv) int main (int argc, char** argv)
{ {
UnitTest t (43); UnitTest t (46);
// Test the source independently. // Test the source independently.
Variant v; Variant v;
@ -54,10 +56,6 @@ int main (int argc, char** argv)
t.is (v.type (), Variant::type_boolean, "get(x) --> boolean"); t.is (v.type (), Variant::type_boolean, "get(x) --> boolean");
t.is (v.get_bool (), true, "get(x) --> true"); t.is (v.get_bool (), true, "get(x) --> true");
t.ok (get ("pi", v), "true <-- get(pi)");
t.is (v.type (), Variant::type_real, "get(pi) --> real");
t.is (v.get_real (), 3.141592, 0.00001, "get(pi) --> 3.14159265");
Eval e; Eval e;
e.addSource (get); e.addSource (get);
Variant result; Variant result;
@ -138,6 +136,20 @@ int main (int argc, char** argv)
t.is (result.type (), Variant::type_integer, "infix '2*3+1' --> integer"); t.is (result.type (), Variant::type_integer, "infix '2*3+1' --> integer");
t.is (result.get_integer (), 7, "infix '2*3+1' --> 7"); t.is (result.get_integer (), 7, "infix '2*3+1' --> 7");
// TW-1254 - Unary minus support.
e.evaluateInfixExpression ("2--3", result);
t.is (result.type (), Variant::type_integer, "infix '2--3' --> integer");
t.is (result.get_integer (), 5, "infix '2--3' --> 5");
//e.debug ();
e.evaluateInfixExpression ("!false", result);
t.is (result.type (), Variant::type_boolean, "infix '!false' --> boolean");
t.is (result.get_bool (), true, "infix '!false' --> true");
e.evaluateInfixExpression ("!true", result);
t.is (result.type (), Variant::type_boolean, "infix '!true' --> boolean");
t.is (result.get_bool (), false, "infix '!true' --> false");
return 0; return 0;
} }

View file

@ -94,18 +94,21 @@ int main (int argc, char** argv)
local_now->tm_hour = 0; local_now->tm_hour = 0;
local_now->tm_min = 0; local_now->tm_min = 0;
local_now->tm_sec = 0; local_now->tm_sec = 0;
local_now->tm_isdst = -1;
time_t local = mktime (local_now); time_t local = mktime (local_now);
std::cout << "# local midnight today " << local << "\n"; std::cout << "# local midnight today " << local << "\n";
local_now->tm_year = 2013 - 1900; local_now->tm_year = 2013 - 1900;
local_now->tm_mon = 12 - 1; local_now->tm_mon = 12 - 1;
local_now->tm_mday = 6; local_now->tm_mday = 6;
local_now->tm_isdst = 0;
time_t local6 = mktime (local_now); time_t local6 = mktime (local_now);
std::cout << "# local midnight 2013-12-06 " << local6 << "\n"; std::cout << "# local midnight 2013-12-06 " << local6 << "\n";
local_now->tm_year = 2013 - 1900; local_now->tm_year = 2013 - 1900;
local_now->tm_mon = 12 - 1; local_now->tm_mon = 12 - 1;
local_now->tm_mday = 1; local_now->tm_mday = 1;
local_now->tm_isdst = 0;
time_t local1 = mktime (local_now); time_t local1 = mktime (local_now);
std::cout << "# local midnight 2013-12-01 " << local1 << "\n"; std::cout << "# local midnight 2013-12-01 " << local1 << "\n";
@ -116,18 +119,21 @@ int main (int argc, char** argv)
utc_now->tm_hour = 0; utc_now->tm_hour = 0;
utc_now->tm_min = 0; utc_now->tm_min = 0;
utc_now->tm_sec = 0; utc_now->tm_sec = 0;
utc_now->tm_isdst = -1;
time_t utc = timegm (utc_now); time_t utc = timegm (utc_now);
std::cout << "# utc midnight today " << utc << "\n"; std::cout << "# utc midnight today " << utc << "\n";
utc_now->tm_year = 2013 - 1900; utc_now->tm_year = 2013 - 1900;
utc_now->tm_mon = 12 - 1; utc_now->tm_mon = 12 - 1;
utc_now->tm_mday = 6; utc_now->tm_mday = 6;
utc_now->tm_isdst = 0;
time_t utc6 = timegm (utc_now); time_t utc6 = timegm (utc_now);
std::cout << "# utc midnight 2013-12-06 " << utc6 << "\n"; std::cout << "# utc midnight 2013-12-06 " << utc6 << "\n";
utc_now->tm_year = 2013 - 1900; utc_now->tm_year = 2013 - 1900;
utc_now->tm_mon = 12 - 1; utc_now->tm_mon = 12 - 1;
utc_now->tm_mday = 1; utc_now->tm_mday = 1;
utc_now->tm_isdst = 0;
time_t utc1 = timegm (utc_now); time_t utc1 = timegm (utc_now);
std::cout << "# utc midnight 2013-12-01 " << utc1 << "\n"; std::cout << "# utc midnight 2013-12-01 " << utc1 << "\n";

View file

@ -1,5 +1,6 @@
#! /bin/sh #! /bin/sh
rc=0
if [ x"$1" = x"--verbose" ]; if [ x"$1" = x"--verbose" ];
then then
for i in ${TESTBLOB} for i in ${TESTBLOB}
@ -9,9 +10,10 @@ then
while read LINE while read LINE
do do
echo "$LINE" echo "$LINE"
done < test.log done < test.log || rc=1
rm test.log rm test.log
done done
exit $rc
else else
date > all.log date > all.log
@ -37,7 +39,7 @@ else
COUNT=`expr $COUNT + 1` COUNT=`expr $COUNT + 1`
fi fi
$i >> all.log 2>&1 $i >> all.log 2>&1 || rc=1
done done
if [ $BAR -eq 1 ]; then if [ $BAR -eq 1 ]; then
@ -53,4 +55,5 @@ else
printf "Fail: %5d\n" `grep -c '^not' all.log` printf "Fail: %5d\n" `grep -c '^not' all.log`
printf "Skipped: %5d\n" `grep -c '^skip' all.log` printf "Skipped: %5d\n" `grep -c '^skip' all.log`
printf "Runtime: %5d seconds\n" $RUNTIME printf "Runtime: %5d seconds\n" $RUNTIME
exit $rc
fi fi

View file

@ -28,6 +28,7 @@
#include <iomanip> #include <iomanip>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <stdlib.h>
#include <test.h> #include <test.h>
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@ -84,6 +85,7 @@ UnitTest::~UnitTest ()
<< " skipped. " << " skipped. "
<< std::setprecision (3) << percentPassed << std::setprecision (3) << percentPassed
<< "% passed.\n"; << "% passed.\n";
exit (_failed > 0);
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

75
test/tw-1300.t Executable file
View file

@ -0,0 +1,75 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import signal
from glob import glob
# Ensure python finds the local simpletap and basetest modules
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import BaseTestCase
class BaseTestBug1300(BaseTestCase):
@classmethod
def prepare(cls):
with open("bug.rc", 'w') as fh:
fh.write("data.location=.\n"
"confirmation=no\n")
def tearDown(self):
"""Needed after each test or setUp will cause duplicated data at start
of the next test.
"""
for file in glob("*.data"):
os.remove(file)
@classmethod
def cleanup(cls):
os.remove("bug.rc")
class TestBug1300(BaseTestBug1300):
def test_dom_exit_status_good(self):
"""If the DOM recognizes a reference, it should return '0'
"""
self.callTaskSuccess(["rc:bug.rc", "_get", "context.program"])
def test_dom_exit_status_bad(self):
"""If the DOM does not recognize a reference, it should return '1'
"""
self.callTaskError(["rc:bug.rc", "_get", "XYZ"])
if __name__ == "__main__":
from simpletap import TAPTestRunner
import unittest
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4

116
test/tw-285.t Executable file
View file

@ -0,0 +1,116 @@
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
################################################################################
##
## Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez.
##
## Permission is hereby granted, free of charge, to any person obtaining a copy
## of this software and associated documentation files (the "Software"), to deal
## in the Software without restriction, including without limitation the rights
## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
## copies of the Software, and to permit persons to whom the Software is
## furnished to do so, subject to the following conditions:
##
## The above copyright notice and this permission notice shall be included
## in all copies or substantial portions of the Software.
##
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
## OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
## THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
## SOFTWARE.
##
## http://www.opensource.org/licenses/mit-license.php
##
################################################################################
import sys
import os
import signal
from glob import glob
# Ensure python finds the local simpletap and basetest modules
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from basetest import BaseTestCase
class BaseTest285(BaseTestCase):
@classmethod
def prepare(cls):
with open("bug.rc", 'w') as fh:
fh.write("data.location=.\n"
"verbose=nothing\n"
"confirmation=no\n")
def setUp(self):
"""Executed before each test in the class"""
# OVERDUE YESTERDAY DUE TODAY TOMORROW WEEK MONTH YEAR
# due:-1week Y - - - - ? ? ?
# due:-1day Y Y - - - ? ? ?
# due:today Y - Y Y - ? ? ?
# due:tomorrow - - Y - Y ? ? ?
# due:3days - - Y - - ? ? ?
# due:1month - - - - - - - ?
# due:1year - - - - - - - -
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_last_week', 'due:-1week'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_yesterday', 'due:-1day'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_earlier_today', 'due:today'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_later_today', 'due:tomorrow'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_three_days', 'due:3days'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_next_month', 'due:1month'])
self.callTaskSuccess(['rc:bug.rc', 'add', 'due_next_year', 'due:1year'])
def tearDown(self):
"""Needed after each test or setUp will cause duplicated data at start
of the next test.
"""
for file in glob("*.data"):
os.remove(file)
@classmethod
def cleanup(cls):
os.remove("bug.rc")
class Test285(BaseTest285):
def test_overdue(self):
"""+OVERDUE"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+OVERDUE", "count"])
self.assertEqual(out, "3\n", "+OVERDUE == 3 tasks")
def test_yesterday(self):
"""+YESTERDAY"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+YESTERDAY", "count"])
self.assertEqual(out, "1\n", "+YESTERDAY == 1 task")
def test_due(self):
"""+DUE"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+DUE", "count"])
self.assertEqual(out, "3\n", "+DUE == 3 task")
def test_today(self):
"""+TODAY"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+TODAY", "count"])
self.assertEqual(out, "1\n", "+TODAY == 1 task")
def test_duetoday(self):
"""+DUETODAY"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+DUETODAY", "count"])
self.assertEqual(out, "1\n", "+DUETODAY == 1 task")
def test_tomorrow(self):
"""+TOMORROW"""
code, out, err = self.callTaskSuccess(["rc:bug.rc", "+TOMORROW", "count"])
self.assertEqual(out, "1\n", "+TOMORROW == 1 task")
if __name__ == "__main__":
from simpletap import TAPTestRunner
import unittest
unittest.main(testRunner=TAPTestRunner())
# vim: ai sts=4 et sw=4