mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-08-23 05:27:47 +02:00
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:
commit
0b1732fcef
39 changed files with 1557 additions and 609 deletions
3
AUTHORS
3
AUTHORS
|
@ -101,6 +101,8 @@ The following submitted code, packages or analysis, and deserve special thanks:
|
|||
Marton Suranyi
|
||||
Nicolas Appriou
|
||||
Jochen Sprickerhof
|
||||
Alexander Sulfrian
|
||||
David Binderman
|
||||
|
||||
Thanks to the following, who submitted detailed bug reports and excellent
|
||||
suggestions:
|
||||
|
@ -203,3 +205,4 @@ suggestions:
|
|||
jck
|
||||
Michele Vetturi
|
||||
Jeremiah Marks
|
||||
Profpatsch
|
||||
|
|
46
ChangeLog
46
ChangeLog
|
@ -1,21 +1,48 @@
|
|||
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).
|
||||
- #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
|
||||
Alves).
|
||||
- #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-255 'Mask' instead of 'iMask' shown in info report (thanks to Benjamin
|
||||
Weber)
|
||||
- TW-261 Easy to create "not deletable" task (thanks to Jan Kunder).
|
||||
- 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-1257 The 'Syncing with <host>:<port>' message ignores verbosity tokens.
|
||||
- TW-1258 Portuguese Localization (thanks to Renato Alves).
|
||||
- TW-1260 New virtual tags YESTERDAY, TOMORROW.
|
||||
- TW-1261 Migrate test bug.360.t to new unit testing framework (thanks to
|
||||
Renato Alves).
|
||||
- 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
|
||||
'affected' verbosity tokens.
|
||||
- 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.
|
||||
- Removed priority counts from the 'projects' report.
|
||||
- New themes: dark-default-16.theme, dark-gray-blue-256.theme
|
||||
|
||||
Bugs
|
||||
- TD-42 Cannot compile taskd - GNUTLS_VERSION undefined in diag.cpp (thanks
|
||||
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).
|
||||
- Added certificate verification to GnuTLS versions < 2.9.10 (thanks to Alexander
|
||||
Sulfrian).
|
||||
- Added certificate hostname verification (thanks to Alexander Sulfrian).
|
||||
- Removed debugging code.
|
||||
|
||||
------ current release ---------------------------
|
||||
|
|
15
DEVELOPER
15
DEVELOPER
|
@ -13,6 +13,10 @@ General Statement
|
|||
suggestions, testing and discussions have taken place there. It is also the
|
||||
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
|
||||
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
|
||||
|
@ -68,6 +72,8 @@ Deprecated Code
|
|||
- Priorities in core. This will be migrated to become a UDA as soon as we
|
||||
have the right support in place for custom sorting.
|
||||
|
||||
- Attribute modifiers.
|
||||
|
||||
New Code Needs
|
||||
This is code that needs to be written, usually down at the C++ function/method
|
||||
level.
|
||||
|
@ -95,7 +101,7 @@ New Code Needs
|
|||
outside the core code, we want to make it be that way.
|
||||
- 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
|
||||
lie outside of the core product, and will be implemented as external scripts
|
||||
|
@ -112,9 +118,9 @@ Documentation Needed
|
|||
- Tutorials
|
||||
- Cookbook
|
||||
|
||||
This is all in the new git://tasktools.git/docs.git repository, where all the
|
||||
documents are sourced as markdown. We welcome everyone with writing skills to
|
||||
help us improve this critical resource.
|
||||
This is all in the new https://git.tasktools.org/projects/ST/repos/tw.org
|
||||
repository, where all the documents are sourced as markdown. We welcome
|
||||
everyone with writing skills to help us improve this critical resource.
|
||||
|
||||
Unit Tests Needed
|
||||
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.
|
||||
2013-09-09 Updated branch info.
|
||||
2014-01-19 Updated for 2.4.0.
|
||||
2014-04-13 Added answers.tasktools.org, corrected URLs.
|
||||
|
|
5
NEWS
5
NEWS
|
@ -5,7 +5,8 @@ New Features in taskwarrior 2.4.0
|
|||
- Removed deprecated commands 'push', 'pull' and 'merge'.
|
||||
- Portuguese (por-PRT) localization.
|
||||
- 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
|
||||
|
||||
|
@ -13,6 +14,8 @@ New commands 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
|
||||
|
||||
Newly deprecated features in taskwarrior 2.4.0
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
.TP
|
||||
|
@ -588,6 +591,9 @@ are:
|
|||
UNTIL Matches if the task expires
|
||||
WAITING Matches if the task is waiting
|
||||
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.
|
||||
Similarly, -BLOCKED is equivalent to +UNBLOCKED.
|
||||
|
|
|
@ -253,7 +253,7 @@ control specific occasions when output is generated. This list may contain:
|
|||
edit Used the verbose template for the 'edit' command
|
||||
special Feedback when applying special tags
|
||||
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,
|
||||
and the "nothing" setting is equivalent to none of the tokens being specified.
|
||||
|
@ -1358,11 +1358,13 @@ using a self-signed certificate. Optional.
|
|||
.RE
|
||||
|
||||
.TP
|
||||
.B taskd.trust=yes|no
|
||||
.B taskd.trust=strict|ignore hostname|allow all
|
||||
.RS
|
||||
If you do not specify a CA certificate when your Taskserver is using a self-
|
||||
signed certificate, you can override the certificate validation by setting this
|
||||
value to 'yes'. Default is not to trust a server certificate.
|
||||
This settings allows you to override the trust level when server certificates
|
||||
are validated. With "allow all", the server certificate is trusted
|
||||
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
|
||||
|
||||
.TP
|
||||
|
|
|
@ -157,9 +157,9 @@ const std::string DOM::get (const std::string& name, const Task& task)
|
|||
std::string canonical;
|
||||
|
||||
// <attr>
|
||||
if (name == "id") return format (task.id);
|
||||
else if (name == "urgency") return format (task.urgency_c ());
|
||||
else if (A3::is_attribute (name, canonical)) return task.get (canonical);
|
||||
if (task.size () && name == "id") return format (task.id);
|
||||
else if (task.size () && name == "urgency") return format (task.urgency_c ());
|
||||
else if (task.size () && A3::is_attribute (name, canonical)) return task.get (canonical);
|
||||
|
||||
// <id>.<name>
|
||||
if (n.getInt (id))
|
||||
|
|
205
src/Dates.cpp
205
src/Dates.cpp
|
@ -78,15 +78,8 @@ static bool isDay (const std::string& name, int& i)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
static bool leapYear (int year)
|
||||
{
|
||||
bool ly = false;
|
||||
|
||||
// (year % 4 == 0) && (year % 100 !=0) OR
|
||||
// (year % 400 == 0)
|
||||
// are leapyears
|
||||
|
||||
if (((!(year % 4)) && (year % 100)) || (!(year % 400))) ly = true;
|
||||
|
||||
return ly;
|
||||
return ((!(year % 4)) && (year % 100)) ||
|
||||
(!(year % 400));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -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)
|
||||
{
|
||||
time_t now = time (NULL);
|
||||
|
@ -209,6 +235,69 @@ bool namedDates (const std::string& name, Variant& value)
|
|||
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")
|
||||
{
|
||||
struct tm* t = localtime (&now);
|
||||
|
@ -256,19 +345,97 @@ bool namedDates (const std::string& name, Variant& value)
|
|||
value = Variant (mktime (t), Variant::type_date);
|
||||
}
|
||||
|
||||
// TODO
|
||||
else if (name == "midsommar")
|
||||
{
|
||||
/*
|
||||
{s,e}o{w,q,ww,cw}
|
||||
|
||||
midsommar
|
||||
midsommarafton
|
||||
23rd
|
||||
for (int midsommar = 20; midsommar <= 26; midsommar++)
|
||||
{
|
||||
Date then (6, midsommar, today.year ());
|
||||
if (6 == then.dayOfWeek ())
|
||||
{
|
||||
_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
|
||||
return false;
|
||||
|
||||
|
|
56
src/E9.cpp
56
src/E9.cpp
|
@ -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_match (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 == "_notag_") operator_hastag (result, right, true, task);
|
||||
else
|
||||
|
@ -252,8 +248,6 @@ void E9::operator_not (Arg& result, Arg& right)
|
|||
result._value = "false";
|
||||
else
|
||||
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._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";
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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;
|
||||
|
||||
// std::cout << "# " << left << " <operator_gt> " << right << " --> " << result << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -582,8 +560,6 @@ void E9::operator_inequal (
|
|||
result._value = result._value == "false"
|
||||
? "true"
|
||||
: "false";
|
||||
|
||||
// std::cout << "# " << left << " <operator_inequal> " << right << " --> " << result << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -656,8 +632,6 @@ void E9::operator_equal (
|
|||
? "true"
|
||||
: "false";
|
||||
}
|
||||
|
||||
// std::cout << "# " << left << " <operator_equal> " << right << " --> " << result << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -696,8 +670,6 @@ void E9::operator_match (
|
|||
}
|
||||
else
|
||||
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";
|
||||
else
|
||||
result._value = invert ? "true" : "false";
|
||||
|
||||
// std::cout << "# tags" << (invert ? " <operator_notag> " : " <operator_hastag> ") << right << " --> " << result << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
4
src/E9.h
4
src/E9.h
|
@ -61,10 +61,6 @@ private:
|
|||
void operator_equal (Arg&, Arg&, Arg&, bool);
|
||||
void operator_match (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&);
|
||||
|
||||
const Arg coerce (const Arg&, const Arg::type);
|
||||
|
|
453
src/Eval.cpp
453
src/Eval.cpp
|
@ -28,6 +28,7 @@
|
|||
#include <time.h>
|
||||
#include <Eval.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Supported operators, borrowed from C++, particularly the precedence.
|
||||
// Note: table is sorted by length of operator string, so searches match
|
||||
// longest first.
|
||||
|
@ -40,43 +41,63 @@ static struct
|
|||
} operators[] =
|
||||
{
|
||||
// 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
|
||||
{ ">", 10, 'b', 'l' }, // Greater than
|
||||
{ "~", 9, 'b', 'l' }, // Regex match
|
||||
{ "!", 15, 'u', 'r' }, // Not
|
||||
|
||||
{ "_hastag_", 9, 'b', 'l'}, // +tag [Pseudo-op]
|
||||
{ "_notag_", 9, 'b', 'l'}, // -tag [Pseudo-op]
|
||||
{ "!", 15, 'u', 'r' }, // Unary not
|
||||
{ "_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' }, // Division
|
||||
{ "%", 13, 'b', 'l' }, // Modulus
|
||||
|
||||
{ "+", 12, 'b', 'l' }, // Addition
|
||||
{ "-", 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
|
||||
|
||||
{ "=", 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 end
|
||||
};
|
||||
|
||||
#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 ()
|
||||
: _ambiguity (true)
|
||||
, _debug (false)
|
||||
{
|
||||
addSource (namedConstants);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -100,10 +121,15 @@ void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const
|
|||
std::string token;
|
||||
Lexer::Type type;
|
||||
while (l.token (token, type))
|
||||
{
|
||||
tokens.push_back (std::pair <std::string, Lexer::Type> (token, type));
|
||||
|
||||
// Parse for syntax checking and operator replacement.
|
||||
infixParse (tokens);
|
||||
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.
|
||||
|
@ -163,7 +189,7 @@ void Eval::evaluatePostfixStack (
|
|||
std::vector <std::pair <std::string, Lexer::Type> >::const_iterator token;
|
||||
for (token = tokens.begin (); token != tokens.end (); ++token)
|
||||
{
|
||||
// Unary operator.
|
||||
// Unary operators.
|
||||
if (token->second == Lexer::typeOperator &&
|
||||
token->first == "!")
|
||||
{
|
||||
|
@ -171,6 +197,18 @@ void Eval::evaluatePostfixStack (
|
|||
values.pop_back ();
|
||||
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.
|
||||
else if (token->second == Lexer::typeOperator)
|
||||
|
@ -210,6 +248,8 @@ void Eval::evaluatePostfixStack (
|
|||
|
||||
values.push_back (left);
|
||||
}
|
||||
|
||||
// Literals and identifiers.
|
||||
else
|
||||
{
|
||||
Variant v (token->first);
|
||||
|
@ -230,6 +270,7 @@ void Eval::evaluatePostfixStack (
|
|||
|
||||
case Lexer::typeIdentifier:
|
||||
{
|
||||
bool found = false;
|
||||
std::vector <bool (*)(const std::string&, Variant&)>::const_iterator source;
|
||||
for (source = _sources.begin (); source != _sources.end (); ++source)
|
||||
{
|
||||
|
@ -237,12 +278,13 @@ void Eval::evaluatePostfixStack (
|
|||
{
|
||||
if (_debug)
|
||||
std::cout << "# [" << values.size () << "] eval source '" << token->first << "' --> '" << (std::string) v << "'\n";
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// An identifier that fails lookup is a string.
|
||||
if (source == _sources.end ())
|
||||
if (!found)
|
||||
{
|
||||
v.cast (Variant::type_string);
|
||||
if (_debug)
|
||||
|
@ -279,6 +321,373 @@ void Eval::evaluatePostfixStack (
|
|||
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.
|
||||
// http://en.wikipedia.org/wiki/Shunting-yard_algorithm
|
||||
|
@ -311,7 +720,7 @@ void Eval::evaluatePostfixStack (
|
|||
// Pop the operator onto the output queue.
|
||||
// Exit.
|
||||
//
|
||||
void Eval:: infixToPostfix (
|
||||
void Eval::infixToPostfix (
|
||||
std::vector <std::pair <std::string, Lexer::Type> >& infix) const
|
||||
{
|
||||
// Short circuit.
|
||||
|
|
11
src/Eval.h
11
src/Eval.h
|
@ -50,6 +50,17 @@ public:
|
|||
private:
|
||||
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 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;
|
||||
|
||||
private:
|
||||
|
|
141
src/ISO8601.cpp
141
src/ISO8601.cpp
|
@ -25,6 +25,7 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <cmake.h>
|
||||
//#include <iostream> // TODO Remove
|
||||
#include <ISO8601.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -603,6 +604,8 @@ bool ISO8601d::validate ()
|
|||
// long tm_gmtoff; offset from UTC in seconds
|
||||
void ISO8601d::resolve ()
|
||||
{
|
||||
//std::cout << "# start ------------------------\n";
|
||||
|
||||
// Don't touch the original values.
|
||||
int year = _year;
|
||||
int month = _month;
|
||||
|
@ -613,55 +616,69 @@ void ISO8601d::resolve ()
|
|||
int seconds = _seconds;
|
||||
int offset = _offset;
|
||||
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};
|
||||
|
||||
// Requests that mktime determine summer time effect.
|
||||
t.tm_isdst = -1;
|
||||
|
||||
// Determine local time.
|
||||
// Get current time.
|
||||
time_t now = time (NULL);
|
||||
struct tm* local_now = localtime (&now);
|
||||
struct tm* utc_now = gmtime (&now);
|
||||
//std::cout << "# now=" << now << "\n";
|
||||
|
||||
// What is a complete TZ?
|
||||
// utc
|
||||
// offset
|
||||
// local (get default)
|
||||
if (utc)
|
||||
offset = 0;
|
||||
else if (! offset)
|
||||
// A UTC offset needs to be accommodated. Once the offset is subtracted,
|
||||
// only local and UTC times remain.
|
||||
if (offset)
|
||||
{
|
||||
#ifdef HAVE_TM_GMTOFF
|
||||
offset = local_now->tm_gmtoff;
|
||||
#else
|
||||
// TODO Umm...
|
||||
#endif
|
||||
seconds -= offset;
|
||||
now -= offset;
|
||||
utc = true;
|
||||
}
|
||||
|
||||
// Subtract the offset, to project local to UTC.
|
||||
seconds -= offset;
|
||||
// Get 'now' in the relevant location.
|
||||
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
|
||||
// it refers to tomorrow.
|
||||
int seconds_utc_now = utc_now->tm_hour * 3600 +
|
||||
utc_now->tm_min * 60 +
|
||||
utc_now->tm_sec;
|
||||
int seconds_now = (t_now->tm_hour * 3600) +
|
||||
(t_now->tm_min * 60) +
|
||||
t_now->tm_sec;
|
||||
//std::cout << "# seconds_now=" << seconds_now << "\n";
|
||||
|
||||
// Project forward one day if the specified seconds are earlier in the day
|
||||
// than the current seconds.
|
||||
if (year == 0 &&
|
||||
month == 0 &&
|
||||
day == 0 &&
|
||||
week == 0 &&
|
||||
weekday == 0 &&
|
||||
seconds < seconds_utc_now)
|
||||
seconds < seconds_now)
|
||||
{
|
||||
//std::cout << "# earlier today, therefore seconds += 86400\n"
|
||||
// << "# seconds=" << seconds << "\n";
|
||||
seconds += 86400;
|
||||
}
|
||||
|
||||
// Conversion of week + weekday to julian.
|
||||
// Convert week + weekday --> julian.
|
||||
if (week)
|
||||
{
|
||||
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
|
||||
{
|
||||
// Default values for year, month, day:
|
||||
|
@ -673,9 +690,9 @@ void ISO8601d::resolve ()
|
|||
//
|
||||
if (year == 0)
|
||||
{
|
||||
year = local_now->tm_year + 1900;
|
||||
month = local_now->tm_mon + 1;
|
||||
day = local_now->tm_mday;
|
||||
year = t_now->tm_year + 1900;
|
||||
month = t_now->tm_mon + 1;
|
||||
day = t_now->tm_mday;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -688,41 +705,73 @@ void ISO8601d::resolve ()
|
|||
day = 1;
|
||||
}
|
||||
}
|
||||
//std::cout << "# Applied default y m d\n"
|
||||
// << "# year=" << year << "\n"
|
||||
// << "# month=" << month << "\n"
|
||||
// << "# day=" << day << "\n";
|
||||
|
||||
if (julian)
|
||||
{
|
||||
month = 1;
|
||||
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_mon = month - 1;
|
||||
t.tm_mday = day;
|
||||
|
||||
// What is a complete time spec?
|
||||
// seconds
|
||||
if (seconds)
|
||||
{
|
||||
if (seconds > 86400)
|
||||
{
|
||||
//std::cout << "# seconds=" << seconds << " is more than a day\n";
|
||||
int days = seconds / 86400;
|
||||
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_min = (seconds % 3600) / 60;
|
||||
t.tm_sec = seconds % 60;
|
||||
}
|
||||
else
|
||||
{
|
||||
// User-provided default.
|
||||
t.tm_hour = _default_seconds / 3600;
|
||||
t.tm_min = (_default_seconds % 3600) / 60;
|
||||
t.tm_sec = _default_seconds % 60;
|
||||
}
|
||||
|
||||
_value = timegm (&t);
|
||||
//std::cout << "# Final t\n"
|
||||
// << "# tm_year=" << t.tm_year << "\n"
|
||||
// << "# tm_mon=" << t.tm_mon << "\n"
|
||||
// << "# tm_mday=" << t.tm_mday << "\n"
|
||||
// << "# tm_hour=" << t.tm_hour << "\n"
|
||||
// << "# 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 ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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";
|
||||
*/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -59,6 +59,7 @@ private:
|
|||
int dayOfWeek (int, int, int);
|
||||
bool validate ();
|
||||
void resolve ();
|
||||
void dump ();
|
||||
|
||||
public:
|
||||
bool _ambiguity;
|
||||
|
|
|
@ -233,6 +233,9 @@ void TF2::commit ()
|
|||
{
|
||||
if (_file.open ())
|
||||
{
|
||||
if (context.config.getBoolean ("locking"))
|
||||
_file.waitForLock ();
|
||||
|
||||
// Truncate the file and rewrite.
|
||||
_file.truncate ();
|
||||
|
||||
|
|
|
@ -45,13 +45,12 @@
|
|||
#include <TLSClient.h>
|
||||
#include <text.h>
|
||||
#include <i18n.h>
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
#define MAX_BUF 16384
|
||||
|
||||
static int verify_certificate_callback (gnutls_session_t);
|
||||
|
||||
static bool trust_override = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
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)
|
||||
{
|
||||
if (trust_override)
|
||||
return 0;
|
||||
|
||||
// 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;
|
||||
const TLSClient* client = (TLSClient*) gnutls_session_get_ptr (session);
|
||||
return client->verify_certificate();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -102,10 +69,13 @@ TLSClient::TLSClient ()
|
|||
: _ca ("")
|
||||
, _cert ("")
|
||||
, _key ("")
|
||||
, _host ("")
|
||||
, _port ("")
|
||||
, _session(0)
|
||||
, _socket (0)
|
||||
, _limit (0)
|
||||
, _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 (trust_override)
|
||||
if (_trust == allow_all)
|
||||
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
|
||||
std::cout << "c: INFO Server certificate trust verified.\n";
|
||||
}
|
||||
|
@ -183,6 +155,10 @@ void TLSClient::init (
|
|||
throw std::string ("Missing CERT file.");
|
||||
|
||||
#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);
|
||||
#endif
|
||||
gnutls_init (&_session, GNUTLS_CLIENT);
|
||||
|
@ -208,9 +184,12 @@ void TLSClient::init (
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TLSClient::connect (const std::string& host, const std::string& port)
|
||||
{
|
||||
// Store the host name, so the verification callback can access it during the
|
||||
// handshake below.
|
||||
gnutls_session_set_ptr (_session, (void*) host.c_str ());
|
||||
_host = host;
|
||||
_port = port;
|
||||
|
||||
// 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.
|
||||
struct addrinfo hints = {0};
|
||||
|
@ -254,7 +233,7 @@ void TLSClient::connect (const std::string& host, const std::string& port)
|
|||
#if GNUTLS_VERSION_NUMBER >= 0x030109
|
||||
gnutls_transport_set_int (_session, _socket);
|
||||
#else
|
||||
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (long) _socket);
|
||||
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) _socket);
|
||||
#endif
|
||||
|
||||
// Perform the TLS handshake
|
||||
|
@ -267,6 +246,20 @@ void TLSClient::connect (const std::string& host, const std::string& port)
|
|||
if (ret < 0)
|
||||
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 GNUTLS_VERSION_NUMBER >= 0x03010a
|
||||
|
@ -285,6 +278,76 @@ void TLSClient::bye ()
|
|||
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)
|
||||
{
|
||||
|
|
|
@ -34,15 +34,18 @@
|
|||
class TLSClient
|
||||
{
|
||||
public:
|
||||
enum trust_level { strict, ignore_hostname, allow_all };
|
||||
|
||||
TLSClient ();
|
||||
~TLSClient ();
|
||||
void limit (int);
|
||||
void debug (int);
|
||||
void trust (bool);
|
||||
void trust (const enum trust_level);
|
||||
void ciphers (const std::string&);
|
||||
void init (const std::string&, const std::string&, const std::string&);
|
||||
void connect (const std::string&, const std::string&);
|
||||
void bye ();
|
||||
int verify_certificate() const;
|
||||
|
||||
void send (const std::string&);
|
||||
void recv (std::string&);
|
||||
|
@ -52,11 +55,14 @@ private:
|
|||
std::string _cert;
|
||||
std::string _key;
|
||||
std::string _ciphers;
|
||||
std::string _host;
|
||||
std::string _port;
|
||||
gnutls_certificate_credentials_t _credentials;
|
||||
gnutls_session_t _session;
|
||||
int _socket;
|
||||
int _limit;
|
||||
bool _debug;
|
||||
enum trust_level _trust;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
61
src/Task.cpp
61
src/Task.cpp
|
@ -24,6 +24,7 @@
|
|||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <iostream> // TODO Remove.
|
||||
#include <cmake.h>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
|
@ -323,6 +324,40 @@ void Task::setStatus (Task::status status)
|
|||
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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 &&
|
||||
status != Task::deleted)
|
||||
{
|
||||
if (getDueState (get ("due")) == 1)
|
||||
Task::dateState state = getDateState ("due");
|
||||
if (state == dateAfterToday ||
|
||||
state == dateEarlierToday ||
|
||||
state == dateLaterToday)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -381,7 +419,9 @@ bool Task::is_duetoday () const
|
|||
if (status != Task::completed &&
|
||||
status != Task::deleted)
|
||||
{
|
||||
if (getDueState (get ("due")) == 2)
|
||||
Task::dateState state = getDateState ("due");
|
||||
if (state == dateEarlierToday ||
|
||||
state == dateLaterToday)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -479,7 +519,9 @@ bool Task::is_overdue () const
|
|||
if (status != Task::completed &&
|
||||
status != Task::deleted)
|
||||
{
|
||||
if (getDueState (get ("due")) == 3)
|
||||
Task::dateState state = getDateState ("due");
|
||||
if (state == dateEarlierToday ||
|
||||
state == dateBeforeToday)
|
||||
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
|
||||
{
|
||||
// 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 == "ANNOTATED") return hasAnnotations ();
|
||||
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.
|
||||
std::vector <std::string> tags;
|
||||
|
|
|
@ -72,6 +72,9 @@ public:
|
|||
// Status values.
|
||||
enum status {pending, completed, deleted, recurring, waiting};
|
||||
|
||||
// Date state values.
|
||||
enum dateState {dateNotDue, dateAfterToday, dateLaterToday, dateEarlierToday, dateBeforeToday};
|
||||
|
||||
// Public data.
|
||||
int id;
|
||||
float urgency_value;
|
||||
|
@ -117,6 +120,8 @@ public:
|
|||
status getStatus () const;
|
||||
void setStatus (status);
|
||||
|
||||
dateState getDateState (const std::string&) const;
|
||||
|
||||
int getTagCount () const;
|
||||
bool hasTag (const std::string&) const;
|
||||
void addTag (const std::string&);
|
||||
|
|
|
@ -1302,6 +1302,8 @@ Variant::operator std::string () const
|
|||
{
|
||||
time_t t = _duration;
|
||||
|
||||
if (t)
|
||||
{
|
||||
int seconds = t % 60; t /= 60;
|
||||
int minutes = t % 60; t /= 60;
|
||||
int hours = t % 24; t /= 24;
|
||||
|
@ -1325,6 +1327,11 @@ Variant::operator std::string () const
|
|||
|
||||
return s.str ();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "P0S";
|
||||
}
|
||||
}
|
||||
|
||||
case type_unknown:
|
||||
throw std::string ("Cannot render an unknown type.");
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
extern Context context;
|
||||
|
||||
// Helper macro.
|
||||
#define LOC(y,x) (((y) * (width + 1)) + (x))
|
||||
#define LOC(y,x) (((y) * (_width + 1)) + (x))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class Bar
|
||||
|
@ -53,26 +53,26 @@ public:
|
|||
~Bar ();
|
||||
|
||||
public:
|
||||
int offset; // from left of chart
|
||||
std::string major_label; // x-axis label, major (year/-/month)
|
||||
std::string minor_label; // x-axis label, minor (month/week/day)
|
||||
int pending; // Number of pending tasks in period
|
||||
int started; // Number of started tasks in period
|
||||
int done; // Number of done tasks in period
|
||||
int added; // Number added in period
|
||||
int removed; // Number removed in period
|
||||
int _offset; // from left of chart
|
||||
std::string _major_label; // x-axis label, major (year/-/month)
|
||||
std::string _minor_label; // x-axis label, minor (month/week/day)
|
||||
int _pending; // Number of pending tasks in period
|
||||
int _started; // Number of started tasks in period
|
||||
int _done; // Number of done tasks in period
|
||||
int _added; // Number added in period
|
||||
int _removed; // Number removed in period
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
Bar::Bar ()
|
||||
: offset (0)
|
||||
, major_label ("")
|
||||
, minor_label ("")
|
||||
, pending (0)
|
||||
, started (0)
|
||||
, done (0)
|
||||
, added (0)
|
||||
, removed (0)
|
||||
: _offset (0)
|
||||
, _major_label ("")
|
||||
, _minor_label ("")
|
||||
, _pending (0)
|
||||
, _started (0)
|
||||
, _done (0)
|
||||
, _added (0)
|
||||
, _removed (0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -87,14 +87,14 @@ Bar& Bar::operator= (const Bar& other)
|
|||
{
|
||||
if (this != &other)
|
||||
{
|
||||
offset = other.offset;
|
||||
major_label = other.major_label;
|
||||
minor_label = other.minor_label;
|
||||
pending = other.pending;
|
||||
started = other.started;
|
||||
done = other.done;
|
||||
added = other.added;
|
||||
removed = other.removed;
|
||||
_offset = other._offset;
|
||||
_major_label = other._major_label;
|
||||
_minor_label = other._minor_label;
|
||||
_pending = other._pending;
|
||||
_started = other._started;
|
||||
_done = other._done;
|
||||
_added = other._added;
|
||||
_removed = other._removed;
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -159,25 +159,25 @@ private:
|
|||
void calculateRates (std::vector <time_t>&);
|
||||
|
||||
public:
|
||||
int width; // Terminal width
|
||||
int height; // Terminal height
|
||||
int graph_width; // Width of plot area
|
||||
int graph_height; // Height of plot area
|
||||
int max_value; // Largest combined bar value
|
||||
int max_label; // Longest y-axis label
|
||||
std::vector <int> labels; // Y-axis labels
|
||||
int estimated_bars; // Estimated bar count
|
||||
int actual_bars; // Calculated bar count
|
||||
std::map <time_t, Bar> bars; // Epoch-indexed set of bars
|
||||
Date earliest; // Date of earliest estimated bar
|
||||
int carryover_done; // Number of 'done' tasks prior to chart range
|
||||
char period; // D, W, M
|
||||
std::string title; // Additional description
|
||||
std::string grid; // String representing grid of characters
|
||||
int _width; // Terminal width
|
||||
int _height; // Terminal height
|
||||
int _graph_width; // Width of plot area
|
||||
int _graph_height; // Height of plot area
|
||||
int _max_value; // Largest combined bar value
|
||||
int _max_label; // Longest y-axis label
|
||||
std::vector <int> _labels; // Y-axis labels
|
||||
int _estimated_bars; // Estimated bar count
|
||||
int _actual_bars; // Calculated bar count
|
||||
std::map <time_t, Bar> _bars; // Epoch-indexed set of bars
|
||||
Date _earliest; // Date of earliest estimated bar
|
||||
int _carryover_done; // Number of 'done' tasks prior to chart range
|
||||
char _period; // D, W, M
|
||||
std::string _title; // Additional description
|
||||
std::string _grid; // String representing grid of characters
|
||||
|
||||
float find_rate; // Calculated find rate
|
||||
float fix_rate; // Calculated fix rate
|
||||
std::string completion; // Estimated completion date
|
||||
float _find_rate; // Calculated find rate
|
||||
float _fix_rate; // Calculated fix rate
|
||||
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
|
||||
// maximum space, and the width drives various other parameters.
|
||||
width = context.getWidth ();
|
||||
height = context.getHeight () - 1; // Allow for new line with prompt.
|
||||
max_value = 0;
|
||||
max_label = 1;
|
||||
graph_height = height - 7;
|
||||
graph_width = width - max_label - 14;
|
||||
_width = context.getWidth ();
|
||||
_height = context.getHeight () - 1; // Allow for new line with prompt.
|
||||
_max_value = 0;
|
||||
_max_label = 1;
|
||||
_graph_height = _height - 7;
|
||||
_graph_width = _width - _max_label - 14;
|
||||
|
||||
// Estimate how many 'bars' can be dsplayed. This will help subset a
|
||||
// potentially enormous data set.
|
||||
estimated_bars = (width - 1 - 14) / 3;
|
||||
_estimated_bars = (_width - 1 - 14) / 3;
|
||||
|
||||
actual_bars = 0;
|
||||
period = type;
|
||||
carryover_done = 0;
|
||||
_actual_bars = 0;
|
||||
_period = type;
|
||||
_carryover_done = 0;
|
||||
|
||||
// Rates are calculated last.
|
||||
find_rate = 0.0;
|
||||
fix_rate = 0.0;
|
||||
_find_rate = 0.0;
|
||||
_fix_rate = 0.0;
|
||||
|
||||
// Set the title.
|
||||
title = "(";
|
||||
_title = "(";
|
||||
std::vector <Arg>::const_iterator arg;
|
||||
for (arg = context.a3.begin (); arg != context.a3.end (); ++arg)
|
||||
{
|
||||
|
@ -217,13 +217,13 @@ Chart::Chart (char type)
|
|||
;
|
||||
else
|
||||
{
|
||||
if (title.length () > 1)
|
||||
title += " ";
|
||||
if (_title.length () > 1)
|
||||
_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")));
|
||||
epoch = from.toEpoch ();
|
||||
|
||||
if (bars.find (epoch) != bars.end ())
|
||||
++bars[epoch].added;
|
||||
if (_bars.find (epoch) != _bars.end ())
|
||||
++_bars[epoch]._added;
|
||||
|
||||
// e--> e--s-->
|
||||
// ppp> pppsss>
|
||||
|
@ -262,14 +262,14 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < start)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
|
||||
while (from < now)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -278,7 +278,7 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < now)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -292,14 +292,14 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
Date end = quantize (Date (task->get_date ("end")));
|
||||
epoch = end.toEpoch ();
|
||||
|
||||
if (bars.find (epoch) != bars.end ())
|
||||
++bars[epoch].removed;
|
||||
if (_bars.find (epoch) != _bars.end ())
|
||||
++_bars[epoch]._removed;
|
||||
|
||||
// Maintain a running total of 'done' tasks that are off the left of the
|
||||
// chart.
|
||||
if (end < earliest)
|
||||
if (end < _earliest)
|
||||
{
|
||||
++carryover_done;
|
||||
++_carryover_done;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -309,21 +309,21 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < start)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
|
||||
while (from < end)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
|
||||
from = increment (from);
|
||||
}
|
||||
|
||||
while (from < now)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].done;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._done;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -333,14 +333,14 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < end)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
|
||||
while (from < now)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].done;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._done;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -353,10 +353,10 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
// Skip old deleted tasks.
|
||||
Date end = quantize (Date (task->get_date ("end")));
|
||||
epoch = end.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ())
|
||||
++bars[epoch].removed;
|
||||
if (_bars.find (epoch) != _bars.end ())
|
||||
++_bars[epoch]._removed;
|
||||
|
||||
if (end < earliest)
|
||||
if (end < _earliest)
|
||||
continue;
|
||||
|
||||
if (task->has ("start"))
|
||||
|
@ -365,14 +365,14 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < start)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
|
||||
while (from < end)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].started;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._started;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +382,7 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
while (from < end)
|
||||
{
|
||||
epoch = from.toEpoch ();
|
||||
if (bars.find (epoch) != bars.end ()) ++bars[epoch].pending;
|
||||
if (_bars.find (epoch) != _bars.end ()) ++_bars[epoch]._pending;
|
||||
from = increment (from);
|
||||
}
|
||||
}
|
||||
|
@ -414,23 +414,23 @@ void Chart::scan (std::vector <Task>& tasks)
|
|||
// +---------------------------------------------------------------------+
|
||||
std::string Chart::render ()
|
||||
{
|
||||
if (graph_height < 5 || // a 4-line graph is essentially unreadable.
|
||||
graph_width < 2) // A single-bar graph is useless.
|
||||
if (_graph_height < 5 || // a 4-line graph is essentially unreadable.
|
||||
_graph_width < 2) // A single-bar graph is useless.
|
||||
{
|
||||
return std::string (STRING_CMD_BURN_TOO_SMALL) + "\n";
|
||||
}
|
||||
|
||||
if (max_value == 0)
|
||||
if (_max_value == 0)
|
||||
context.footnote (STRING_FEEDBACK_NO_MATCH);
|
||||
|
||||
// Create a grid, folded into a string.
|
||||
grid = "";
|
||||
for (int i = 0; i < height; ++i)
|
||||
grid += std::string (width, ' ') + "\n";
|
||||
_grid = "";
|
||||
for (int i = 0; i < _height; ++i)
|
||||
_grid += std::string (_width, ' ') + "\n";
|
||||
|
||||
// Title.
|
||||
std::string full_title;
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D': full_title = STRING_CMD_BURN_DAILY; 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;
|
||||
|
||||
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;
|
||||
grid.replace (LOC (0, (width - full_title.length ()) / 2), full_title.length (), full_title);
|
||||
full_title += " " + _title;
|
||||
_grid.replace (LOC (0, (_width - full_title.length ()) / 2), full_title.length (), full_title);
|
||||
}
|
||||
else
|
||||
{
|
||||
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 (0, (_width - full_title.length ()) / 2), full_title.length (), full_title);
|
||||
_grid.replace (LOC (1, (_width - _title.length ()) / 2), _title.length (), _title);
|
||||
}
|
||||
}
|
||||
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.
|
||||
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 + 1, width - 10), 10, "PP " + leftJustify (STRING_CMD_BURN_PENDING, 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 + 1, _width - 10), 10, "PP " + leftJustify (STRING_CMD_BURN_PENDING, 7));
|
||||
|
||||
// Determine y-axis labelling.
|
||||
std::vector <int> labels;
|
||||
yLabels (labels);
|
||||
max_label = (int) log10 ((double) labels[2]) + 1;
|
||||
std::vector <int> _labels;
|
||||
yLabels (_labels);
|
||||
_max_label = (int) log10 ((double) _labels[2]) + 1;
|
||||
|
||||
// Draw y-axis.
|
||||
for (int i = 0; i < graph_height; ++i)
|
||||
grid.replace (LOC (i + 1, max_label + 1), 1, "|");
|
||||
for (int i = 0; i < _graph_height; ++i)
|
||||
_grid.replace (LOC (i + 1, _max_label + 1), 1, "|");
|
||||
|
||||
// Draw y-axis labels.
|
||||
char label [12];
|
||||
sprintf (label, "%*d", max_label, labels[2]);
|
||||
grid.replace (LOC (1, max_label - strlen (label)), strlen (label), label);
|
||||
sprintf (label, "%*d", max_label, labels[1]);
|
||||
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");
|
||||
sprintf (label, "%*d", _max_label, _labels[2]);
|
||||
_grid.replace (LOC (1, _max_label - strlen (label)), strlen (label), label);
|
||||
sprintf (label, "%*d", _max_label, _labels[1]);
|
||||
_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");
|
||||
|
||||
// Draw x-axis.
|
||||
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 + 1), 1, "+");
|
||||
_grid.replace (LOC (_height - 6, _max_label + 2), _graph_width, std::string (_graph_width, '-'));
|
||||
|
||||
// Draw x-axis labels.
|
||||
std::vector <time_t> bars_in_sequence;
|
||||
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);
|
||||
|
||||
std::sort (bars_in_sequence.begin (), bars_in_sequence.end ());
|
||||
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)
|
||||
{
|
||||
Bar bar = bars[*seq];
|
||||
Bar bar = _bars[*seq];
|
||||
|
||||
// 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)
|
||||
grid.replace (LOC (height - 4, max_label + 2 + ((actual_bars - bar.offset - 1) * 3)), bar.major_label.length (), " " + 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);
|
||||
|
||||
major_label = bar.major_label;
|
||||
_major_label = bar._major_label;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw bars.
|
||||
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 (bar.offset < actual_bars)
|
||||
if (bar._offset < _actual_bars)
|
||||
{
|
||||
int pending = ( bar.pending * 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 pending = ( bar._pending * _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];
|
||||
|
||||
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)
|
||||
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)
|
||||
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.
|
||||
calculateRates (bars_in_sequence);
|
||||
char rate[12];
|
||||
if (find_rate != 0.0)
|
||||
sprintf (rate, "%.1f/d", find_rate);
|
||||
if (_find_rate != 0.0)
|
||||
sprintf (rate, "%.1f/d", _find_rate);
|
||||
else
|
||||
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)
|
||||
sprintf (rate, "%.1f/d", fix_rate);
|
||||
if (_fix_rate != 0.0)
|
||||
sprintf (rate, "%.1f/d", _fix_rate);
|
||||
else
|
||||
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.
|
||||
if (completion.length ())
|
||||
grid.replace (LOC (height - 2, max_label + 32), 22 + completion.length (), "Estimated completion: " + completion);
|
||||
if (_completion.length ())
|
||||
_grid.replace (LOC (_height - 2, _max_label + 32), 22 + _completion.length (), "Estimated completion: " + _completion);
|
||||
|
||||
optimizeGrid ();
|
||||
|
||||
|
@ -563,53 +563,53 @@ std::string Chart::render ()
|
|||
|
||||
// Replace DD, SS, PP with colored strings.
|
||||
std::string::size_type i;
|
||||
while ((i = grid.find ("PP")) != std::string::npos)
|
||||
grid.replace (i, 2, color_pending.colorize (" "));
|
||||
while ((i = _grid.find ("PP")) != std::string::npos)
|
||||
_grid.replace (i, 2, color_pending.colorize (" "));
|
||||
|
||||
while ((i = grid.find ("SS")) != std::string::npos)
|
||||
grid.replace (i, 2, color_started.colorize (" "));
|
||||
while ((i = _grid.find ("SS")) != std::string::npos)
|
||||
_grid.replace (i, 2, color_started.colorize (" "));
|
||||
|
||||
while ((i = grid.find ("DD")) != std::string::npos)
|
||||
grid.replace (i, 2, color_done.colorize (" "));
|
||||
while ((i = _grid.find ("DD")) != std::string::npos)
|
||||
_grid.replace (i, 2, color_done.colorize (" "));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Replace DD, SS, PP with ./+/X strings.
|
||||
std::string::size_type i;
|
||||
while ((i = grid.find ("PP")) != std::string::npos)
|
||||
grid.replace (i, 2, " X");
|
||||
while ((i = _grid.find ("PP")) != std::string::npos)
|
||||
_grid.replace (i, 2, " X");
|
||||
|
||||
while ((i = grid.find ("SS")) != std::string::npos)
|
||||
grid.replace (i, 2, " +");
|
||||
while ((i = _grid.find ("SS")) != std::string::npos)
|
||||
_grid.replace (i, 2, " +");
|
||||
|
||||
while ((i = grid.find ("DD")) != std::string::npos)
|
||||
grid.replace (i, 2, " .");
|
||||
while ((i = _grid.find ("DD")) != std::string::npos)
|
||||
_grid.replace (i, 2, " .");
|
||||
}
|
||||
|
||||
return grid;
|
||||
return _grid;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// grid =~ /\s+$//g
|
||||
// _grid =~ /\s+$//g
|
||||
void Chart::optimizeGrid ()
|
||||
{
|
||||
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;
|
||||
while (grid[non_ws] == ' ')
|
||||
while (_grid[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)
|
||||
{
|
||||
if (period == 'D') return input.startOfDay ();
|
||||
if (period == 'W') return input.startOfWeek ();
|
||||
if (period == 'M') return input.startOfMonth ();
|
||||
if (_period == 'D') return input.startOfDay ();
|
||||
if (_period == 'W') return input.startOfWeek ();
|
||||
if (_period == 'M') return input.startOfMonth ();
|
||||
|
||||
return input;
|
||||
}
|
||||
|
@ -624,7 +624,7 @@ Date Chart::increment (const Date& input)
|
|||
|
||||
int days;
|
||||
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D':
|
||||
if (++d > Date::daysInMonth (m, y))
|
||||
|
@ -675,7 +675,7 @@ Date Chart::decrement (const Date& input)
|
|||
int m = input.month ();
|
||||
int y = input.year ();
|
||||
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D':
|
||||
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 ()
|
||||
{
|
||||
Bar bar;
|
||||
|
||||
// Determine the last bar date.
|
||||
Date cursor;
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D': cursor = Date ().startOfDay (); break;
|
||||
case 'W': cursor = Date ().startOfWeek (); break;
|
||||
|
@ -734,43 +734,43 @@ void Chart::generateBars ()
|
|||
|
||||
// Iterate and determine all the other bar dates.
|
||||
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.
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D': // month/day
|
||||
{
|
||||
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 ());
|
||||
bar.minor_label = str;
|
||||
bar._minor_label = str;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'W': // year/week
|
||||
sprintf (str, "%d", cursor.year ());
|
||||
bar.major_label = str;
|
||||
bar._major_label = str;
|
||||
|
||||
sprintf (str, "%02d", cursor.weekOfYear (0));
|
||||
bar.minor_label = str;
|
||||
bar._minor_label = str;
|
||||
break;
|
||||
|
||||
case 'M': // year/month
|
||||
sprintf (str, "%d", cursor.year ());
|
||||
bar.major_label = str;
|
||||
bar._major_label = str;
|
||||
|
||||
sprintf (str, "%02d", cursor.month ());
|
||||
bar.minor_label = str;
|
||||
bar._minor_label = str;
|
||||
break;
|
||||
}
|
||||
|
||||
bar.offset = i;
|
||||
bars[cursor.toEpoch ()] = bar;
|
||||
bar._offset = i;
|
||||
_bars[cursor.toEpoch ()] = bar;
|
||||
|
||||
// Record the earliest date, for use as a cutoff when scanning data.
|
||||
earliest = cursor;
|
||||
_earliest = cursor;
|
||||
|
||||
// Move to the previous period.
|
||||
cursor = decrement (cursor);
|
||||
|
@ -780,39 +780,39 @@ void Chart::generateBars ()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void Chart::maxima ()
|
||||
{
|
||||
max_value = 0;
|
||||
max_label = 1;
|
||||
_max_value = 0;
|
||||
_max_label = 1;
|
||||
|
||||
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.
|
||||
int total = it->second.pending +
|
||||
it->second.started +
|
||||
it->second.done +
|
||||
carryover_done;
|
||||
// Determine _max_label.
|
||||
int total = it->second._pending +
|
||||
it->second._started +
|
||||
it->second._done +
|
||||
_carryover_done;
|
||||
|
||||
// Determine max_value.
|
||||
if (total > max_value)
|
||||
max_value = total;
|
||||
// Determine _max_value.
|
||||
if (total > _max_value)
|
||||
_max_value = total;
|
||||
|
||||
int length = (int) log10 ((double) total) + 1;
|
||||
if (length > max_label)
|
||||
max_label = length;
|
||||
if (length > _max_label)
|
||||
_max_label = length;
|
||||
}
|
||||
|
||||
// How many bars can be shown?
|
||||
actual_bars = (width - max_label - 14) / 3;
|
||||
graph_width = width - max_label - 14;
|
||||
_actual_bars = (_width - _max_label - 14) / 3;
|
||||
_graph_width = _width - _max_label - 14;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
{
|
||||
// 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;
|
||||
|
||||
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
|
||||
// rates or estimated completion date.
|
||||
if (bars[sequence.back ()].pending == 0)
|
||||
if (_bars[sequence.back ()]._pending == 0)
|
||||
return;
|
||||
|
||||
// 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?
|
||||
int half_days = 1;
|
||||
int quarter_days = 1;
|
||||
switch (period)
|
||||
switch (_period)
|
||||
{
|
||||
case 'D':
|
||||
half_days = half;
|
||||
|
@ -869,14 +869,14 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
|
|||
|
||||
for (unsigned int i = half; i < sequence.size (); ++i)
|
||||
{
|
||||
total_added_50 += bars[sequence[i]].added;
|
||||
total_removed_50 += bars[sequence[i]].removed;
|
||||
total_added_50 += _bars[sequence[i]]._added;
|
||||
total_removed_50 += _bars[sequence[i]]._removed;
|
||||
}
|
||||
|
||||
for (unsigned int i = half + quarter; i < sequence.size (); ++i)
|
||||
{
|
||||
total_added_75 += bars[sequence[i]].added;
|
||||
total_removed_75 += bars[sequence[i]].removed;
|
||||
total_added_75 += _bars[sequence[i]]._added;
|
||||
total_removed_75 += _bars[sequence[i]]._removed;
|
||||
}
|
||||
|
||||
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.
|
||||
float bias = (float) context.config.getReal ("burndown.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);
|
||||
_find_rate = (find_rate_50 * (1.0 - bias) + find_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?
|
||||
// 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) * "
|
||||
<< bias
|
||||
<< ") = "
|
||||
<< find_rate
|
||||
<< _find_rate
|
||||
<< "\nChart::calculateRates fix rate: "
|
||||
<< "("
|
||||
<< total_removed_50
|
||||
|
@ -923,20 +923,25 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
|
|||
<< " days) * "
|
||||
<< bias
|
||||
<< ") = "
|
||||
<< fix_rate;
|
||||
<< _fix_rate;
|
||||
context.debug (rates.str ());
|
||||
|
||||
// Estimate completion
|
||||
if (fix_rate > find_rate)
|
||||
if (_fix_rate > _find_rate)
|
||||
{
|
||||
int current_pending = bars[sequence.back ()].pending;
|
||||
int remaining_days = (int) (current_pending / (fix_rate - find_rate));
|
||||
int current_pending = _bars[sequence.back ()]._pending;
|
||||
int remaining_days = (int) (current_pending / (_fix_rate - _find_rate));
|
||||
|
||||
Date now;
|
||||
OldDuration delta (remaining_days * 86400);
|
||||
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 ()
|
||||
+ ")";
|
||||
|
@ -945,18 +950,18 @@ void Chart::calculateRates (std::vector <time_t>& sequence)
|
|||
est << "Chart::calculateRates Completion: "
|
||||
<< current_pending
|
||||
<< " tasks / ("
|
||||
<< fix_rate
|
||||
<< _fix_rate
|
||||
<< " - "
|
||||
<< find_rate
|
||||
<< _find_rate
|
||||
<< ") = "
|
||||
<< remaining_days
|
||||
<< " days = "
|
||||
<< completion;
|
||||
<< _completion;
|
||||
context.debug (est.str ());
|
||||
}
|
||||
else
|
||||
{
|
||||
completion = STRING_CMD_BURN_NO_CONVERGE;
|
||||
_completion = STRING_CMD_BURN_NO_CONVERGE;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -561,23 +561,24 @@ std::string CmdCalendar::renderMonths (
|
|||
duedmy.month () == months[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);
|
||||
break;
|
||||
|
||||
case 2: // today
|
||||
case Task::dateEarlierToday:
|
||||
case Task::dateLaterToday:
|
||||
cellColor.blend (color_duetoday);
|
||||
cellColor.blend (color_duetoday);
|
||||
break;
|
||||
|
||||
case 3: // overdue
|
||||
case Task::dateBeforeToday:
|
||||
cellColor.blend (color_overdue);
|
||||
break;
|
||||
|
||||
case 0: // not due at all
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,8 +232,12 @@ int CmdDiagnostics::execute (std::string& output)
|
|||
? " (readable)" : " (not readable)")
|
||||
<< "\n";
|
||||
|
||||
if (context.config.get ("taskd.trust") != "")
|
||||
out << " Trust: override\n";
|
||||
if (context.config.get ("taskd.trust") == "allow all")
|
||||
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: "
|
||||
<< context.config.get ("taskd.certificate")
|
||||
|
|
|
@ -52,17 +52,23 @@ int CmdGet::execute (std::string& output)
|
|||
if (words.size () == 0)
|
||||
throw std::string (STRING_CMD_GET_NO_DOM);
|
||||
|
||||
bool found = false;
|
||||
std::vector <std::string> results;
|
||||
std::vector <std::string>::iterator word;
|
||||
for (word = words.begin (); word != words.end (); ++word)
|
||||
{
|
||||
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);
|
||||
output += "\n";
|
||||
return 0;
|
||||
return found ? 0 : 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -232,7 +232,7 @@ int CmdShow::execute (std::string& output)
|
|||
i->first.substr (0, 6) != "alias." &&
|
||||
i->first.substr (0, 5) != "hook." &&
|
||||
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, 17) != "urgency.user.tag." &&
|
||||
i->first.substr (0, 12) != "urgency.uda.")
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <inttypes.h>
|
||||
#include <signal.h>
|
||||
#include <Context.h>
|
||||
#include <TLSClient.h>
|
||||
#include <Color.h>
|
||||
#include <text.h>
|
||||
#include <util.h>
|
||||
|
@ -87,14 +86,18 @@ int CmdSync::execute (std::string& output)
|
|||
if (credentials.size () != 3)
|
||||
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.
|
||||
File ca (context.config.get ("taskd.ca"));
|
||||
if (ca._data != "" && ! ca.exists ())
|
||||
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);
|
||||
|
||||
File certificate (context.config.get ("taskd.certificate"));
|
||||
|
@ -146,6 +149,7 @@ int CmdSync::execute (std::string& output)
|
|||
|
||||
request.setPayload (payload);
|
||||
|
||||
if (context.verbose ("sync"))
|
||||
out << format (STRING_CMD_SYNC_PROGRESS, connection)
|
||||
<< "\n";
|
||||
|
||||
|
@ -193,6 +197,7 @@ int CmdSync::execute (std::string& output)
|
|||
Task dummy;
|
||||
if (context.tdb2.get (uuid, dummy))
|
||||
{
|
||||
if (context.verbose ("sync"))
|
||||
out << " "
|
||||
<< colorChanged.colorize (
|
||||
format (STRING_CMD_SYNC_MOD,
|
||||
|
@ -203,6 +208,7 @@ int CmdSync::execute (std::string& output)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (context.verbose ("sync"))
|
||||
out << " "
|
||||
<< colorAdded.colorize (
|
||||
format (STRING_CMD_SYNC_ADD,
|
||||
|
@ -294,6 +300,7 @@ int CmdSync::execute (std::string& output)
|
|||
status = 1;
|
||||
}
|
||||
|
||||
if (context.verbose ("sync"))
|
||||
out << "\n";
|
||||
output = out.str ();
|
||||
|
||||
|
@ -319,7 +326,7 @@ bool CmdSync::send (
|
|||
const std::string& ca,
|
||||
const std::string& certificate,
|
||||
const std::string& key,
|
||||
bool trust,
|
||||
const enum TLSClient::trust_level trust,
|
||||
const Msg& request,
|
||||
Msg& response)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <string>
|
||||
#include <Command.h>
|
||||
#include <Msg.h>
|
||||
#include <TLSClient.h>
|
||||
|
||||
class CmdSync : public Command
|
||||
{
|
||||
|
@ -38,7 +39,7 @@ public:
|
|||
int execute (std::string&);
|
||||
|
||||
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
|
||||
|
|
|
@ -43,8 +43,6 @@ void handleRecurrence ();
|
|||
Date getNextRecurrence (Date&, std::string&);
|
||||
bool generateDueDates (Task&, std::vector <Date>&);
|
||||
void updateRecurrenceMask (Task&);
|
||||
int getDueState (const std::string&);
|
||||
int getDueState (const Date&);
|
||||
bool nag (Task&);
|
||||
|
||||
// rules.cpp
|
||||
|
|
|
@ -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
|
||||
// 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 (getDueState (t->get ("due")) == 3)
|
||||
if (t->getDateState ("due") == Task::dateBeforeToday)
|
||||
isOverdue = true;
|
||||
|
||||
std::string priority = t->get ("priority");
|
||||
|
@ -486,7 +421,7 @@ bool nag (Task& task)
|
|||
}
|
||||
else if (t->getStatus () == Task::pending)
|
||||
{
|
||||
if (getDueState (t->get ("due")) == 3)
|
||||
if (t->getDateState ("due") == Task::dateBeforeToday)
|
||||
overdue++;
|
||||
|
||||
std::string priority = t->get ("priority");
|
||||
|
|
|
@ -222,7 +222,7 @@ static void colorizeDue (Task& task, const Color& base, Color& c)
|
|||
Task::status status = task.getStatus ();
|
||||
if (status != Task::completed &&
|
||||
status != Task::deleted &&
|
||||
getDueState (task.get ("due")) == 1)
|
||||
task.getDateState ("due") == Task::dateAfterToday)
|
||||
c.blend (base);
|
||||
}
|
||||
}
|
||||
|
@ -233,9 +233,10 @@ static void colorizeDueToday (Task& task, const Color& base, Color& c)
|
|||
if (task.has ("due"))
|
||||
{
|
||||
Task::status status = task.getStatus ();
|
||||
Task::dateState dateState = task.getDateState ("due");
|
||||
if (status != Task::completed &&
|
||||
status != Task::deleted &&
|
||||
getDueState (task.get ("due")) == 2)
|
||||
(dateState == Task::dateLaterToday || dateState == Task::dateEarlierToday))
|
||||
c.blend (base);
|
||||
}
|
||||
}
|
||||
|
@ -248,7 +249,7 @@ static void colorizeOverdue (Task& task, const Color& base, Color& c)
|
|||
Task::status status = task.getStatus ();
|
||||
if (status != Task::completed &&
|
||||
status != Task::deleted &&
|
||||
getDueState (task.get ("due")) == 3)
|
||||
task.getDateState ("due") == Task::dateBeforeToday)
|
||||
c.blend (base);
|
||||
}
|
||||
}
|
||||
|
|
12
test/calc.t
12
test/calc.t
|
@ -27,7 +27,7 @@
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Test::More tests => 14;
|
||||
use Test::More tests => 22;
|
||||
|
||||
# '15min' is seen as '15', 'min', not '15min' duration.
|
||||
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 '60' Number/, 'Number 60');
|
||||
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');
|
||||
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');
|
||||
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;
|
||||
|
||||
|
|
47
test/count.t
47
test/count.t
|
@ -27,6 +27,7 @@
|
|||
|
||||
use strict;
|
||||
use warnings;
|
||||
use POSIX qw(mktime);
|
||||
use Test::More tests => 7;
|
||||
|
||||
# Ensure environment has no influence.
|
||||
|
@ -49,30 +50,38 @@ 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 add four wait:eom 2>&1};
|
||||
|
||||
# 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
|
||||
# means a second child task is generated. This would be fixed by 'eom'
|
||||
# expanding to 2013-04-30 24:00:00, as per ISO-8601.
|
||||
qx{../src/task rc:count.rc add five due:eom recur:monthly 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];
|
||||
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
my $output = qx{../src/task rc:count.rc count 2>&1};
|
||||
like ($output, qr/^5\n/ms, 'count');
|
||||
# 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
|
||||
# means a second child task is generated. This would be fixed by 'eom'
|
||||
# expanding to 2013-04-30 24:00:00, as per ISO-8601.
|
||||
qx{../src/task rc:count.rc add five due:eom recur:monthly 2>&1};
|
||||
|
||||
$output = qx{../src/task rc:count.rc count status:deleted rc.debug:1 2>&1};
|
||||
like ($output, qr/^1\n/ms, 'count status:deleted');
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
my $output = qx{../src/task rc:count.rc count 2>&1};
|
||||
like ($output, qr/^5\n/ms, 'count');
|
||||
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count e 2>&1};
|
||||
like ($output, qr/^3\n/ms, 'count e');
|
||||
$output = qx{../src/task rc:count.rc count status:deleted rc.debug:1 2>&1};
|
||||
like ($output, qr/^1\n/ms, 'count status:deleted');
|
||||
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count description.startswith:f 2>&1};
|
||||
like ($output, qr/^2\n/ms, 'count description.startswith:f');
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count e 2>&1};
|
||||
like ($output, qr/^3\n/ms, 'count e');
|
||||
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count due.any: 2>&1};
|
||||
like ($output, qr/^1\n/ms, 'count due.any:');
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count description.startswith:f 2>&1};
|
||||
like ($output, qr/^2\n/ms, 'count description.startswith:f');
|
||||
|
||||
diag ("Problem: the next test fails at EOM");
|
||||
$output = qx{../src/task rc:count.rc count due.any: 2>&1};
|
||||
like ($output, qr/^1\n/ms, 'count due.any:');
|
||||
}
|
||||
|
||||
# Cleanup.
|
||||
unlink qw(pending.data completed.data undo.data backlog.data count.rc);
|
||||
|
|
|
@ -35,16 +35,18 @@ Context context;
|
|||
// A few hard-coded symbols.
|
||||
bool get (const std::string& name, Variant& value)
|
||||
{
|
||||
if (name == "pi") {value = Variant (3.14159165); return true;}
|
||||
else if (name == "x") {value = Variant (true); return true;}
|
||||
|
||||
if (name == "x")
|
||||
value = Variant (true);
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
UnitTest t (43);
|
||||
UnitTest t (46);
|
||||
|
||||
// Test the source independently.
|
||||
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.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;
|
||||
e.addSource (get);
|
||||
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.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;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,18 +94,21 @@ int main (int argc, char** argv)
|
|||
local_now->tm_hour = 0;
|
||||
local_now->tm_min = 0;
|
||||
local_now->tm_sec = 0;
|
||||
local_now->tm_isdst = -1;
|
||||
time_t local = mktime (local_now);
|
||||
std::cout << "# local midnight today " << local << "\n";
|
||||
|
||||
local_now->tm_year = 2013 - 1900;
|
||||
local_now->tm_mon = 12 - 1;
|
||||
local_now->tm_mday = 6;
|
||||
local_now->tm_isdst = 0;
|
||||
time_t local6 = mktime (local_now);
|
||||
std::cout << "# local midnight 2013-12-06 " << local6 << "\n";
|
||||
|
||||
local_now->tm_year = 2013 - 1900;
|
||||
local_now->tm_mon = 12 - 1;
|
||||
local_now->tm_mday = 1;
|
||||
local_now->tm_isdst = 0;
|
||||
time_t local1 = mktime (local_now);
|
||||
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_min = 0;
|
||||
utc_now->tm_sec = 0;
|
||||
utc_now->tm_isdst = -1;
|
||||
time_t utc = timegm (utc_now);
|
||||
std::cout << "# utc midnight today " << utc << "\n";
|
||||
|
||||
utc_now->tm_year = 2013 - 1900;
|
||||
utc_now->tm_mon = 12 - 1;
|
||||
utc_now->tm_mday = 6;
|
||||
utc_now->tm_isdst = 0;
|
||||
time_t utc6 = timegm (utc_now);
|
||||
std::cout << "# utc midnight 2013-12-06 " << utc6 << "\n";
|
||||
|
||||
utc_now->tm_year = 2013 - 1900;
|
||||
utc_now->tm_mon = 12 - 1;
|
||||
utc_now->tm_mday = 1;
|
||||
utc_now->tm_isdst = 0;
|
||||
time_t utc1 = timegm (utc_now);
|
||||
std::cout << "# utc midnight 2013-12-01 " << utc1 << "\n";
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#! /bin/sh
|
||||
|
||||
rc=0
|
||||
if [ x"$1" = x"--verbose" ];
|
||||
then
|
||||
for i in ${TESTBLOB}
|
||||
|
@ -9,9 +10,10 @@ then
|
|||
while read LINE
|
||||
do
|
||||
echo "$LINE"
|
||||
done < test.log
|
||||
done < test.log || rc=1
|
||||
rm test.log
|
||||
done
|
||||
exit $rc
|
||||
else
|
||||
date > all.log
|
||||
|
||||
|
@ -37,7 +39,7 @@ else
|
|||
COUNT=`expr $COUNT + 1`
|
||||
fi
|
||||
|
||||
$i >> all.log 2>&1
|
||||
$i >> all.log 2>&1 || rc=1
|
||||
done
|
||||
|
||||
if [ $BAR -eq 1 ]; then
|
||||
|
@ -53,4 +55,5 @@ else
|
|||
printf "Fail: %5d\n" `grep -c '^not' all.log`
|
||||
printf "Skipped: %5d\n" `grep -c '^skip' all.log`
|
||||
printf "Runtime: %5d seconds\n" $RUNTIME
|
||||
exit $rc
|
||||
fi
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <iomanip>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <test.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -84,6 +85,7 @@ UnitTest::~UnitTest ()
|
|||
<< " skipped. "
|
||||
<< std::setprecision (3) << percentPassed
|
||||
<< "% passed.\n";
|
||||
exit (_failed > 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
|
75
test/tw-1300.t
Executable file
75
test/tw-1300.t
Executable 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
116
test/tw-285.t
Executable 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
|
Loading…
Add table
Add a link
Reference in a new issue