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

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

View file

@ -101,6 +101,8 @@ The following submitted code, packages or analysis, and deserve special thanks:
Marton Suranyi
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

View file

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

View file

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

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

View file

@ -25,7 +25,11 @@ Your contributions are especially welcome. Whether it comes in the form of
code patches, ideas, discussion, bug reports, encouragement or criticism, your
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

View file

@ -537,6 +537,9 @@ from tasks, or the system. Supported DOM references are:
Note that the 'rc.<name>' reference may need to be escaped using '--' to prevent
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.

View file

@ -253,7 +253,7 @@ control specific occasions when output is generated. This list may contain:
edit Used the verbose template for the 'edit' command
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

View file

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

View file

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

View file

@ -146,10 +146,6 @@ void E9::eval (const Task& task, std::vector <Arg>& value_stack)
else if (arg->_raw == "=") operator_equal (result, left, right, case_sensitive);
else if (arg->_raw == "~") operator_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";
}
////////////////////////////////////////////////////////////////////////////////

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -382,71 +382,6 @@ void updateRecurrenceMask (Task& task)
}
}
////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue. Returns
// 0 = not due at all
// 1 = imminent
// 2 = today
// 3 = overdue
int getDueState (const std::string& due)
{
if (due.length ())
{
Date dt (::atoi (due.c_str ()));
// rightNow is the current date + time.
static Date rightNow;
Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());
if (dt < rightNow)
return 3;
if (rightNow.sameDay (dt))
return 2;
int imminentperiod = context.config.getInteger ("due");
if (imminentperiod == 0)
return 1;
Date imminentDay = thisDay + imminentperiod * 86400;
if (dt < imminentDay)
return 1;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Determines whether a task is overdue. Returns
// 0 = not due at all
// 1 = imminent
// 2 = today
// 3 = overdue
int getDueState (const Date& due)
{
// rightNow is the current date + time.
static Date rightNow;
Date thisDay (rightNow.month (), rightNow.day (), rightNow.year ());
if (due < rightNow)
return 3;
if (rightNow.sameDay (due))
return 2;
int imminentperiod = context.config.getInteger ("due");
if (imminentperiod == 0)
return 1;
Date imminentDay = thisDay + imminentperiod * 86400;
if (due < imminentDay)
return 1;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// Returns a Boolean indicator as to whether a nag message was generated, so
// 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");

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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

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