Merge branch '2.5.2' into master

This commit is contained in:
Tomas Babej
2020-12-05 17:46:20 -05:00
552 changed files with 11657 additions and 34178 deletions

1
src/.gitignore vendored
View File

@@ -3,3 +3,4 @@ Makefile.in
debug
calc
lex
liblibshared.a

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -32,11 +32,8 @@
#include <Context.h>
#include <Lexer.h>
#include <Color.h>
#include <text.h>
#include <util.h>
#include <i18n.h>
extern Context context;
#include <shared.h>
#include <format.h>
// Overridden by rc.abbreviation.minimum.
int CLI2::minimumMatchLength = 3;
@@ -213,7 +210,7 @@ const std::string A2::dump () const
else tags += "\033[32m" + tag + "\033[0m ";
}
return output + " " + atts + tags;
return output + ' ' + atts + tags;
}
////////////////////////////////////////////////////////////////////////////////
@@ -236,7 +233,7 @@ void CLI2::getOverride (int argc, const char** argv, std::string& home, File& rc
if (last_slash != std::string::npos)
home = rc.parent ();
context.header (format (STRING_PARSER_ALTERNATE_RC, rc._data));
Context::getContext ().header (format ("Using alternate .taskrc file {1}", rc._data));
// Keep looping, because if there are multiple rc:file arguments, the last
// one should dominate.
@@ -249,7 +246,7 @@ void CLI2::getOverride (int argc, const char** argv, std::string& home, File& rc
// Static method.
void CLI2::getDataLocation (int argc, const char** argv, Path& data)
{
std::string location = context.config.get ("data.location");
std::string location = Context::getContext ().config.get ("data.location");
if (location != "")
data = location;
@@ -263,7 +260,7 @@ void CLI2::getDataLocation (int argc, const char** argv, Path& data)
raw.substr (0, 16) == "rc.data.location")
{
data = Directory (raw.substr (17));
context.header (format (STRING_PARSER_ALTERNATE_DATA, (std::string) data));
Context::getContext ().header (format ("Using alternate data.location {1}", (std::string) data));
// Keep looping, because if there are multiple rc:file arguments, the last
// one should dominate.
@@ -277,22 +274,31 @@ void CLI2::applyOverrides (int argc, const char** argv)
{
for (int i = 0; i < argc; ++i)
{
// Don't process any arguments after a '--'
std::string raw = argv[i];
if (raw == "--")
break;
// Overrides always start with 'rc.'
if (raw.length () > 3 &&
raw.substr (0, 3) == "rc.")
{
// Our separator can either be '=' or ':', so try and find both.
auto sep = raw.find ('=', 3);
if (sep == std::string::npos)
sep = raw.find (':', 3);
// Process our override if well-formed
if (sep != std::string::npos)
{
std::string name = raw.substr (3, sep - 3);
std::string value = raw.substr (sep + 1);
context.config.set (name, value);
context.footnote (format (STRING_PARSER_OVERRIDE_RC, name, value));
Context::getContext ().config.set (name, value);
if (Context::getContext ().verbose("override"))
Context::getContext ().footnote (format ("Configuration override rc.{1}:{2}", name, value));
}
}
}
@@ -376,9 +382,7 @@ void CLI2::handleArg0 ()
A2 cal ("calendar", Lexer::Type::word);
_args.push_back (cal);
}
else if (basename == "task" ||
basename == "tw" ||
basename == "t")
else
{
_args.push_back (a);
}
@@ -399,6 +403,7 @@ void CLI2::lexArguments ()
{
bool quoted = Lexer::wasQuoted (_original_args[i].attribute ("raw"));
// Process single-token arguments.
std::string lexeme;
Lexer::Type type;
Lexer lex (_original_args[i].attribute ("raw"));
@@ -422,11 +427,13 @@ void CLI2::lexArguments ()
_args.push_back (a);
}
// Process muktiple-token arguments.
else
{
std::string quote = "'";
std::string escaped = _original_args[i].attribute ("raw");
str_replace (escaped, quote, "\\'");
escaped = str_replace (escaped, quote, "\\'");
std::string::size_type cursor = 0;
std::string word;
@@ -460,8 +467,8 @@ void CLI2::lexArguments ()
}
}
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze lexArguments"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze lexArguments"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -491,7 +498,7 @@ void CLI2::demotion ()
else if (a._lextype == Lexer::Type::pair &&
canonicalize (canonical, "pseudo", a.attribute ("name")))
{
context.config.set (canonical, a.attribute ("value"));
Context::getContext ().config.set (canonical, a.attribute ("value"));
changes = true;
// Equivalent to erasing 'a'.
@@ -502,16 +509,16 @@ void CLI2::demotion ()
}
if (changes &&
context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze demotion"));
Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze demotion"));
}
////////////////////////////////////////////////////////////////////////////////
// Intended to be called after ::add() to perform the final analysis.
void CLI2::analyze ()
{
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze"));
// Process _original_args.
_args.clear ();
@@ -524,7 +531,7 @@ void CLI2::analyze ()
{
defaultCommand ();
if (! findCommand ())
throw std::string (STRING_TRIVIAL_INPUT);
throw std::string ("You must specify a command or a task to modify.");
}
demotion ();
@@ -568,10 +575,10 @@ void CLI2::addContextFilter ()
return;
// Detect if any context is set, and bail out if not
std::string contextName = context.config.get ("context");
std::string contextName = Context::getContext ().config.get ("context");
if (contextName == "")
{
context.debug ("No context.");
Context::getContext ().debug ("No context.");
return;
}
@@ -582,23 +589,23 @@ void CLI2::addContextFilter ()
a._lextype == Lexer::Type::number ||
a._lextype == Lexer::Type::set)
{
context.debug (format ("UUID/ID argument found '{1}', not applying context.", a.attribute ("raw")));
Context::getContext ().debug (format ("UUID/ID argument found '{1}', not applying context.", a.attribute ("raw")));
return;
}
}
// Apply context
context.debug ("Applying context: " + contextName);
std::string contextFilter = context.config.get ("context." + contextName);
Context::getContext ().debug ("Applying context: " + contextName);
std::string contextFilter = Context::getContext ().config.get ("context." + contextName);
if (contextFilter == "")
context.debug ("Context '" + contextName + "' not defined.");
Context::getContext ().debug ("Context '" + contextName + "' not defined.");
else
{
_context_filter_added = true;
addFilter (contextFilter);
if (context.verbose ("context"))
context.footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
if (Context::getContext ().verbose ("context"))
Context::getContext ().footnote (format ("Context '{1}' set. Use 'task context none' to remove.", contextName));
}
}
@@ -624,7 +631,7 @@ void CLI2::prepareFilter ()
desugarFilterPatterns ();
insertJunctions (); // Deliberately after all desugar calls.
if (context.verbose ("filter"))
if (Context::getContext ().verbose ("filter"))
{
std::string combined;
for (const auto& a : _args)
@@ -632,14 +639,14 @@ void CLI2::prepareFilter ()
if (a.hasTag ("FILTER"))
{
if (combined != "")
combined += " ";
combined += ' ';
combined += a.attribute ("raw");
}
}
if (combined.size ())
context.footnote (std::string (STRING_COLUMN_LABEL_FILTER) + ": " + combined);
Context::getContext ().footnote (std::string ("Filter: ") + combined);
}
}
@@ -652,13 +659,13 @@ const std::vector <std::string> CLI2::getWords ()
if (a.hasTag ("MISCELLANEOUS"))
words.push_back (a.attribute ("raw"));
if (context.config.getInteger ("debug.parser") >= 2)
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
{
Color colorOrigArgs ("gray10 on gray4");
std::string message = " ";
for (const auto& word : words)
message += colorOrigArgs.colorize (word) + " ";
context.debug ("CLI2::getWords" + message);
message += colorOrigArgs.colorize (word) + ' ';
Context::getContext ().debug ("CLI2::getWords" + message);
}
return words;
@@ -737,13 +744,13 @@ const std::string CLI2::dump (const std::string& title) const
out << colorFilter.colorize (i->attribute ("raw"));
}
out << "\n";
out << '\n';
if (_args.size ())
{
out << " _args\n";
for (const auto& a : _args)
out << " " << a.dump () << "\n";
out << " " << a.dump () << '\n';
}
if (_id_ranges.size ())
@@ -752,21 +759,21 @@ const std::string CLI2::dump (const std::string& title) const
for (const auto& range : _id_ranges)
{
if (range.first != range.second)
out << colorArgs.colorize (range.first + "-" + range.second) << " ";
out << colorArgs.colorize (range.first + "-" + range.second) << ' ';
else
out << colorArgs.colorize (range.first) << " ";
out << colorArgs.colorize (range.first) << ' ';
}
out << "\n";
out << '\n';
}
if (_uuid_list.size ())
{
out << " _uuid_list\n ";
for (const auto& uuid : _uuid_list)
out << colorArgs.colorize (uuid) << " ";
out << colorArgs.colorize (uuid) << ' ';
out << "\n";
out << '\n';
}
return out.str ();
@@ -845,11 +852,11 @@ void CLI2::aliasExpansion ()
while (action && counter++ < safetyValveDefault);
if (counter >= safetyValveDefault)
context.debug (format (STRING_PARSER_ALIAS_NEST, safetyValveDefault));
Context::getContext ().debug (format ("Nested alias limit of {1} reached.", safetyValveDefault));
if (changes &&
context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze aliasExpansion"));
Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze aliasExpansion"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -883,8 +890,8 @@ void CLI2::canonicalizeNames ()
}
if (changes &&
context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze canonicalizeNames"));
Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze canonicalizeNames"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -893,7 +900,7 @@ void CLI2::categorizeArgs ()
{
// Context is only applied for commands that request it.
std::string command = getCommand ();
Command* cmd = context.commands[command];
Command* cmd = Context::getContext ().commands[command];
if (cmd && cmd->uses_context ())
addContextFilter ();
@@ -935,7 +942,7 @@ void CLI2::categorizeArgs ()
! cmd->accepts_miscellaneous ())
{
// No commands were expected --> error.
throw format (STRING_PARSER_UNEXPECTED_ARG, command, a.attribute ("raw"));
throw format ("The '{1}' command does not allow '{2}'.", command, a.attribute ("raw"));
}
else if (cmd &&
! cmd->accepts_filter () &&
@@ -959,7 +966,7 @@ void CLI2::categorizeArgs ()
cmd->accepts_miscellaneous ())
{
// Error: internally inconsistent.
throw std::string (STRING_UNKNOWN_ERROR);
throw std::string ("Unknown error. Please report.");
}
else if (cmd &&
cmd->accepts_filter () &&
@@ -999,13 +1006,13 @@ void CLI2::categorizeArgs ()
cmd->accepts_miscellaneous ())
{
// Error: internally inconsistent.
throw std::string (STRING_UNKNOWN_ERROR);
throw std::string ("Unknown error. Please report.");
}
}
if (changes &&
context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze categorizeArgs"));
Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze categorizeArgs"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -1073,8 +1080,8 @@ void CLI2::parenthesizeOriginalFilter ()
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze parenthesizeOriginalFilter"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze parenthesizeOriginalFilter"));
}
}
@@ -1105,7 +1112,7 @@ bool CLI2::findCommand ()
a.tag ("CMD");
// Apply command DNA as tags.
Command* command = context.commands[canonical];
Command* command = Context::getContext ().commands[canonical];
if (command->read_only ()) a.tag ("READONLY");
if (command->displays_id ()) a.tag ("SHOWSID");
if (command->needs_gc ()) a.tag ("RUNSGC");
@@ -1114,8 +1121,8 @@ bool CLI2::findCommand ()
if (command->accepts_modifications ()) a.tag ("ALLOWSMODIFICATIONS");
if (command->accepts_miscellaneous ()) a.tag ("ALLOWSMISC");
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze findCommand"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze findCommand"));
// Stop and indicate command found.
return true;
@@ -1176,8 +1183,8 @@ void CLI2::desugarFilterTags ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter desugarFilterTags"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterTags"));
}
}
@@ -1202,8 +1209,8 @@ void CLI2::findStrayModifications ()
}
if (changes)
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter findStrayModifications"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter findStrayModifications"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -1240,12 +1247,12 @@ void CLI2::desugarFilterAttributes ()
// )
// Use this sequence in place of a single value.
std::vector <A2> values = lexExpression (value);
if (context.config.getInteger ("debug.parser") >= 2)
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
{
context.debug ("CLI2::lexExpression " + name + ":" + value);
Context::getContext ().debug ("CLI2::lexExpression " + name + ':' + value);
for (auto& v : values)
context.debug (" " + v.dump ());
context.debug (" ");
Context::getContext ().debug (" " + v.dump ());
Context::getContext ().debug (" ");
}
bool found = false;
@@ -1258,7 +1265,7 @@ void CLI2::desugarFilterAttributes ()
// date --> yes
// duration --> yes
bool evalSupported = true;
Column* col = context.columns[canonical];
Column* col = Context::getContext ().columns[canonical];
if (col && col->type () == "string")
evalSupported = false;
@@ -1270,7 +1277,7 @@ void CLI2::desugarFilterAttributes ()
A2 op ("", Lexer::Type::op);
op.tag ("FILTER");
A2 rhs ("", Lexer::Type::string);
A2 rhs ("", values[0]._lextype);
rhs.tag ("FILTER");
// Special case for '<name>:<value>'.
@@ -1357,22 +1364,27 @@ void CLI2::desugarFilterAttributes ()
#endif
}
else
throw format (STRING_PARSER_UNKNOWN_ATTMOD, mod);
throw format ("Error: unrecognized attribute modifier '{1}'.", mod);
reconstructed.push_back (lhs);
reconstructed.push_back (op);
// Do not modify this construct without full understanding.
if (values.size () == 1 || ! evalSupported)
// Getting this wrong breaks a whole lot of filtering tests.
if (values.size () > 1 || evalSupported)
{
if (Lexer::isDOM (rhs.attribute ("raw")))
rhs._lextype = Lexer::Type::dom;
for (auto& v : values)
reconstructed.push_back (v);
}
else if (Lexer::isDOM (rhs.attribute ("raw")))
{
rhs._lextype = Lexer::Type::dom;
reconstructed.push_back (rhs);
}
else
for (auto& v : values)
reconstructed.push_back (v);
{
reconstructed.push_back (rhs);
}
found = true;
}
@@ -1399,8 +1411,8 @@ void CLI2::desugarFilterAttributes ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter desugarFilterAttributes"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterAttributes"));
}
}
@@ -1438,8 +1450,8 @@ void CLI2::desugarFilterPatterns ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter desugarFilterPatterns"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterPatterns"));
}
}
@@ -1456,7 +1468,7 @@ void CLI2::findIDs ()
{
bool changes = false;
if (context.config.getBoolean ("sugar"))
if (Context::getContext ().config.getBoolean ("sugar"))
{
bool previousFilterArgWasAnOperator = false;
int filterCount = 0;
@@ -1480,8 +1492,7 @@ void CLI2::findIDs ()
else if (a._lextype == Lexer::Type::set)
{
// Split the ID list into elements.
std::vector <std::string> elements;
split (elements, a.attribute ("raw"), ',');
auto elements = split (a.attribute ("raw"), ',');
for (auto& element : elements)
{
@@ -1536,8 +1547,7 @@ void CLI2::findIDs ()
a.tag ("FILTER");
// Split the ID list into elements.
std::vector <std::string> elements;
split (elements, raw, ',');
auto elements = split (raw, ',');
for (const auto& element : elements)
{
@@ -1578,8 +1588,8 @@ void CLI2::findIDs ()
}
if (changes)
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter findIDs"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter findIDs"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -1587,7 +1597,7 @@ void CLI2::findUUIDs ()
{
bool changes = false;
if (context.config.getBoolean ("sugar"))
if (Context::getContext ().config.getBoolean ("sugar"))
{
for (const auto& a : _args)
{
@@ -1639,8 +1649,8 @@ void CLI2::findUUIDs ()
}
if (changes)
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter findUUIDs"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter findUUIDs"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -1724,8 +1734,8 @@ void CLI2::insertIDExpr ()
else
{
bool ascending = true;
int low = strtol (r->first.c_str (), NULL, 10);
int high = strtol (r->second.c_str (), NULL, 10);
int low = strtol (r->first.c_str (), nullptr, 10);
int high = strtol (r->second.c_str (), nullptr, 10);
if (low <= high)
ascending = true;
else
@@ -1786,8 +1796,8 @@ void CLI2::insertIDExpr ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter insertIDExpr"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter insertIDExpr"));
}
}
@@ -1823,8 +1833,8 @@ void CLI2::lexFilterArgs ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter lexFilterArgs"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter lexFilterArgs"));
}
}
@@ -1930,8 +1940,8 @@ void CLI2::desugarFilterPlainArgs ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter desugarFilterPlainArgs"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter desugarFilterPlainArgs"));
}
}
@@ -1984,8 +1994,8 @@ void CLI2::insertJunctions ()
{
_args = reconstructed;
if (context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::prepareFilter insertJunctions"));
if (Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::prepareFilter insertJunctions"));
}
}
@@ -2025,7 +2035,7 @@ void CLI2::defaultCommand ()
if (! found_sequence)
{
// Apply overrides, if any.
std::string defaultCommand = context.config.get ("default.command");
std::string defaultCommand = Context::getContext ().config.get ("default.command");
if (defaultCommand != "")
{
// Modify _args, _original_args to be:
@@ -2068,8 +2078,8 @@ void CLI2::defaultCommand ()
}
if (changes &&
context.config.getInteger ("debug.parser") >= 2)
context.debug (dump ("CLI2::analyze defaultCommand"));
Context::getContext ().config.getInteger ("debug.parser") >= 2)
Context::getContext ().debug (dump ("CLI2::analyze defaultCommand"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -2113,3 +2123,5 @@ std::vector <A2> CLI2::lexExpression (const std::string& expression)
}
////////////////////////////////////////////////////////////////////////////////
// vim: ts=2:sw=2

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_CLI2

View File

@@ -1,55 +1,58 @@
cmake_minimum_required (VERSION 2.8)
cmake_minimum_required (VERSION 3.0)
include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/commands
${CMAKE_SOURCE_DIR}/src/columns
${CMAKE_SOURCE_DIR}/src/libshared/src
${TASK_INCLUDE_DIRS})
set (task_SRCS CLI2.cpp CLI2.h
Color.cpp Color.h
Config.cpp Config.h
Context.cpp Context.h
DOM.cpp DOM.h
Dates.cpp Dates.h
Eval.cpp Eval.h
Filter.cpp Filter.h
FS.cpp FS.h
Hooks.cpp Hooks.h
ISO8601.cpp ISO8601.h
JSON.cpp JSON.h
Lexer.cpp Lexer.h
Msg.cpp Msg.h
Nibbler.cpp Nibbler.h
RX.cpp RX.h
TDB2.cpp TDB2.h
Task.cpp Task.h
Timer.cpp Timer.h
TLSClient.cpp TLSClient.h
Variant.cpp Variant.h
ViewTask.cpp ViewTask.h
ViewText.cpp ViewText.h
dependency.cpp
feedback.cpp
i18n.h
interactive.cpp
legacy.cpp
recur.cpp
rules.cpp
sort.cpp
text.cpp text.h
utf8.cpp utf8.h
util.cpp util.h
wcwidth6.cpp)
add_library (task CLI2.cpp CLI2.h
Context.cpp Context.h
DOM.cpp DOM.h
Eval.cpp Eval.h
Filter.cpp Filter.h
Hooks.cpp Hooks.h
Lexer.cpp Lexer.h
TDB2.cpp TDB2.h
Task.cpp Task.h
TLSClient.cpp TLSClient.h
Variant.cpp Variant.h
ViewTask.cpp ViewTask.h
dependency.cpp
feedback.cpp
legacy.cpp
nag.cpp
recur.cpp
rules.cpp
sort.cpp
util.cpp util.h)
add_library (libshared libshared/src/Color.cpp libshared/src/Color.h
libshared/src/Configuration.cpp libshared/src/Configuration.h
libshared/src/Datetime.cpp libshared/src/Datetime.h
libshared/src/Duration.cpp libshared/src/Duration.h
libshared/src/FS.cpp libshared/src/FS.h
libshared/src/JSON.cpp libshared/src/JSON.h
libshared/src/Msg.cpp libshared/src/Msg.h
libshared/src/Pig.cpp libshared/src/Pig.h
libshared/src/RX.cpp libshared/src/RX.h
libshared/src/Table.cpp libshared/src/Table.h
libshared/src/Timer.cpp libshared/src/Timer.h
libshared/src/format.cpp libshared/src/format.h
libshared/src/ip.cpp
libshared/src/shared.cpp libshared/src/shared.h
libshared/src/unicode.cpp libshared/src/unicode.h
libshared/src/utf8.cpp libshared/src/utf8.h
libshared/src/wcwidth6.cpp)
add_library (task STATIC ${task_SRCS})
add_executable (task_executable main.cpp)
add_executable (calc_executable calc.cpp)
add_executable (lex_executable lex.cpp)
# Yes, 'task' is included twice, otherwise linking fails on assorted OSes.
target_link_libraries (task_executable task commands columns task ${TASK_LIBRARIES})
target_link_libraries (calc_executable task commands columns task ${TASK_LIBRARIES})
target_link_libraries (lex_executable task commands columns task ${TASK_LIBRARIES})
# Yes, 'task' (and hence libshared) is included twice, otherwise linking fails on assorted OSes.
target_link_libraries (task_executable task commands columns libshared task libshared ${TASK_LIBRARIES})
target_link_libraries (calc_executable task commands columns libshared task libshared ${TASK_LIBRARIES})
target_link_libraries (lex_executable task commands columns libshared task libshared ${TASK_LIBRARIES})
set_property (TARGET task_executable PROPERTY OUTPUT_NAME "task")

View File

@@ -1,603 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Color.h>
#include <sstream>
#include <vector>
#include <algorithm>
#include <main.h>
#include <Lexer.h>
#include <text.h>
#include <i18n.h>
#include <string>
// uint to string lookup table for Color::_colorize()
// _colorize() gets called _a lot_, having this lookup table is a cheap
// performance optimization.
const char *colorstring[] = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"10", "11", "12", "13", "14", "15", "16", "17", "18", "19",
"20", "21", "22", "23", "24", "25", "26", "27", "28", "29",
"30", "31", "32", "33", "34", "35", "36", "37", "38", "39",
"40", "41", "42", "43", "44", "45", "46", "47", "48", "49",
"50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
"60", "61", "62", "63", "64", "65", "66", "67", "68", "69",
"70", "71", "72", "73", "74", "75", "76", "77", "78", "79",
"80", "81", "82", "83", "84", "85", "86", "87", "88", "89",
"90", "91", "92", "93", "94", "95", "96", "97", "98", "99",
"100", "101", "102", "103", "104", "105", "106", "107", "108", "109",
"110", "111", "112", "113", "114", "115", "116", "117", "118", "119",
"120", "121", "122", "123", "124", "125", "126", "127", "128", "129",
"130", "131", "132", "133", "134", "135", "136", "137", "138", "139",
"140", "141", "142", "143", "144", "145", "146", "147", "148", "149",
"150", "151", "152", "153", "154", "155", "156", "157", "158", "159",
"160", "161", "162", "163", "164", "165", "166", "167", "168", "169",
"170", "171", "172", "173", "174", "175", "176", "177", "178", "179",
"180", "181", "182", "183", "184", "185", "186", "187", "188", "189",
"190", "191", "192", "193", "194", "195", "196", "197", "198", "199",
"200", "201", "202", "203", "204", "205", "206", "207", "208", "209",
"210", "211", "212", "213", "214", "215", "216", "217", "218", "219",
"220", "221", "222", "223", "224", "225", "226", "227", "228", "229",
"230", "231", "232", "233", "234", "235", "236", "237", "238", "239",
"240", "241", "242", "243", "244", "245", "246", "247", "248", "249",
"250", "251", "252", "253", "254", "255"
};
////////////////////////////////////////////////////////////////////////////////
static struct
{
Color::color_id id;
std::string english_name;
int index; // offset red=3 (therefore fg=33, bg=43)
} allColors[] =
{
// Color.h enum English Index
{ Color::nocolor, "none", 0},
{ Color::black, "black", 1}, // fg 29+0 bg 39+0
{ Color::red, "red", 2},
{ Color::green, "green", 3},
{ Color::yellow, "yellow", 4},
{ Color::blue, "blue", 5},
{ Color::magenta, "magenta", 6},
{ Color::cyan, "cyan", 7},
{ Color::white, "white", 8},
};
#define NUM_COLORS (sizeof (allColors) / sizeof (allColors[0]))
////////////////////////////////////////////////////////////////////////////////
Color::Color ()
: _value (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (const Color& other)
{
_value = other._value;
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (unsigned int c)
: _value (0)
{
if (!(c & _COLOR_HASFG)) _value &= ~_COLOR_FG;
if (!(c & _COLOR_HASBG)) _value &= ~_COLOR_BG;
_value = c & (_COLOR_256 | _COLOR_HASBG | _COLOR_HASFG |_COLOR_UNDERLINE |
_COLOR_INVERSE | _COLOR_BOLD | _COLOR_BRIGHT | _COLOR_BG |
_COLOR_FG);
}
////////////////////////////////////////////////////////////////////////////////
// Supports the following constructs:
// [bright] [color] [on color] [bright] [underline]
//
// Where [color] is one of:
// black
// red
// ...
// grayN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
// greyN 0 <= N <= 23 fg 38;5;232 + N bg 48;5;232 + N
// colorN 0 <= N <= 255 fg 38;5;N bg 48;5;N
// rgbRGB 0 <= R,G,B <= 5 fg 38;5;16 + R*36 + G*6 + B bg 48;5;16 + R*36 + G*6 + B
Color::Color (const std::string& spec)
: _value (0)
{
// Split spec into words.
std::vector <std::string> words;
split (words, spec, ' ');
// Construct the color as two separate colors, then blend them later. This
// make it possible to declare a color such as "color1 on black", and have
// the upgrade work properly.
unsigned int fg_value = 0;
unsigned int bg_value = 0;
bool bg = false;
int index;
for (auto& word : words)
{
word = Lexer::lowerCase (Lexer::trim (word));
if (word == "bold") fg_value |= _COLOR_BOLD;
else if (word == "bright") bg_value |= _COLOR_BRIGHT;
else if (word == "underline") fg_value |= _COLOR_UNDERLINE;
else if (word == "inverse") fg_value |= _COLOR_INVERSE;
else if (word == "on") bg = true;
// X where X is one of black, red, blue ...
else if ((index = find (word)) != -1)
{
if (index)
{
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
}
}
}
// greyN/grayN, where 0 <= N <= 23.
else if (! word.compare (0, 4, "grey", 4) ||
! word.compare (0, 4, "gray", 4))
{
index = strtol (word.substr (4).c_str (), nullptr, 10);
if (index < 0 || index > 23)
throw format (STRING_COLOR_UNRECOGNIZED, word);
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= (index + 232) << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index + 232;
fg_value |= _COLOR_256;
}
}
// rgbRGB, where 0 <= R,G,B <= 5.
else if (! word.compare (0, 3, "rgb", 3))
{
index = strtol (word.substr (3).c_str (), nullptr, 10);
if (word.length () != 6 ||
index < 0 || index > 555)
throw format (STRING_COLOR_UNRECOGNIZED, word);
int r = strtol (word.substr (3, 1).c_str (), nullptr, 10);
int g = strtol (word.substr (4, 1).c_str (), nullptr, 10);
int b = strtol (word.substr (5, 1).c_str (), nullptr, 10);
if (r < 0 || r > 5 ||
g < 0 || g > 5 ||
b < 0 || b > 5)
throw format (STRING_COLOR_UNRECOGNIZED, word);
index = 16 + r*36 + g*6 + b;
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
fg_value |= _COLOR_256;
}
}
// colorN, where 0 <= N <= 255.
else if (! word.compare (0, 5, "color", 5))
{
index = strtol (word.substr (5).c_str (), nullptr, 10);
if (index < 0 || index > 255)
throw format (STRING_COLOR_UNRECOGNIZED, word);
upgrade ();
if (bg)
{
bg_value |= _COLOR_HASBG;
bg_value |= index << 8;
bg_value |= _COLOR_256;
}
else
{
fg_value |= _COLOR_HASFG;
fg_value |= index;
fg_value |= _COLOR_256;
}
}
else if (word != "")
throw format (STRING_COLOR_UNRECOGNIZED, word);
}
// Now combine the fg and bg into a single color.
_value = fg_value;
blend (Color (bg_value));
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (color_id fg)
: _value (0)
{
if (fg != Color::nocolor)
{
_value |= _COLOR_HASFG;
_value |= fg;
}
}
////////////////////////////////////////////////////////////////////////////////
Color::Color (color_id fg, color_id bg, bool underline, bool bold, bool bright)
: _value (0)
{
_value |= ((underline ? 1 : 0) << 18)
| ((bold ? 1 : 0) << 17)
| ((bright ? 1 : 0) << 16);
if (bg != Color::nocolor)
{
_value |= _COLOR_HASBG;
_value |= (bg << 8);
}
if (fg != Color::nocolor)
{
_value |= _COLOR_HASFG;
_value |= fg;
}
}
////////////////////////////////////////////////////////////////////////////////
Color::operator std::string () const
{
std::string description;
if (_value & _COLOR_BOLD) description += "bold";
if (_value & _COLOR_UNDERLINE)
description += std::string (description.length () ? " " : "") + "underline";
if (_value & _COLOR_INVERSE)
description += std::string (description.length () ? " " : "") + "inverse";
if (_value & _COLOR_HASFG)
description += std::string (description.length () ? " " : "") + fg ();
if (_value & _COLOR_HASBG)
{
description += std::string (description.length () ? " " : "") + "on";
if (_value & _COLOR_BRIGHT)
description += std::string (description.length () ? " " : "") + "bright";
description += " " + bg ();
}
return description;
}
////////////////////////////////////////////////////////////////////////////////
Color::operator int () const
{
return (int) _value;
}
////////////////////////////////////////////////////////////////////////////////
// If 'other' has styles that are compatible, merge them into this. Colors in
// other take precedence.
void Color::blend (const Color& other)
{
if (!other.nontrivial ())
return;
Color c (other);
_value |= (c._value & _COLOR_UNDERLINE); // Always inherit underline.
_value |= (c._value & _COLOR_INVERSE); // Always inherit inverse.
// 16 <-- 16.
if (!(_value & _COLOR_256) &&
!(c._value & _COLOR_256))
{
_value |= (c._value & _COLOR_BOLD); // Inherit bold.
_value |= (c._value & _COLOR_BRIGHT); // Inherit bright.
if (c._value & _COLOR_HASFG)
{
_value |= _COLOR_HASFG; // There is now a color.
_value &= ~_COLOR_FG; // Remove previous color.
_value |= (c._value & _COLOR_FG); // Apply other color.
}
if (c._value & _COLOR_HASBG)
{
_value |= _COLOR_HASBG; // There is now a color.
_value &= ~_COLOR_BG; // Remove previous color.
_value |= (c._value & _COLOR_BG); // Apply other color.
}
return;
}
else
{
// Upgrade either color, if necessary.
if (!(_value & _COLOR_256)) upgrade ();
if (!(c._value & _COLOR_256)) c.upgrade ();
// 256 <-- 256.
if (c._value & _COLOR_HASFG)
{
_value |= _COLOR_HASFG; // There is now a color.
_value &= ~_COLOR_FG; // Remove previous color.
_value |= (c._value & _COLOR_FG); // Apply other color.
}
if (c._value & _COLOR_HASBG)
{
_value |= _COLOR_HASBG; // There is now a color.
_value &= ~_COLOR_BG; // Remove previous color.
_value |= (c._value & _COLOR_BG); // Apply other color.
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Color::upgrade ()
{
if (!(_value & _COLOR_256))
{
if (_value & _COLOR_HASFG)
{
bool bold = _value & _COLOR_BOLD;
unsigned int fg = _value & _COLOR_FG;
_value &= ~_COLOR_FG;
_value &= ~_COLOR_BOLD;
_value |= (bold ? fg + 7 : fg - 1);
}
if (_value & _COLOR_HASBG)
{
bool bright = _value & _COLOR_BRIGHT;
unsigned int bg = (_value & _COLOR_BG) >> 8;
_value &= ~_COLOR_BG;
_value &= ~_COLOR_BRIGHT;
_value |= (bright ? bg + 7 : bg - 1) << 8;
}
_value |= _COLOR_256;
}
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::colorize (const std::string& input) const
{
std::string result;
_colorize (result, input);
return result;
}
////////////////////////////////////////////////////////////////////////////////
// Sample color codes:
// red \033[31m
// bold red \033[91m
// underline red \033[4;31m
// bold underline red \033[1;4;31m
//
// on red \033[41m
// on bright red \033[101m
//
// 256 fg \033[38;5;Nm
// 256 bg \033[48;5;Nm
void Color::_colorize (std::string &result, const std::string& input) const
{
if (!nontrivial ())
{
result += input;
return;
}
int count = 0;
// 256 color
if (_value & _COLOR_256)
{
if (_value & _COLOR_UNDERLINE)
result += "\033[4m";
if (_value & _COLOR_INVERSE)
result += "\033[7m";
if (_value & _COLOR_HASFG)
{
result += "\033[38;5;";
result += colorstring[(_value & _COLOR_FG)];
result += "m";
}
if (_value & _COLOR_HASBG)
{
result += "\033[48;5;";
result += colorstring[((_value & _COLOR_BG) >> 8)];
result += "m";
}
result += input;
result += "\033[0m";
}
// 16 color
else
{
result += "\033[";
if (_value & _COLOR_BOLD)
{
if (count++) result += ";";
result += "1";
}
if (_value & _COLOR_UNDERLINE)
{
if (count++) result += ";";
result += "4";
}
if (_value & _COLOR_INVERSE)
{
if (count++) result += ";";
result += "7";
}
if (_value & _COLOR_HASFG)
{
if (count++) result += ";";
result += colorstring[(29 + (_value & _COLOR_FG))];
}
if (_value & _COLOR_HASBG)
{
if (count++) result += ";";
result += colorstring[((_value & _COLOR_BRIGHT ? 99 : 39) + ((_value & _COLOR_BG) >> 8))];
}
result += "m";
result += input;
result += "\033[0m";
}
}
////////////////////////////////////////////////////////////////////////////////
// Remove color codes from a string.
std::string Color::strip (const std::string& input)
{
int length = input.length ();
bool inside = false;
std::string output;
for (int i = 0; i < length; ++i)
{
if (inside)
{
if (input[i] == 'm')
inside = false;
}
else
{
if (input[i] == 033)
inside = true;
else
output += input[i];
}
}
return output;
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::colorize (const std::string& input, const std::string& spec)
{
Color c (spec);
return c.colorize (input);
}
////////////////////////////////////////////////////////////////////////////////
bool Color::nontrivial () const
{
return _value != 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
int Color::find (const std::string& input)
{
for (unsigned int i = 0; i < NUM_COLORS; ++i)
if (allColors[i].english_name == input)
return (int) i;
return -1;
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::fg () const
{
int index = _value & _COLOR_FG;
if (_value & _COLOR_256)
{
if (_value & _COLOR_HASFG)
{
std::stringstream s;
s << "color" << (_value & _COLOR_FG);
return s.str ();
}
}
else
{
for (unsigned int i = 0; i < NUM_COLORS; ++i)
if (allColors[i].index == index)
return allColors[i].english_name;
}
return "";
}
////////////////////////////////////////////////////////////////////////////////
std::string Color::bg () const
{
int index = (_value & _COLOR_BG) >> 8;
if (_value & _COLOR_256)
{
if (_value & _COLOR_HASBG)
{
std::stringstream s;
s << "color" << ((_value & _COLOR_BG) >> 8);
return s.str ();
}
}
else
{
for (unsigned int i = 0; i < NUM_COLORS; ++i)
if (allColors[i].index == index)
return allColors[i].english_name;
}
return "";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,75 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_COLOR
#define INCLUDED_COLOR
#include <string>
#define _COLOR_INVERSE 0x00400000 // Inverse attribute.
#define _COLOR_256 0x00200000 // 256-color mode.
#define _COLOR_HASBG 0x00100000 // Has background color (all values taken).
#define _COLOR_HASFG 0x00080000 // Has foreground color (all values taken).
#define _COLOR_UNDERLINE 0x00040000 // General underline attribute.
#define _COLOR_BOLD 0x00020000 // 16-color bold attribute.
#define _COLOR_BRIGHT 0x00010000 // 16-color bright background attribute.
#define _COLOR_BG 0x0000FF00 // 8-bit background color index.
#define _COLOR_FG 0x000000FF // 8-bit foreground color index.
class Color
{
public:
enum color_id {nocolor = 0, black, red, green, yellow, blue, magenta, cyan, white};
Color ();
Color (const Color&);
Color (unsigned int); // 256 | INVERSE | UNDERLINE | BOLD | BRIGHT | (BG << 8) | FG
Color (const std::string&); // "red on bright black"
Color (color_id); // fg.
Color (color_id, color_id, bool, bool, bool); // fg, bg, underline, bold, bright
operator std::string () const;
operator int () const;
void upgrade ();
void blend (const Color&);
std::string colorize (const std::string&) const;
static std::string colorize (const std::string&, const std::string&);
void _colorize (std::string&, const std::string&) const;
static std::string strip (const std::string&);
bool nontrivial () const;
private:
int find (const std::string&);
std::string fg () const;
std::string bg () const;
private:
unsigned int _value;
};
#endif

View File

@@ -1,637 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Config.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <sys/stat.h>
#include <inttypes.h>
#include <unistd.h>
#include <stdlib.h>
#include <ISO8601.h>
#include <FS.h>
#include <Timer.h>
#include <JSON.h>
#include <Lexer.h>
#include <text.h>
#include <util.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
// This string is used in two ways:
// 1) It is used to create a new .taskrc file, by copying it directly to disk.
// 2) It is parsed and used as default values for all Config.get calls.
std::string Config::_defaults =
"# Taskwarrior program configuration file.\n"
"# For more documentation, see http://taskwarrior.org or try 'man task', 'man task-color',\n"
"# 'man task-sync' or 'man taskrc'\n"
"\n"
"# Here is an example of entries that use the default, override and blank values\n"
"# variable=foo -- By specifying a value, this overrides the default\n"
"# variable= -- By specifying no value, this means no default\n"
"# #variable=foo -- By commenting out the line, or deleting it, this uses the default\n"
"\n"
"# Use the command 'task show' to see all defaults and overrides\n"
"\n"
"# Files\n"
"data.location=~/.task\n"
"locking=on # Use file-level locking\n"
"gc=on # Garbage-collect data files - DO NOT CHANGE unless you are sure\n"
"exit.on.missing.db=no # Whether to exit if ~/.task is not found\n"
"hooks=on # Master control switch for hooks\n"
"\n"
"# Terminal\n"
"detection=on # Detects terminal width\n"
"defaultwidth=80 # Without detection, assumed width\n"
"defaultheight=24 # Without detection, assumed height\n"
"avoidlastcolumn=no # Fixes Cygwin width problem\n"
"hyphenate=on # Hyphenates lines wrapped on non-word-breaks\n"
"#editor=vi # Preferred text editor\n"
"reserved.lines=1 # Assume a 1-line prompt\n"
"\n"
"# Miscellaneous\n"
"# # Comma-separated list. May contain any subset of:\n"
"verbose=blank,header,footnote,label,new-id,new-uuid,affected,edit,special,project,sync,unwait,recur\n"
"confirmation=yes # Confirmation on delete, big changes\n"
"recurrence=yes # Enable recurrence\n"
"recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n"
"allow.empty.filter=yes # An empty filter gets a warning and requires confirmation\n"
"indent.annotation=2 # Indent spaces for annotations\n"
"indent.report=0 # Indent spaces for whole report\n"
"row.padding=0 # Left and right padding for each row of report\n"
"column.padding=1 # Spaces between each column in a report\n"
"bulk=3 # 3 or more tasks considered a bulk change and is confirmed\n"
"nag=You have more urgent tasks. # Nag message to keep you honest\n" // TODO
"search.case.sensitive=yes # Setting to no allows case insensitive searches\n"
"active.indicator=* # What to show as an active task indicator\n"
"tag.indicator=+ # What to show as a tag indicator\n"
"dependency.indicator=D # What to show as a dependency indicator\n"
"recurrence.indicator=R # What to show as a task recurrence indicator\n"
"recurrence.limit=1 # Number of future recurring pending tasks\n"
"undo.style=side # Undo style - can be 'side', or 'diff'\n"
"regex=yes # Assume all search/filter strings are regexes\n"
"xterm.title=no # Sets xterm title for some commands\n"
"expressions=infix # Prefer infix over postfix expressions\n"
"json.array=on # Enclose JSON output in [ ]\n"
"json.depends.array=off # Encode dependencies as a JSON array\n"
"abbreviation.minimum=2 # Shortest allowed abbreviation\n"
"\n"
"# Dates\n"
"dateformat=Y-M-D # Preferred input and display date format\n"
"dateformat.holiday=YMD # Preferred input date format for holidays\n"
"dateformat.edit=Y-M-D H:N:S # Preferred display date format when editing\n"
"dateformat.info=Y-M-D H:N:S # Preferred display date format for information\n"
"dateformat.report= # Preferred display date format for reports\n"
"dateformat.annotation= # Preferred display date format for annotations\n"
"date.iso=yes # Enable ISO date support\n"
"weekstart="
STRING_DATE_SUNDAY
" # Sunday or Monday only\n"
"displayweeknumber=yes # Show week numbers on calendar\n"
"due=7 # Task is considered due in 7 days\n"
"\n"
"# Calendar controls\n"
"calendar.legend=yes # Display the legend on calendar\n"
"calendar.details=sparse # Calendar shows information for tasks w/due dates: full, sparse or none\n"
"calendar.details.report=list # Report to use when showing task information in cal\n"
"calendar.offset=no # Apply an offset value to control the first month of the calendar\n"
"calendar.offset.value=-1 # The number of months the first month of the calendar is moved\n"
"calendar.holidays=none # Show public holidays on calendar:full, sparse or none\n"
"#monthsperline=3 # Number of calendar months on a line\n"
"\n"
"# Journal controls\n"
"journal.time=no # Record start/stop commands as annotation\n"
"journal.time.start.annotation=Started task # Annotation description for the start journal entry\n"
"journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n"
"journal.info=on # Display task journal with info command\n"
"\n"
"# Dependency controls\n"
"dependency.reminder=on # Nags on dependency chain violations\n"
"dependency.confirmation=on # Should dependency chain repair be confirmed?\n"
"\n"
"# Urgency Coefficients\n"
"urgency.user.tag.next.coefficient=15.0 # Urgency coefficient for 'next' special tag\n"
"urgency.due.coefficient=12.0 # Urgency coefficient for due dates\n"
"urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n"
"urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n"
"urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n"
"urgency.age.coefficient=2.0 # Urgency coefficient for age\n"
"urgency.annotations.coefficient=1.0 # Urgency coefficient for annotations\n"
"urgency.tags.coefficient=1.0 # Urgency coefficient for tags\n"
"urgency.project.coefficient=1.0 # Urgency coefficient for projects\n"
"urgency.blocked.coefficient=-5.0 # Urgency coefficient for blocked tasks\n"
"urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n"
"urgency.inherit=off # Recursively inherit highest urgency value from blocked tasks\n"
"urgency.age.max=365 # Maximum age in days\n"
"\n"
"#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n"
"#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n"
"#urgency.uda.foo.coefficient=5.0 # Urgency coefficients for UDA 'foo'\n"
"\n"
"# Color controls.\n"
"color=on # Enable color\n"
"\n"
"rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda.\n"
"\n"
"# General decoration\n"
"rule.color.merge=yes\n"
"color.label=\n"
"color.label.sort=\n"
"color.alternate=on gray2\n"
"color.header=color3\n"
"color.footnote=color3\n"
"color.warning=bold red\n"
"color.error=white on red\n"
"color.debug=color4\n"
"\n"
"# Task state\n"
"color.completed=\n"
"color.deleted=\n"
"color.active=rgb555 on rgb410\n"
"color.recurring=rgb013\n"
"color.scheduled=on rgb001\n"
"color.until=\n"
"color.blocked=white on color8\n"
"color.blocking=black on color15\n"
"\n"
"# Project\n"
"color.project.none=\n"
"\n"
"# Priority UDA\n"
"color.uda.priority.H=color255\n"
"color.uda.priority.L=color245\n"
"color.uda.priority.M=color250\n"
"\n"
"# Tags\n"
"color.tag.next=rgb440\n"
"color.tag.none=\n"
"color.tagged=rgb031\n"
"\n"
"# Due\n"
"color.due.today=rgb400\n"
"color.due=color1\n"
"color.overdue=color9\n"
"\n"
"# Report: burndown\n"
"color.burndown.done=on rgb010\n"
"color.burndown.pending=on color9\n"
"color.burndown.started=on color11\n"
"\n"
"# Report: history\n"
"color.history.add=color0 on rgb500\n"
"color.history.delete=color0 on rgb550\n"
"color.history.done=color0 on rgb050\n"
"\n"
"# Report: summary\n"
"color.summary.background=white on color0\n"
"color.summary.bar=black on rgb141\n"
"\n"
"# Command: calendar\n"
"color.calendar.due.today=color15 on color1\n"
"color.calendar.due=color0 on color1\n"
"color.calendar.holiday=color0 on color11\n"
"color.calendar.overdue=color0 on color9\n"
"color.calendar.today=color15 on rgb013\n"
"color.calendar.weekend=on color235\n"
"color.calendar.weeknumber=rgb013\n"
"\n"
"# Command: sync\n"
"color.sync.added=rgb010\n"
"color.sync.changed=color11\n"
"color.sync.rejected=color9\n"
"\n"
"# Command: undo\n"
"color.undo.after=color2\n"
"color.undo.before=color1\n"
"\n"
"# UDA priority\n"
"uda.priority.type=string # UDA priority is a string type\n"
"uda.priority.label=Priority # UDA priority has a display label'\n"
"uda.priority.values=H,M,L, # UDA priority values are 'H', 'M', 'L' or ''\n"
" # UDA priority sorting is 'H' > 'M' > 'L' > '' (highest to lowest)\n"
"#uda.priority.default=M # UDA priority default value of 'M'\n"
"urgency.uda.priority.H.coefficient=6.0 # UDA priority coefficient for value 'H'\n"
"urgency.uda.priority.M.coefficient=3.9 # UDA priority coefficient for value 'M'\n"
"urgency.uda.priority.L.coefficient=1.8 # UDA priority coefficient for value 'L'\n"
"\n"
"# Here is the rule precedence order, highest to lowest.\n"
"# Note that these are just the color rule names, without the leading 'color.'\n"
"# and any trailing '.value'.\n"
"rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda.\n"
"\n"
"#default.project=foo # Default project for 'add' command\n"
"#default.due=eom # Default due date for 'add' command\n"
"default.command=next # When no arguments are specified\n"
"\n"
"_forcecolor=no # Forces color to be on, even for non TTY output\n"
"complete.all.tags=no # Include old tag names in '_ags' command\n"
"list.all.projects=no # Include old project names in 'projects' command\n"
"summary.all.projects=no # Include old project names in 'summary' command\n"
"list.all.tags=no # Include old tag names in 'tags' command\n"
"print.empty.columns=no # Print columns which have no data for any task\n"
"debug=no # Display diagnostics\n"
"sugar=yes # Syntactic sugar\n"
"obfuscate=no # Obfuscate data for error reporting\n"
"fontunderline=yes # Uses underlines rather than -------\n"
"\n"
"# WARNING: Please read the documentation (man task-sync) before setting up\n"
"# Taskwarrior for Taskserver synchronization.\n"
"#taskd.ca <certificate file>\n"
"#taskd.certificate <certificate file>\n"
"#taskd.credentials <organization>/<name>/<password>\n"
"#taskd.server <server>:<port>\n"
"taskd.trust=strict\n"
"#taskd.trust=ignore hostname\n"
"#taskd.trust=allow all\n"
"taskd.ciphers=NORMAL\n"
"\n"
"# Aliases - alternate names for commands\n"
"alias.rm=delete # Alias for the delete command\n"
"alias.history=history.monthly # Prefer monthly over annual history reports\n"
"alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history reports\n"
"alias.burndown=burndown.weekly # Prefer the weekly burndown chart\n"
"alias.shell=exec tasksh # Alias old shell command to new shell\n"
"\n"
"# Reports\n"
"\n"
"report.long.description=All details of tasks\n"
"report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until,Description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.remaining,scheduled,due,until,description\n"
"report.long.filter=status:pending\n"
"report.long.sort=modified-\n"
"\n"
"report.list.description=Most details of tasks\n"
"report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n"
"report.list.filter=status:pending\n"
"report.list.sort=start-,due+,project+,urgency-\n"
"\n"
"report.ls.description=Few details of tasks\n"
"report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.filter=status:pending\n"
"report.ls.sort=start-,description+\n"
"\n"
"report.minimal.description=Minimal details of tasks\n"
"report.minimal.labels=ID,Project,Tags,Description\n"
"report.minimal.columns=id,project,tags.count,description.count\n"
"report.minimal.filter=status:pending or status:waiting\n"
"report.minimal.sort=project+/,description+\n"
"\n"
"report.newest.description=Newest tasks\n"
"report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.newest.filter=status:pending or status:waiting\n"
"report.newest.sort=entry-\n"
"\n"
"report.oldest.description=Oldest tasks\n"
"report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.oldest.filter=status:pending or status:waiting\n"
"report.oldest.sort=entry+\n"
"\n"
"report.overdue.description=Overdue tasks\n"
"report.overdue.labels=ID,Active,Age,Deps,P,Project,Tag,R,S,Due,Until,Description,Urg\n"
"report.overdue.columns=id,start.age,entry.age,depends,priority,project,tags,recur.indicator,scheduled.countdown,due,until,description,urgency\n"
"report.overdue.filter=(status:pending or status:waiting) and +OVERDUE\n"
"report.overdue.sort=urgency-,due+\n"
"\n"
"report.active.description=Active tasks\n"
"report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait,scheduled.remaining,due,until,description\n"
"report.active.filter=status:pending and +ACTIVE\n"
"report.active.sort=project+,start+\n"
"\n"
"report.completed.description=Completed tasks\n"
"report.completed.labels=ID,UUID,Created,Completed,Age,Deps,P,Project,Tags,R,Due,Description\n"
"report.completed.columns=id,uuid.short,entry,end,entry.age,depends,priority,project,tags,recur.indicator,due,description\n"
"report.completed.filter=status:completed\n"
"report.completed.sort=end+\n"
"\n"
"report.recurring.description=Recurring Tasks\n"
"report.recurring.labels=ID,Active,Age,D,P,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
"report.recurring.filter=(status:pending or status:waiting) and (+PARENT or +CHILD)\n"
"report.recurring.sort=due+,urgency-,entry+\n"
"\n"
"report.waiting.description=Waiting (hidden) tasks\n"
"report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,Remaining,Sched,Due,Until,Description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.remaining,scheduled,due,until,description\n"
"report.waiting.filter=+WAITING\n"
"report.waiting.sort=due+,wait+,entry+\n"
"\n"
"report.all.description=All tasks\n"
"report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled.remaining,due,until.remaining,description\n"
"report.all.sort=entry-\n"
"\n"
"report.next.description=Most urgent tasks\n"
"report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency\n"
"report.next.filter=status:pending limit:page\n"
"report.next.sort=urgency-\n"
"\n"
"report.ready.description=Most urgent actionable tasks\n"
"report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n"
"report.ready.filter=+READY\n"
"report.ready.sort=start-,urgency-\n"
"\n"
"report.blocked.description=Blocked tasks\n"
"report.blocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n"
"report.blocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n"
"report.blocked.sort=due+,priority-,start-,project+\n"
"report.blocked.filter=status:pending +BLOCKED\n"
"\n"
"report.unblocked.description=Unblocked tasks\n"
"report.unblocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n"
"report.unblocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n"
"report.unblocked.sort=due+,priority-,start-,project+\n"
"report.unblocked.filter=status:pending -BLOCKED\n"
"\n"
"report.blocking.description=Blocking tasks\n"
"report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait,scheduled.remaining,due.relative,until.remaining,description.count,urgency\n"
"report.blocking.sort=urgency-,due+,entry+\n"
"report.blocking.filter= status:pending +BLOCKING\n"
"\n";
////////////////////////////////////////////////////////////////////////////////
// DO NOT CALL Config::setDefaults.
//
// This is a default constructor, and as such is only used to:
// a) initialize a default Context constructor
// b) run unit tests
//
// In all real use cases, Config::load is called.
Config::Config ()
: _original_file ()
{
}
////////////////////////////////////////////////////////////////////////////////
// Read the Configuration file and populate the *this map. The file format is
// simply lines with name=value pairs. Whitespace between name, = and value is
// not tolerated, but blank lines and comments starting with # are allowed.
//
// Nested files are now supported, with the following construct:
// include /absolute/path/to/file
//
void Config::load (const std::string& file, int nest /* = 1 */)
{
Timer timer ("Config::load (" + file + ")");
if (nest > 10)
throw std::string (STRING_CONFIG_OVERNEST);
// First time in, load the default values.
if (nest == 1)
{
setDefaults ();
_original_file = File (file);
}
// Read the file, then parse the contents.
std::string contents;
if (File::read (file, contents) && contents.length ())
parse (contents, nest);
}
////////////////////////////////////////////////////////////////////////////////
void Config::parse (const std::string& input, int nest /* = 1 */)
{
// Shortcut case for default constructor.
if (input.length () == 0)
return;
// Split the input into lines.
std::vector <std::string> lines;
split (lines, input, "\n");
// Parse each line.
for (auto& line : lines)
{
// Remove comments.
auto pound = line.find ("#"); // no i18n
if (pound != std::string::npos)
line = line.substr (0, pound);
line = Lexer::trim (line, " \t"); // no i18n
// Skip empty lines.
if (line.length () > 0)
{
auto equal = line.find ("="); // no i18n
if (equal != std::string::npos)
{
std::string key = Lexer::trim (line.substr (0, equal), " \t"); // no i18n
std::string value = Lexer::trim (line.substr (equal+1, line.length () - equal), " \t"); // no i18n
(*this)[key] = json::decode (value);
}
else
{
auto include = line.find ("include"); // no i18n.
if (include != std::string::npos)
{
Path included (Lexer::trim (line.substr (include + 7), " \t"));
if (included.is_absolute ())
{
if (included.readable ())
this->load (included, nest + 1);
else
throw format (STRING_CONFIG_READ_INCLUDE, included._data);
}
else
throw format (STRING_CONFIG_INCLUDE_PATH, included._data);
}
else
throw format (STRING_CONFIG_BAD_ENTRY, line);
}
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Config::createDefaultRC (const std::string& rc, const std::string& data)
{
// Override data.location in the defaults.
auto loc = _defaults.find ("data.location=~/.task");
// loc+0^ +14^ +21^
ISO8601d now;
std::stringstream contents;
contents << "# [Created by "
<< PACKAGE_STRING
<< " "
<< now.toString ("m/d/Y H:N:S")
<< "]\n"
<< _defaults.substr (0, loc + 14)
<< data
<< "\n\n# Color theme (uncomment one to use)\n"
<< "#include " << TASK_RCDIR << "/light-16.theme\n"
<< "#include " << TASK_RCDIR << "/light-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-16.theme\n"
<< "#include " << TASK_RCDIR << "/dark-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-red-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-green-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-blue-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-violets-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-yellow-green.theme\n"
<< "#include " << TASK_RCDIR << "/dark-gray-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-gray-blue-256.theme\n"
<< "#include " << TASK_RCDIR << "/solarized-dark-256.theme\n"
<< "#include " << TASK_RCDIR << "/solarized-light-256.theme\n"
<< "#include " << TASK_RCDIR << "/no-color.theme\n"
<< "\n";
// Write out the new file.
if (! File::write (rc, contents.str ()))
throw format (STRING_CONFIG_BAD_WRITE, rc);
}
////////////////////////////////////////////////////////////////////////////////
void Config::createDefaultData (const std::string& data)
{
Directory d (data);
if (! d.exists ())
{
if (getBoolean ("exit.on.missing.db"))
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
d.create ();
d += "hooks";
d.create ();
}
}
////////////////////////////////////////////////////////////////////////////////
void Config::setDefaults ()
{
parse (_defaults);
}
////////////////////////////////////////////////////////////////////////////////
void Config::clear ()
{
std::map <std::string, std::string>::clear ();
}
////////////////////////////////////////////////////////////////////////////////
bool Config::has (const std::string& key)
{
return (*this).find (key) != (*this).end ();
}
////////////////////////////////////////////////////////////////////////////////
// Return the configuration value given the specified key.
std::string Config::get (const std::string& key)
{
auto found = find (key);
if (found != end ())
return found->second;
return "";
}
////////////////////////////////////////////////////////////////////////////////
int Config::getInteger (const std::string& key)
{
auto found = find (key);
if (found != end ())
return strtoimax (found->second.c_str (), nullptr, 10);
return 0;
}
////////////////////////////////////////////////////////////////////////////////
double Config::getReal (const std::string& key)
{
//NOTE: Backwards compatible handling of next coefficient.
//TODO: Remove.
if (key == "urgency.user.tag.next.coefficient" and has ("urgency.next.coefficient"))
return getReal ("urgency.next.coefficient");
auto found = find (key);
if (found != end ())
return strtod (found->second.c_str (), nullptr);
return 0.0;
}
////////////////////////////////////////////////////////////////////////////////
bool Config::getBoolean (const std::string& key)
{
auto found = find (key);
if (found != end ())
{
std::string value = Lexer::lowerCase ((*this)[key]);
if (value == "t" || // TODO Deprecate
value == "true" ||
value == "1" ||
value == "+" || // TODO Deprecate
value == "y" ||
value == "yes" ||
value == "on" ||
value == "enable" || // TODO Deprecate
value == "enabled") // TODO Deprecate
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void Config::set (const std::string& key, const int value)
{
(*this)[key] = format (value);
}
////////////////////////////////////////////////////////////////////////////////
void Config::set (const std::string& key, const double value)
{
(*this)[key] = format (value, 1, 8);
}
////////////////////////////////////////////////////////////////////////////////
void Config::set (const std::string& key, const std::string& value)
{
(*this)[key] = value;
}
////////////////////////////////////////////////////////////////////////////////
// Provide a vector of all configuration keys.
void Config::all (std::vector<std::string>& items) const
{
for (auto& it : *this)
items.push_back (it.first);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,70 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_CONFIG
#define INCLUDED_CONFIG
#include <map>
#include <vector>
#include <string>
#include <FS.h>
class Config : public std::map <std::string, std::string>
{
public:
Config ();
Config (const Config&);
Config& operator= (const Config&);
void load (const std::string&, int nest = 1);
void parse (const std::string&, int nest = 1);
void createDefaultRC (const std::string&, const std::string&);
void createDefaultData (const std::string&);
void setDefaults ();
void clear ();
bool has (const std::string&);
std::string get (const std::string&);
int getInteger (const std::string&);
double getReal (const std::string&);
bool getBoolean (const std::string&);
void set (const std::string&, const int);
void set (const std::string&, const double);
void set (const std::string&, const std::string&);
void all (std::vector <std::string>&) const;
public:
File _original_file;
private:
static std::string _defaults;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -29,6 +29,7 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <assert.h>
#include <stdlib.h>
@@ -37,15 +38,361 @@
#include <FS.h>
#include <Eval.h>
#include <Variant.h>
#include <ISO8601.h>
#include <text.h>
#include <util.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <format.h>
#include <main.h>
#include <i18n.h>
#ifdef HAVE_COMMIT
#include <commit.h>
#endif
#include <stdio.h>
#include <sys/ioctl.h>
#ifdef SOLARIS
#include <sys/termios.h>
#endif
////////////////////////////////////////////////////////////////////////////////
// This string is parsed and used as default values for configuration.
std::string configurationDefaults =
"# Taskwarrior program configuration file.\n"
"# For more documentation, see http://taskwarrior.org or try 'man task', 'man task-color',\n"
"# 'man task-sync' or 'man taskrc'\n"
"\n"
"# Here is an example of entries that use the default, override and blank values\n"
"# variable=foo -- By specifying a value, this overrides the default\n"
"# variable= -- By specifying no value, this means no default\n"
"# #variable=foo -- By commenting out the line, or deleting it, this uses the default\n"
"\n"
"# Use the command 'task show' to see all defaults and overrides\n"
"\n"
"# Files\n"
"data.location=~/.task\n"
"locking=1 # Use file-level locking\n"
"gc=1 # Garbage-collect data files - DO NOT CHANGE unless you are sure\n"
"exit.on.missing.db=0 # Whether to exit if ~/.task is not found\n"
"hooks=1 # Master control switch for hooks\n"
"\n"
"# Terminal\n"
"detection=1 # Detects terminal width\n"
"defaultwidth=80 # Without detection, assumed width\n"
"defaultheight=24 # Without detection, assumed height\n"
"avoidlastcolumn=0 # Fixes Cygwin width problem\n"
"hyphenate=1 # Hyphenates lines wrapped on non-word-breaks\n"
"#editor=vi # Preferred text editor\n"
"reserved.lines=1 # Assume a 1-line prompt\n"
"\n"
"# Miscellaneous\n"
"# # Comma-separated list. May contain any subset of:\n"
"verbose=blank,header,footnote,label,new-id,affected,edit,special,project,sync,unwait,override,recur\n"
"confirmation=1 # Confirmation on delete, big changes\n"
"recurrence=1 # Enable recurrence\n"
"recurrence.confirmation=prompt # Confirmation for propagating changes among recurring tasks (yes/no/prompt)\n"
"allow.empty.filter=1 # An empty filter gets a warning and requires confirmation\n"
"indent.annotation=2 # Indent spaces for annotations\n"
"indent.report=0 # Indent spaces for whole report\n"
"row.padding=0 # Left and right padding for each row of report\n"
"column.padding=1 # Spaces between each column in a report\n"
"bulk=3 # 3 or more tasks considered a bulk change and is confirmed\n"
"nag=You have more urgent tasks. # Nag message to keep you honest\n" // TODO
"search.case.sensitive=1 # Setting to no allows case insensitive searches\n"
"active.indicator=* # What to show as an active task indicator\n"
"tag.indicator=+ # What to show as a tag indicator\n"
"dependency.indicator=D # What to show as a dependency indicator\n"
"recurrence.indicator=R # What to show as a task recurrence indicator\n"
"recurrence.limit=1 # Number of future recurring pending tasks\n"
"undo.style=side # Undo style - can be 'side', or 'diff'\n"
"regex=1 # Assume all search/filter strings are regexes\n"
"xterm.title=0 # Sets xterm title for some commands\n"
"expressions=infix # Prefer infix over postfix expressions\n"
"json.array=1 # Enclose JSON output in [ ]\n"
"json.depends.array=0 # Encode dependencies as a JSON array\n"
"abbreviation.minimum=2 # Shortest allowed abbreviation\n"
"\n"
"# Dates\n"
"dateformat=Y-M-D # Preferred input and display date format\n"
"dateformat.holiday=YMD # Preferred input date format for holidays\n"
"dateformat.edit=Y-M-D H:N:S # Preferred display date format when editing\n"
"dateformat.info=Y-M-D H:N:S # Preferred display date format for information\n"
"dateformat.report= # Preferred display date format for reports\n"
"dateformat.annotation= # Preferred display date format for annotations\n"
"date.iso=1 # Enable ISO date support\n"
"weekstart=sunday # Sunday or Monday only\n"
"displayweeknumber=1 # Show week numbers on calendar\n"
"due=7 # Task is considered due in 7 days\n"
"\n"
"# Calendar controls\n"
"calendar.legend=1 # Display the legend on calendar\n"
"calendar.details=sparse # Calendar shows information for tasks w/due dates: full, sparse or none\n"
"calendar.details.report=list # Report to use when showing task information in cal\n"
"calendar.offset=0 # Apply an offset value to control the first month of the calendar\n"
"calendar.offset.value=-1 # The number of months the first month of the calendar is moved\n"
"calendar.holidays=none # Show public holidays on calendar:full, sparse or none\n"
"#monthsperline=3 # Number of calendar months on a line\n"
"\n"
"# Journal controls\n"
"journal.time=0 # Record start/stop commands as annotation\n"
"journal.time.start.annotation=Started task # Annotation description for the start journal entry\n"
"journal.time.stop.annotation=Stopped task # Annotation description for the stop journal entry\n"
"journal.info=1 # Display task journal with info command\n"
"\n"
"# Dependency controls\n"
"dependency.reminder=1 # Nags on dependency chain violations\n"
"dependency.confirmation=1 # Should dependency chain repair be confirmed?\n"
"\n"
"# Urgency Coefficients\n"
"urgency.user.tag.next.coefficient=15.0 # Urgency coefficient for 'next' special tag\n"
"urgency.due.coefficient=12.0 # Urgency coefficient for due dates\n"
"urgency.blocking.coefficient=8.0 # Urgency coefficient for blocking tasks\n"
"urgency.active.coefficient=4.0 # Urgency coefficient for active tasks\n"
"urgency.scheduled.coefficient=5.0 # Urgency coefficient for scheduled tasks\n"
"urgency.age.coefficient=2.0 # Urgency coefficient for age\n"
"urgency.annotations.coefficient=1.0 # Urgency coefficient for annotations\n"
"urgency.tags.coefficient=1.0 # Urgency coefficient for tags\n"
"urgency.project.coefficient=1.0 # Urgency coefficient for projects\n"
"urgency.blocked.coefficient=-5.0 # Urgency coefficient for blocked tasks\n"
"urgency.waiting.coefficient=-3.0 # Urgency coefficient for waiting status\n"
"urgency.inherit=0 # Recursively inherit highest urgency value from blocked tasks\n"
"urgency.age.max=365 # Maximum age in days\n"
"\n"
"#urgency.user.project.foo.coefficient=5.0 # Urgency coefficients for 'foo' project\n"
"#urgency.user.tag.foo.coefficient=5.0 # Urgency coefficients for 'foo' tag\n"
"#urgency.uda.foo.coefficient=5.0 # Urgency coefficients for UDA 'foo'\n"
"\n"
"# Color controls.\n"
"color=1 # Enable color\n"
"\n"
"# Here is the rule precedence order, highest to lowest.\n"
"# Note that these are just the color rule names, without the leading 'color.'\n"
"# and any trailing '.value'.\n"
"rule.precedence.color=deleted,completed,active,keyword.,tag.,project.,overdue,scheduled,due.today,due,blocked,blocking,recurring,tagged,uda.\n"
"\n"
"# General decoration\n"
"rule.color.merge=1\n"
"color.label=\n"
"color.label.sort=\n"
"color.alternate=on gray2\n"
"color.header=color3\n"
"color.footnote=color3\n"
"color.warning=bold red\n"
"color.error=white on red\n"
"color.debug=color4\n"
"\n"
"# Task state\n"
"color.completed=\n"
"color.deleted=\n"
"color.active=rgb555 on rgb410\n"
"color.recurring=rgb013\n"
"color.scheduled=on rgb001\n"
"color.until=\n"
"color.blocked=white on color8\n"
"color.blocking=black on color15\n"
"\n"
"# Project\n"
"color.project.none=\n"
"\n"
"# Priority UDA\n"
"color.uda.priority.H=color255\n"
"color.uda.priority.L=color245\n"
"color.uda.priority.M=color250\n"
"\n"
"# Tags\n"
"color.tag.next=rgb440\n"
"color.tag.none=\n"
"color.tagged=rgb031\n"
"\n"
"# Due\n"
"color.due.today=rgb400\n"
"color.due=color1\n"
"color.overdue=color9\n"
"\n"
"# Report: burndown\n"
"color.burndown.done=on rgb010\n"
"color.burndown.pending=on color9\n"
"color.burndown.started=on color11\n"
"\n"
"# Report: history\n"
"color.history.add=color0 on rgb500\n"
"color.history.delete=color0 on rgb550\n"
"color.history.done=color0 on rgb050\n"
"\n"
"# Report: summary\n"
"color.summary.background=white on color0\n"
"color.summary.bar=black on rgb141\n"
"\n"
"# Command: calendar\n"
"color.calendar.due.today=color15 on color1\n"
"color.calendar.due=color0 on color1\n"
"color.calendar.holiday=color0 on color11\n"
"color.calendar.overdue=color0 on color9\n"
"color.calendar.today=color15 on rgb013\n"
"color.calendar.weekend=on color235\n"
"color.calendar.weeknumber=rgb013\n"
"\n"
"# Command: sync\n"
"color.sync.added=rgb010\n"
"color.sync.changed=color11\n"
"color.sync.rejected=color9\n"
"\n"
"# Command: undo\n"
"color.undo.after=color2\n"
"color.undo.before=color1\n"
"\n"
"# UDA priority\n"
"uda.priority.type=string # UDA priority is a string type\n"
"uda.priority.label=Priority # UDA priority has a display label'\n"
"uda.priority.values=H,M,L, # UDA priority values are 'H', 'M', 'L' or ''\n"
" # UDA priority sorting is 'H' > 'M' > 'L' > '' (highest to lowest)\n"
"#uda.priority.default=M # UDA priority default value of 'M'\n"
"urgency.uda.priority.H.coefficient=6.0 # UDA priority coefficient for value 'H'\n"
"urgency.uda.priority.M.coefficient=3.9 # UDA priority coefficient for value 'M'\n"
"urgency.uda.priority.L.coefficient=1.8 # UDA priority coefficient for value 'L'\n"
"\n"
"#default.project=foo # Default project for 'add' command\n"
"#default.due=eom # Default due date for 'add' command\n"
"#default.scheduled=eom # Default scheduled date for 'add' command\n"
"default.command=next # When no arguments are specified\n"
"default.timesheet.filter=( +PENDING and start.after:now-4wks ) or ( +COMPLETED and end.after:now-4wks )\n"
"\n"
"_forcecolor=0 # Forces color to be on, even for non TTY output\n"
"complete.all.tags=0 # Include old tag names in '_ags' command\n"
"list.all.projects=0 # Include old project names in 'projects' command\n"
"summary.all.projects=0 # Include old project names in 'summary' command\n"
"list.all.tags=0 # Include old tag names in 'tags' command\n"
"print.empty.columns=0 # Print columns which have no data for any task\n"
"debug=0 # Display diagnostics\n"
"debug.tls=0 # Sync diagnostics\n"
"sugar=1 # Syntactic sugar\n"
"obfuscate=0 # Obfuscate data for error reporting\n"
"fontunderline=1 # Uses underlines rather than -------\n"
"\n"
"# WARNING: Please read the documentation (man task-sync) before setting up\n"
"# Taskwarrior for Taskserver synchronization.\n"
"#taskd.ca=<certificate file>\n"
"#taskd.certificate=<certificate file>\n"
"#taskd.credentials=<organization>/<name>/<password>\n"
"#taskd.server=<server>:<port>\n"
"taskd.trust=strict\n"
"#taskd.trust=ignore hostname\n"
"#taskd.trust=allow all\n"
"taskd.ciphers=NORMAL\n"
"\n"
"# Aliases - alternate names for commands\n"
"alias.rm=delete # Alias for the delete command\n"
"alias.history=history.monthly # Prefer monthly over annual history reports\n"
"alias.ghistory=ghistory.monthly # Prefer monthly graphical over annual history reports\n"
"alias.burndown=burndown.weekly # Prefer the weekly burndown chart\n"
"alias.shell=exec tasksh # Alias old shell command to new shell\n"
"\n"
"# Reports\n"
"\n"
"report.long.description=All details of tasks\n"
"report.long.labels=ID,A,Created,Mod,Deps,P,Project,Tags,Recur,Wait,Sched,Due,Until,Description\n"
"report.long.columns=id,start.active,entry,modified.age,depends,priority,project,tags,recur,wait.remaining,scheduled,due,until,description\n"
"report.long.filter=status:pending\n"
"report.long.sort=modified-\n"
"\n"
"report.list.description=Most details of tasks\n"
"report.list.labels=ID,Active,Age,D,P,Project,Tags,R,Sch,Due,Until,Description,Urg\n"
"report.list.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due,until.remaining,description.count,urgency\n"
"report.list.filter=status:pending\n"
"report.list.sort=start-,due+,project+,urgency-\n"
"\n"
"report.ls.description=Few details of tasks\n"
"report.ls.labels=ID,A,D,Project,Tags,R,Wait,S,Due,Until,Description\n"
"report.ls.columns=id,start.active,depends.indicator,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due.countdown,until.countdown,description.count\n"
"report.ls.filter=status:pending\n"
"report.ls.sort=start-,description+\n"
"\n"
"report.minimal.description=Minimal details of tasks\n"
"report.minimal.labels=ID,Project,Tags,Description\n"
"report.minimal.columns=id,project,tags.count,description.count\n"
"report.minimal.filter=status:pending or status:waiting\n"
"report.minimal.sort=project+/,description+\n"
"\n"
"report.newest.description=Newest tasks\n"
"report.newest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.newest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.newest.filter=status:pending or status:waiting\n"
"report.newest.sort=entry-\n"
"\n"
"report.oldest.description=Oldest tasks\n"
"report.oldest.labels=ID,Active,Created,Age,Mod,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.oldest.columns=id,start.age,entry,entry.age,modified.age,depends.indicator,priority,project,tags,recur.indicator,wait.remaining,scheduled.countdown,due,until.age,description\n"
"report.oldest.filter=status:pending or status:waiting\n"
"report.oldest.sort=entry+\n"
"\n"
"report.overdue.description=Overdue tasks\n"
"report.overdue.labels=ID,Active,Age,Deps,P,Project,Tag,R,S,Due,Until,Description,Urg\n"
"report.overdue.columns=id,start.age,entry.age,depends,priority,project,tags,recur.indicator,scheduled.countdown,due,until,description,urgency\n"
"report.overdue.filter=(status:pending or status:waiting) and +OVERDUE\n"
"report.overdue.sort=urgency-,due+\n"
"\n"
"report.active.description=Active tasks\n"
"report.active.labels=ID,Started,Active,Age,D,P,Project,Tags,Recur,W,Sch,Due,Until,Description\n"
"report.active.columns=id,start,start.age,entry.age,depends.indicator,priority,project,tags,recur,wait,scheduled.remaining,due,until,description\n"
"report.active.filter=status:pending and +ACTIVE\n"
"report.active.sort=project+,start+\n"
"\n"
"report.completed.description=Completed tasks\n"
"report.completed.labels=ID,UUID,Created,Completed,Age,Deps,P,Project,Tags,R,Due,Description\n"
"report.completed.columns=id,uuid.short,entry,end,entry.age,depends,priority,project,tags,recur.indicator,due,description\n"
"report.completed.filter=status:completed\n"
"report.completed.sort=end+\n"
"\n"
"report.recurring.description=Recurring Tasks\n"
"report.recurring.labels=ID,Active,Age,D,P,Project,Tags,Recur,Sch,Due,Until,Description,Urg\n"
"report.recurring.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur,scheduled.countdown,due,until.remaining,description,urgency\n"
"report.recurring.filter=(status:pending or status:waiting) and (+PARENT or +CHILD)\n"
"report.recurring.sort=due+,urgency-,entry+\n"
"\n"
"report.waiting.description=Waiting (hidden) tasks\n"
"report.waiting.labels=ID,A,Age,D,P,Project,Tags,R,Wait,Remaining,Sched,Due,Until,Description\n"
"report.waiting.columns=id,start.active,entry.age,depends.indicator,priority,project,tags,recur.indicator,wait,wait.remaining,scheduled,due,until,description\n"
"report.waiting.filter=+WAITING\n"
"report.waiting.sort=due+,wait+,entry+\n"
"\n"
"report.all.description=All tasks\n"
"report.all.labels=ID,St,UUID,A,Age,Done,D,P,Project,Tags,R,Wait,Sch,Due,Until,Description\n"
"report.all.columns=id,status.short,uuid.short,start.active,entry.age,end.age,depends.indicator,priority,project.parent,tags.count,recur.indicator,wait.remaining,scheduled.remaining,due,until.remaining,description\n"
"report.all.sort=entry-\n"
"\n"
"report.next.description=Most urgent tasks\n"
"report.next.labels=ID,Active,Age,Deps,P,Project,Tag,Recur,S,Due,Until,Description,Urg\n"
"report.next.columns=id,start.age,entry.age,depends,priority,project,tags,recur,scheduled.countdown,due.relative,until.remaining,description,urgency\n"
"report.next.filter=status:pending limit:page\n"
"report.next.sort=urgency-\n"
"\n"
"report.ready.description=Most urgent actionable tasks\n"
"report.ready.labels=ID,Active,Age,D,P,Project,Tags,R,S,Due,Until,Description,Urg\n"
"report.ready.columns=id,start.age,entry.age,depends.indicator,priority,project,tags,recur.indicator,scheduled.countdown,due.countdown,until.remaining,description,urgency\n"
"report.ready.filter=+READY\n"
"report.ready.sort=start-,urgency-\n"
"\n"
"report.blocked.description=Blocked tasks\n"
"report.blocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n"
"report.blocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n"
"report.blocked.sort=due+,priority-,start-,project+\n"
"report.blocked.filter=status:pending +BLOCKED\n"
"\n"
"report.unblocked.description=Unblocked tasks\n"
"report.unblocked.columns=id,depends,project,priority,due,start.active,entry.age,description\n"
"report.unblocked.labels=ID,Deps,Proj,Pri,Due,Active,Age,Description\n"
"report.unblocked.sort=due+,priority-,start-,project+\n"
"report.unblocked.filter=status:pending -BLOCKED\n"
"\n"
"report.blocking.description=Blocking tasks\n"
"report.blocking.labels=ID,UUID,A,Deps,Project,Tags,R,W,Sch,Due,Until,Description,Urg\n"
"report.blocking.columns=id,uuid.short,start.active,depends,project,tags,recur,wait,scheduled.remaining,due.relative,until.remaining,description.count,urgency\n"
"report.blocking.sort=urgency-,due+,entry+\n"
"report.blocking.filter=status:pending +BLOCKING\n"
"\n"
"report.timesheet.filter=(+PENDING and start.after:now-4wks) or (+COMPLETED and end.after:now-4wks)\n"
"\n";
// Supported modifiers, synonyms on the same line.
static const char* modifierNames[] =
{
@@ -65,19 +412,18 @@ static const char* modifierNames[] =
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
Context* Context::context;
////////////////////////////////////////////////////////////////////////////////
Context::Context ()
: rc_file ("~/.taskrc")
, data_dir ("~/.task")
, config ()
, tdb2 ()
, determine_color_use (true)
, use_color (true)
, run_gc (true)
, verbosity_legacy (false)
, terminal_width (0)
, terminal_height (0)
Context& Context::getContext ()
{
return *Context::context;
}
////////////////////////////////////////////////////////////////////////////////
void Context::setContext (Context* context)
{
Context::context = context;
}
////////////////////////////////////////////////////////////////////////////////
@@ -94,7 +440,6 @@ Context::~Context ()
int Context::initialize (int argc, const char** argv)
{
timer_total.start ();
timer_init.start ();
int rc = 0;
try
@@ -116,11 +461,17 @@ int Context::initialize (int argc, const char** argv)
if (override)
{
rc_file = File (override);
header (format (STRING_CONTEXT_RC_OVERRIDE, rc_file._data));
header (format ("TASKRC override: {1}", rc_file._data));
}
// Artificial scope for timing purposes.
{
Timer timer;
config.parse (configurationDefaults);
config.load (rc_file._data);
debugTiming (format ("Config::load ({1})", rc_file._data), timer);
}
config.clear ();
config.load (rc_file);
CLI2::applyOverrides (argc, argv);
////////////////////////////////////////////////////////////////////////////
@@ -141,7 +492,7 @@ int Context::initialize (int argc, const char** argv)
{
data_dir = Directory (override);
config.set ("data.location", data_dir._data);
header (format (STRING_CONTEXT_DATA_OVERRIDE, data_dir._data));
header (format ("TASKDATA override: {1}", data_dir._data));
}
tdb2.set_location (data_dir);
@@ -207,8 +558,8 @@ int Context::initialize (int argc, const char** argv)
cli2.analyze ();
// Extract a recomposed command line.
bool foundDefault = false;
bool foundAssumed = false;
auto foundDefault = false;
auto foundAssumed = false;
std::string combined;
for (auto& a : cli2._args)
{
@@ -228,7 +579,7 @@ int Context::initialize (int argc, const char** argv)
header ("[" + combined + "]");
if (foundAssumed)
header (STRING_ASSUME_INFO);
header ("No command specified - assuming 'information'.");
////////////////////////////////////////////////////////////////////////////
//
@@ -253,7 +604,7 @@ int Context::initialize (int argc, const char** argv)
catch (...)
{
error (STRING_UNKNOWN_ERROR);
error ("Unknown error. Please report.");
rc = 3;
}
@@ -265,9 +616,9 @@ int Context::initialize (int argc, const char** argv)
{
for (auto& d : debugMessages)
if (color ())
std::cerr << colorizeDebug (d) << "\n";
std::cerr << colorizeDebug (d) << '\n';
else
std::cerr << d << "\n";
std::cerr << d << '\n';
}
// Dump all headers, controlled by 'header' verbosity token.
@@ -275,9 +626,9 @@ int Context::initialize (int argc, const char** argv)
{
for (auto& h : headers)
if (color ())
std::cerr << colorizeHeader (h) << "\n";
std::cerr << colorizeHeader (h) << '\n';
else
std::cerr << h << "\n";
std::cerr << h << '\n';
}
// Dump all footnotes, controlled by 'footnote' verbosity token.
@@ -285,21 +636,21 @@ int Context::initialize (int argc, const char** argv)
{
for (auto& f : footnotes)
if (color ())
std::cerr << colorizeFootnote (f) << "\n";
std::cerr << colorizeFootnote (f) << '\n';
else
std::cerr << f << "\n";
std::cerr << f << '\n';
}
// Dump all errors, non-maskable.
// Colorized as footnotes.
for (auto& e : errors)
if (color ())
std::cerr << colorizeFootnote (e) << "\n";
std::cerr << colorizeFootnote (e) << '\n';
else
std::cerr << e << "\n";
std::cerr << e << '\n';
}
timer_init.stop ();
time_init_us += timer_total.total_us ();
return rc;
}
@@ -317,38 +668,38 @@ int Context::run ()
hooks.onExit (); // No chance to update data.
timer_total.stop ();
time_total_us += timer_total.total_us ();
std::stringstream s;
s << "Perf "
<< PACKAGE_STRING
<< " "
<< ' '
#ifdef HAVE_COMMIT
<< COMMIT
#else
<< "-"
<< '-'
#endif
<< " "
<< ISO8601d ().toISO ()
<< ' '
<< Datetime ().toISO ()
<< " init:" << timer_init.total ()
<< " load:" << timer_load.total ()
<< " gc:" << timer_gc.total ()
<< " filter:" << timer_filter.total ()
<< " commit:" << timer_commit.total ()
<< " sort:" << timer_sort.total ()
<< " render:" << timer_render.total ()
<< " hooks:" << timer_hooks.total ()
<< " other:" << timer_total.total () -
timer_init.total () -
timer_load.total () -
timer_gc.total () -
timer_filter.total () -
timer_commit.total () -
timer_sort.total () -
timer_render.total () -
timer_hooks.total ()
<< " total:" << timer_total.total ()
<< "\n";
<< " init:" << time_init_us
<< " load:" << time_load_us
<< " gc:" << (time_gc_us > 0 ? time_gc_us - time_load_us : time_gc_us)
<< " filter:" << time_filter_us
<< " commit:" << time_commit_us
<< " sort:" << time_sort_us
<< " render:" << time_render_us
<< " hooks:" << time_hooks_us
<< " other:" << time_total_us -
time_init_us -
time_gc_us -
time_filter_us -
time_commit_us -
time_sort_us -
time_render_us -
time_hooks_us
<< " total:" << time_total_us
<< '\n';
debug (s.str ());
}
@@ -366,7 +717,7 @@ int Context::run ()
catch (...)
{
error (STRING_UNKNOWN_ERROR);
error ("Unknown error. Please report.");
rc = 3;
}
@@ -375,9 +726,9 @@ int Context::run ()
{
for (auto& d : debugMessages)
if (color ())
std::cerr << colorizeDebug (d) << "\n";
std::cerr << colorizeDebug (d) << '\n';
else
std::cerr << d << "\n";
std::cerr << d << '\n';
}
// Dump all headers, controlled by 'header' verbosity token.
@@ -385,9 +736,9 @@ int Context::run ()
{
for (auto& h : headers)
if (color ())
std::cerr << colorizeHeader (h) << "\n";
std::cerr << colorizeHeader (h) << '\n';
else
std::cerr << h << "\n";
std::cerr << h << '\n';
}
// Dump the report output.
@@ -398,18 +749,18 @@ int Context::run ()
{
for (auto& f : footnotes)
if (color ())
std::cerr << colorizeFootnote (f) << "\n";
std::cerr << colorizeFootnote (f) << '\n';
else
std::cerr << f << "\n";
std::cerr << f << '\n';
}
// Dump all errors, non-maskable.
// Colorized as footnotes.
for (auto& e : errors)
if (color ())
std::cerr << colorizeError (e) << "\n";
std::cerr << colorizeError (e) << '\n';
else
std::cerr << e << "\n";
std::cerr << e << '\n';
return rc;
}
@@ -469,6 +820,70 @@ int Context::dispatch (std::string &out)
return commands["help"]->execute (out);
}
////////////////////////////////////////////////////////////////////////////////
int Context::getWidth ()
{
// Determine window size.
auto width = config.getInteger ("defaultwidth");
// A zero width value means 'infinity', which is approximated here by 2^16.
if (width == 0)
return 65536;
if (config.getBoolean ("detection"))
{
if (terminal_width == 0 &&
terminal_height == 0)
{
unsigned short buff[4];
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1)
{
terminal_height = buff[0];
terminal_width = buff[1];
}
}
width = terminal_width;
// Ncurses does this, and perhaps we need to as well, to avoid a problem on
// Cygwin where the display goes right up to the terminal width, and causes
// an odd color wrapping problem.
if (config.getBoolean ("avoidlastcolumn"))
--width;
}
return width;
}
////////////////////////////////////////////////////////////////////////////////
int Context::getHeight ()
{
// Determine window size.
auto height = config.getInteger ("defaultheight");
// A zero height value means 'infinity', which is approximated here by 2^16.
if (height == 0)
return 65536;
if (config.getBoolean ("detection"))
{
if (terminal_width == 0 &&
terminal_height == 0)
{
unsigned short buff[4];
if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &buff) != -1)
{
terminal_height = buff[0];
terminal_width = buff[1];
}
}
height = terminal_height;
}
return height;
}
////////////////////////////////////////////////////////////////////////////////
bool Context::color ()
{
@@ -519,7 +934,8 @@ bool Context::verbose (const std::string& token)
if (verbosity.empty ())
{
verbosity_legacy = config.getBoolean ("verbose");
split (verbosity, config.get ("verbose"), ',');
for (auto& token : split (config.get ("verbose"), ','))
verbosity.insert (token);
// Regular feedback means almost everything.
// This odd test is to see if a Boolean-false value is a real one, which
@@ -542,6 +958,7 @@ bool Context::verbose (const std::string& token)
v != "sync" && //
v != "filter" && //
v != "unwait" && //
v != "override" && //
v != "recur") //
{
// This list emulates rc.verbose=off in version 1.9.4.
@@ -553,7 +970,7 @@ bool Context::verbose (const std::string& token)
if (! verbosity.count ("footnote"))
{
// TODO: Some of these may not use footnotes yet. They should.
for (auto flag : {"affected", "new-id", "new-uuid", "project", "unwait", "recur"})
for (auto flag : {"affected", "new-id", "new-uuid", "project", "unwait", "override", "recur"})
{
if (verbosity.count (flag))
{
@@ -600,7 +1017,7 @@ void Context::getLimits (int& rows, int& lines)
lines = 0;
// This is an integer specified as a filter (limit:10).
std::string limit = config.get ("limit");
auto limit = config.get ("limit");
if (limit != "")
{
if (limit == "page")
@@ -610,7 +1027,7 @@ void Context::getLimits (int& rows, int& lines)
}
else
{
rows = (int) strtol (limit.c_str (), NULL, 10);
rows = (int) strtol (limit.c_str (), nullptr, 10);
lines = 0;
}
}
@@ -621,21 +1038,23 @@ void Context::getLimits (int& rows, int& lines)
// easier, it has been decoupled from Context.
void Context::staticInitialization ()
{
CLI2::minimumMatchLength = config.getInteger ("abbreviation.minimum");
Lexer::minimumMatchLength = config.getInteger ("abbreviation.minimum");
ISO8601d::minimumMatchLength = config.getInteger ("abbreviation.minimum");
CLI2::minimumMatchLength = config.getInteger ("abbreviation.minimum");
Lexer::minimumMatchLength = config.getInteger ("abbreviation.minimum");
Task::defaultProject = config.get ("default.project");
Task::defaultDue = config.get ("default.due");
Task::defaultProject = config.get ("default.project");
Task::defaultDue = config.get ("default.due");
Task::defaultScheduled = config.get ("default.scheduled");
Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive");
Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex");
Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat");
ISO8601p::isoEnabled = ISO8601d::isoEnabled = config.getBoolean ("date.iso");
Task::searchCaseSensitive = Variant::searchCaseSensitive = config.getBoolean ("search.case.sensitive");
Task::regex = Variant::searchUsingRegex = config.getBoolean ("regex");
Lexer::dateFormat = Variant::dateFormat = config.get ("dateformat");
TDB2::debug_mode = config.getBoolean ("debug");
Datetime::isoEnabled = config.getBoolean ("date.iso");
Datetime::standaloneDateEnabled = false;
Datetime::standaloneTimeEnabled = false;
Duration::standaloneSecondsEnabled = false;
ISO8601d::weekstart = config.get ("weekstart");
TDB2::debug_mode = config.getBoolean ("debug");
for (auto& rc : config)
{
@@ -643,8 +1062,7 @@ void Context::staticInitialization ()
rc.first.substr (rc.first.length () - 7, 7) == ".values")
{
std::string name = rc.first.substr (4, rc.first.length () - 7 - 4);
std::vector <std::string> values;
split (values, rc.second, ',');
auto values = split (rc.second, ',');
for (auto r = values.rbegin(); r != values.rend (); ++r)
Task::customOrder[name].push_back (*r);
@@ -670,9 +1088,7 @@ void Context::staticInitialization ()
Task::urgencyAgeMax = config.getReal ("urgency.age.max");
// Tag- and project-specific coefficients.
std::vector <std::string> all;
config.all (all);
for (auto& var : all)
for (auto& var : config.all ())
if (var.substr (0, 13) == "urgency.user." ||
var.substr (0, 12) == "urgency.uda.")
Task::coefficients[var] = config.getReal (var);
@@ -685,14 +1101,60 @@ void Context::createDefaultConfig ()
if (rc_file._data != "" && ! rc_file.exists ())
{
if (config.getBoolean ("confirmation") &&
!confirm (format (STRING_CONTEXT_CREATE_RC, home_dir, rc_file._data)))
throw std::string (STRING_CONTEXT_NEED_RC);
! confirm ( format ("A configuration file could not be found in {1}\n\nWould you like a sample {2} created, so Taskwarrior can proceed?", home_dir, rc_file._data)))
throw std::string ("Cannot proceed without rc file.");
config.createDefaultRC (rc_file, data_dir._original);
// Override data.location in the defaults.
auto loc = configurationDefaults.find ("data.location=~/.task");
// loc+0^ +14^ +21^
Datetime now;
std::stringstream contents;
contents << "# [Created by "
<< PACKAGE_STRING
<< ' '
<< now.toString ("m/d/Y H:N:S")
<< "]\n"
<< configurationDefaults.substr (0, loc + 14)
<< data_dir._original
<< "\n\n# Color theme (uncomment one to use)\n"
<< "#include " << TASK_RCDIR << "/light-16.theme\n"
<< "#include " << TASK_RCDIR << "/light-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-16.theme\n"
<< "#include " << TASK_RCDIR << "/dark-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-red-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-green-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-blue-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-violets-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-yellow-green.theme\n"
<< "#include " << TASK_RCDIR << "/dark-gray-256.theme\n"
<< "#include " << TASK_RCDIR << "/dark-gray-blue-256.theme\n"
<< "#include " << TASK_RCDIR << "/solarized-dark-256.theme\n"
<< "#include " << TASK_RCDIR << "/solarized-light-256.theme\n"
<< "#include " << TASK_RCDIR << "/no-color.theme\n"
<< '\n';
// Write out the new file.
if (! File::write (rc_file._data, contents.str ()))
throw format ("Could not write to '{1}'.", rc_file._data);
}
// Create data location, if necessary.
config.createDefaultData (data_dir);
Directory d (data_dir);
if (! d.exists ())
{
if (config.getBoolean ("exit.on.missing.db"))
throw std::string ("Error: rc.data.location does not exist - exiting according to rc.exit.on.missing.db setting.");
d.create ();
if (config.has ("hooks.location"))
d = Directory (config.get ("hooks.location"));
else
d += "hooks";
d.create ();
}
}
////////////////////////////////////////////////////////////////////////////////
@@ -729,6 +1191,20 @@ void Context::decomposeSortField (
}
}
////////////////////////////////////////////////////////////////////////////////
void Context::debugTiming (const std::string& details, const Timer& timer)
{
std::stringstream out;
out << "Timer "
<< details
<< ' '
<< std::setprecision (6)
<< std::fixed
<< timer.total_us () / 1.0e6
<< " sec";
debug (out.str ());
}
////////////////////////////////////////////////////////////////////////////////
// This capability is to answer the question of 'what did I just do to generate
// this output?'.
@@ -736,7 +1212,7 @@ void Context::updateXtermTitle ()
{
if (config.getBoolean ("xterm.title") && isatty (STDOUT_FILENO))
{
std::string command = cli2.getCommand ();
auto command = cli2.getCommand ();
std::string title;
for (auto a = cli2._args.begin (); a != cli2._args.end (); ++a)
@@ -747,7 +1223,7 @@ void Context::updateXtermTitle ()
title += a->attribute ("raw");
}
std::cout << "]0;task " << command << " " << title << "";
std::cout << "]0;task " << command << ' ' << title << "";
}
}
@@ -755,7 +1231,7 @@ void Context::updateXtermTitle ()
// This function allows a clean output if the command is a helper subcommand.
void Context::updateVerbosity ()
{
std::string command = cli2.getCommand ();
auto command = cli2.getCommand ();
if (command != "" &&
command[0] == '_')
{
@@ -831,3 +1307,5 @@ void Context::debug (const std::string& input)
}
////////////////////////////////////////////////////////////////////////////////
// vim ts=2:sw=2

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -29,7 +29,7 @@
#include <Command.h>
#include <Column.h>
#include <Config.h>
#include <Configuration.h>
#include <Task.h>
#include <TDB2.h>
#include <Hooks.h>
@@ -41,12 +41,15 @@
class Context
{
public:
Context (); // Default constructor
Context () = default; // Default constructor
~Context (); // Destructor
Context (const Context&);
Context& operator= (const Context&);
static Context& getContext ();
static void setContext (Context*);
int initialize (int, const char**); // all startup
int run ();
int dispatch (std::string&); // command handler dispatch
@@ -66,6 +69,7 @@ public:
void error (const std::string&); // Error message sink - non-maskable
void decomposeSortField (const std::string&, std::string&, bool&, bool&);
void debugTiming (const std::string&, const Timer&);
private:
void staticInitialization ();
@@ -75,43 +79,40 @@ private:
void loadAliases ();
void propagateDebug ();
static Context* context;
public:
CLI2 cli2;
std::string home_dir;
File rc_file;
Path data_dir;
Config config;
CLI2 cli2 {};
std::string home_dir {};
File rc_file {"~/.taskrc"};
Path data_dir {"~/.task"};
Configuration config {};
TDB2 tdb2 {};
Hooks hooks {};
bool determine_color_use {true};
bool use_color {true};
bool run_gc {true};
bool verbosity_legacy {false};
std::set <std::string> verbosity {};
std::vector <std::string> headers {};
std::vector <std::string> footnotes {};
std::vector <std::string> errors {};
std::vector <std::string> debugMessages {};
std::map <std::string, Command*> commands {};
std::map <std::string, Column*> columns {};
int terminal_width {0};
int terminal_height {0};
TDB2 tdb2;
Hooks hooks;
bool determine_color_use;
bool use_color;
bool run_gc;
bool verbosity_legacy;
std::set <std::string> verbosity;
std::vector <std::string> headers;
std::vector <std::string> footnotes;
std::vector <std::string> errors;
std::vector <std::string> debugMessages;
std::map <std::string, Command*> commands;
std::map <std::string, Column*> columns;
int terminal_width;
int terminal_height;
Timer timer_total;
Timer timer_init;
Timer timer_load;
Timer timer_gc;
Timer timer_filter;
Timer timer_commit;
Timer timer_sort;
Timer timer_render;
Timer timer_hooks;
Timer timer_total {};
long time_total_us {0};
long time_init_us {0};
long time_load_us {0};
long time_gc_us {0};
long time_filter_us {0};
long time_commit_us {0};
long time_sort_us {0};
long time_render_us {0};
long time_hooks_us {0};
};
#endif

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -30,13 +30,13 @@
#include <map>
#include <stdlib.h>
#include <Variant.h>
#include <Lexer.h>
#include <Context.h>
#include <Nibbler.h>
#include <ISO8601.h>
#include <text.h>
#include <i18n.h>
extern Context context;
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <format.h>
#include <util.h>
////////////////////////////////////////////////////////////////////////////////
// DOM Supported References:
@@ -44,11 +44,19 @@ extern Context context;
// Configuration:
// rc.<name>
//
// Taskwarrior:
// tw.syncneeded
// tw.program
// tw.args
// tw.width
// tw.height
// tw.version
//
// System:
// context.program
// context.args
// context.width
// context.height
// context.program // 2017-02-25 Deprecated in 2.6.0
// context.args // 2017-02-25 Deprecated in 2.6.0
// context.width // 2017-02-25 Deprecated in 2.6.0
// context.height // 2017-02-25 Deprecated in 2.6.0
// system.version
// system.os
//
@@ -58,16 +66,15 @@ bool getDOM (const std::string& name, Variant& value)
if (name == "")
return false;
int len = name.length ();
Nibbler n (name);
auto len = name.length ();
// rc. --> context.config
if (len > 3 &&
! name.compare (0, 3, "rc.", 3))
{
std::string key = name.substr (3);
auto c = context.config.find (key);
if (c != context.config.end ())
auto key = name.substr (3);
auto c = Context::getContext ().config.find (key);
if (c != Context::getContext ().config.end ())
{
value = Variant (c->second);
return true;
@@ -76,22 +83,83 @@ bool getDOM (const std::string& name, Variant& value)
return false;
}
// tw.*
if (len > 3 &&
! name.compare (0, 3, "tw.", 3))
{
if (name == "tw.syncneeded")
{
value = Variant (0);
for (const auto& line : Context::getContext ().tdb2.backlog.get_lines ())
{
if (line[0] == '{')
{
value = Variant (1);
break;
}
}
return true;
}
else if (name == "tw.program")
{
value = Variant (Context::getContext ().cli2.getBinary ());
return true;
}
else if (name == "tw.args")
{
std::string commandLine;
for (auto& arg : Context::getContext ().cli2._original_args)
{
if (commandLine != "")
commandLine += ' ';
commandLine += arg.attribute("raw");
}
value = Variant (commandLine);
return true;
}
else if (name == "tw.width")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_width
? Context::getContext ().terminal_width
: Context::getContext ().getWidth ()));
return true;
}
else if (name == "tw.height")
{
value = Variant (static_cast<int> (Context::getContext ().terminal_height
? Context::getContext ().terminal_height
: Context::getContext ().getHeight ()));
return true;
}
else if (name == "tw.version")
{
value = Variant (VERSION);
return true;
}
return false;
}
// context.*
if (len > 8 &&
! name.compare (0, 8, "context.", 8))
{
if (name == "context.program")
{
value = Variant (context.cli2.getBinary ());
value = Variant (Context::getContext ().cli2.getBinary ());
return true;
}
else if (name == "context.args")
{
std::string commandLine;
for (auto& arg : context.cli2._original_args)
for (auto& arg : Context::getContext ().cli2._original_args)
{
if (commandLine != "")
commandLine += " ";
commandLine += ' ';
commandLine += arg.attribute("raw");
}
@@ -101,23 +169,21 @@ bool getDOM (const std::string& name, Variant& value)
}
else if (name == "context.width")
{
value = Variant (static_cast<int> (context.terminal_width
? context.terminal_width
: context.getWidth ()));
value = Variant (static_cast<int> (Context::getContext ().terminal_width
? Context::getContext ().terminal_width
: Context::getContext ().getWidth ()));
return true;
}
else if (name == "context.height")
{
value = Variant (static_cast<int> (context.terminal_height
? context.terminal_height
: context.getHeight ()));
value = Variant (static_cast<int> (Context::getContext ().terminal_height
? Context::getContext ().terminal_height
: Context::getContext ().getHeight ()));
return true;
}
else
throw format (STRING_DOM_UNREC, name);
}
// TODO stats.<name>
return false;
}
// system. --> Implement locally.
if (len > 7 &&
@@ -133,33 +199,11 @@ bool getDOM (const std::string& name, Variant& value)
// OS type.
else if (name == "system.os")
{
#if defined (DARWIN)
value = Variant ("Darwin");
#elif defined (SOLARIS)
value = Variant ("Solaris");
#elif defined (CYGWIN)
value = Variant ("Cygwin");
#elif defined (HAIKU)
value = Variant ("Haiku");
#elif defined (OPENBSD)
value = Variant ("OpenBSD");
#elif defined (FREEBSD)
value = Variant ("FreeBSD");
#elif defined (NETBSD)
value = Variant ("NetBSD");
#elif defined (LINUX)
value = Variant ("Linux");
#elif defined (KFREEBSD)
value = Variant ("GNU/kFreeBSD");
#elif defined (GNUHURD)
value = Variant ("GNU/Hurd");
#else
value = Variant (STRING_DOM_UNKNOWN);
#endif
value = Variant (osName ());
return true;
}
else
throw format (STRING_DOM_UNREC, name);
return false;
}
// Empty string if nothing is found.
@@ -189,10 +233,11 @@ bool getDOM (const std::string& name, Variant& value)
// <date>.second
//
// Annotations (entry is a date):
// annotations.count
// annotations.<N>.entry
// annotations.<N>.description
//
// This code emphasizes speed, hence 'id' and 'urgecny' being evaluated first
// This code emphasizes speed, hence 'id' and 'urgency' being evaluated first
// as special cases.
bool getDOM (const std::string& name, const Task& task, Variant& value)
{
@@ -214,31 +259,29 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
}
// split name on '.'
std::vector <std::string> elements;
split (elements, name, '.');
auto elements = split (name, '.');
Task ref (task);
Nibbler n (elements[0]);
n.save ();
int id;
std::string uuid;
// If elements[0] is a UUID, load that task (if necessary), and clobber ref.
if (n.getPartialUUID (uuid) && n.depleted ())
Lexer lexer (elements[0]);
std::string token;
Lexer::Type type;
if (lexer.token (token, type))
{
if (uuid != ref.get ("uuid"))
context.tdb2.get (uuid, ref);
// Eat elements[0]/UUID.
elements.erase (elements.begin ());
}
else
{
// If elements[0] is a ID, load that task (if necessary), and clobber ref.
if (n.getInt (id) && n.depleted ())
if (type == Lexer::Type::uuid &&
token.length () == elements[0].length ())
{
if (id != ref.id)
context.tdb2.get (id, ref);
if (token != ref.get ("uuid"))
Context::getContext ().tdb2.get (token, ref);
// Eat elements[0]/UUID.
elements.erase (elements.begin ());
}
else if (type == Lexer::Type::number &&
token.find ('.') == std::string::npos)
{
auto id = strtol (token.c_str (), nullptr, 10);
if (id && id != ref.id)
Context::getContext ().tdb2.get (id, ref);
// Eat elements[0]/ID.
elements.erase (elements.begin ());
@@ -248,7 +291,7 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
auto size = elements.size ();
std::string canonical;
if ((size == 1 || size == 2) && context.cli2.canonicalize (canonical, "attribute", elements[0]))
if ((size == 1 || size == 2) && Context::getContext ().cli2.canonicalize (canonical, "attribute", elements[0]))
{
// Now that 'ref' is the contextual task, and any ID/UUID is chopped off the
// elements vector, DOM resolution is now simple.
@@ -264,7 +307,7 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
return true;
}
Column* column = context.columns[canonical];
Column* column = Context::getContext ().columns[canonical];
if (ref.data.size () && size == 1 && column)
{
@@ -286,12 +329,12 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
{
auto period = ref.get (canonical);
ISO8601p iso;
Duration iso;
std::string::size_type cursor = 0;
if (iso.parse (period, cursor))
value = Variant ((time_t) iso, Variant::type_duration);
value = Variant (iso.toTime_t (), Variant::type_duration);
else
value = Variant ((time_t) ISO8601p (ref.get (canonical)), Variant::type_duration);
value = Variant (Duration (ref.get (canonical)).toTime_t (), Variant::type_duration);
}
else if (column->type () == "numeric")
value = Variant (ref.get_float (canonical));
@@ -309,7 +352,7 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
if (ref.data.size () && size == 2 && column && column->type () == "date")
{
ISO8601d date (ref.get_date (canonical));
Datetime date (ref.get_date (canonical));
if (elements[1] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[1] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[1] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
@@ -322,12 +365,17 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
}
}
if (ref.data.size () && size == 2 && elements[0] == "annotations" && elements[1] == "count")
{
value = Variant (static_cast<int> (ref.getAnnotationCount ()));
return true;
}
if (ref.data.size () && size == 3 && elements[0] == "annotations")
{
std::map <std::string, std::string> annos;
ref.getAnnotations (annos);
auto annos = ref.getAnnotations ();
int a = strtol (elements[1].c_str (), NULL, 10);
int a = strtol (elements[1].c_str (), nullptr, 10);
int count = 0;
// Count off the 'a'th annotation.
@@ -353,10 +401,9 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
if (ref.data.size () && size == 4 && elements[0] == "annotations" && elements[2] == "entry")
{
std::map <std::string, std::string> annos;
ref.getAnnotations (annos);
auto annos = ref.getAnnotations ();
int a = strtol (elements[1].c_str (), NULL, 10);
int a = strtol (elements[1].c_str (), nullptr, 10);
int count = 0;
// Count off the 'a'th annotation.
@@ -373,7 +420,7 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
// <annotations>.<N>.entry.hour
// <annotations>.<N>.entry.minute
// <annotations>.<N>.entry.second
ISO8601d date (i.first.substr (11));
Datetime date (i.first.substr (11));
if (elements[3] == "year") { value = Variant (static_cast<int> (date.year ())); return true; }
else if (elements[3] == "month") { value = Variant (static_cast<int> (date.month ())); return true; }
else if (elements[3] == "day") { value = Variant (static_cast<int> (date.day ())); return true; }
@@ -392,3 +439,232 @@ bool getDOM (const std::string& name, const Task& task, Variant& value)
}
////////////////////////////////////////////////////////////////////////////////
// DOM Class
//
// References are paths into a tree structure. For example:
//
// 1.due.month.number
// 1.due.day.number
//
// Are represented internally as:
//
// 1
// +- due
// +- day
// | +- number
// +- month
// +- number
//
// The tree is augmented by other elements:
//
// 1
// +- due
// | +- day
// | | +- number
// | +- month
// | +- number
// +- system
// +- os
//
// Each node in the tree has a name ("due", "system", "day"), and each node may
// have a data source attached to it.
//
// The DOM class is independent of the project, in that it knows nothing about
// the internal data or program structure. It knows only that certain DOM path
// elements have handlers which will provide the data.
//
// The DOM class is therefore responsible for maintaining a tree of named nodes
// with associated proividers. When a reference value is requested, the DOM
// class will decompose the reference path, and navigate the tree to the lowest
// level provider, and call it.
//
// This makes the DOM class a reusible object.
////////////////////////////////////////////////////////////////////////////////
DOM::~DOM ()
{
delete _node;
}
////////////////////////////////////////////////////////////////////////////////
void DOM::addSource (
const std::string& reference,
bool (*provider)(const std::string&, Variant&))
{
if (_node == nullptr)
_node = new DOM::Node ();
_node->addSource (reference, provider);
}
////////////////////////////////////////////////////////////////////////////////
bool DOM::valid (const std::string& reference) const
{
return _node && _node->find (reference) != nullptr;
}
////////////////////////////////////////////////////////////////////////////////
Variant DOM::get (const std::string& reference) const
{
Variant v ("");
if (_node)
{
auto node = _node->find (reference);
if (node != nullptr &&
node->_provider != nullptr)
{
if (node->_provider (reference, v))
return v;
}
}
return v;
}
////////////////////////////////////////////////////////////////////////////////
int DOM::count () const
{
if (_node)
return _node->count ();
return 0;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> DOM::decomposeReference (const std::string& reference)
{
return split (reference, '.');
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::dump () const
{
if (_node)
return _node->dump ();
return "";
}
////////////////////////////////////////////////////////////////////////////////
DOM::Node::~Node ()
{
for (auto& branch : _branches)
delete branch;
}
////////////////////////////////////////////////////////////////////////////////
void DOM::Node::addSource (
const std::string& reference,
bool (*provider)(const std::string&, Variant&))
{
auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference))
{
auto found {false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
cursor = branch;
found = true;
break;
}
}
if (! found)
{
auto branch = new DOM::Node ();
branch->_name = element;
cursor->_branches.push_back (branch);
cursor = branch;
}
}
cursor->_provider = provider;
}
////////////////////////////////////////////////////////////////////////////////
// A valid reference is one that has a provider function.
bool DOM::Node::valid (const std::string& reference) const
{
return find (reference) != nullptr;
}
////////////////////////////////////////////////////////////////////////////////
const DOM::Node* DOM::Node::find (const std::string& reference) const
{
auto cursor = this;
for (const auto& element : DOM::decomposeReference (reference))
{
auto found {false};
for (auto& branch : cursor->_branches)
{
if (branch->_name == element)
{
cursor = branch;
found = true;
break;
}
}
if (! found)
break;
}
if (reference.length () && cursor != this)
return cursor;
return nullptr;
}
////////////////////////////////////////////////////////////////////////////////
int DOM::Node::count () const
{
// Recurse and count the branches.
int total {0};
for (auto& branch : _branches)
{
if (branch->_provider)
++total;
total += branch->count ();
}
return total;
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dumpNode (
const DOM::Node* node,
int depth) const
{
std::stringstream out;
// Indent.
out << std::string (depth * 2, ' ');
out << "\033[31m" << node->_name << "\033[0m";
if (node->_provider)
out << " 0x" << std::hex << (long long) (void*) node->_provider;
out << '\n';
// Recurse for branches.
for (auto& b : node->_branches)
out << dumpNode (b, depth + 1);
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////
std::string DOM::Node::dump () const
{
std::stringstream out;
out << "DOM::Node (" << count () << " nodes)\n"
<< dumpNode (this, 1);
return out.str ();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -31,7 +31,46 @@
#include <Variant.h>
#include <Task.h>
// 2017-04-22 Deprecated, use DOM::get.
bool getDOM (const std::string&, Variant&);
bool getDOM (const std::string&, const Task&, Variant&);
class DOM
{
public:
~DOM ();
void addSource (const std::string&, bool (*)(const std::string&, Variant&));
bool valid (const std::string&) const;
/*
// TODO Task object should register a generic provider.
Variant get (const Task&, const std::string&) const;
*/
Variant get (const std::string&) const;
int count () const;
static std::vector <std::string> decomposeReference (const std::string&);
std::string dump () const;
private:
class Node
{
public:
~Node ();
void addSource (const std::string&, bool (*)(const std::string&, Variant&));
bool valid (const std::string&) const;
const DOM::Node* find (const std::string&) const;
int count () const;
std::string dumpNode (const DOM::Node*, int) const;
std::string dump () const;
public:
std::string _name {"Unknown"};
bool (*_provider)(const std::string&, Variant&) {nullptr};
std::vector <DOM::Node*> _branches {};
};
private:
DOM::Node* _node {nullptr};
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,519 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Dates.h>
#include <algorithm>
#include <stdlib.h>
#include <time.h>
#include <text.h>
#include <ISO8601.h>
#include <Lexer.h>
#include <CLI2.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
static bool isMonth (const std::string& name, int& i)
{
i = ISO8601d::monthOfYear (name) - 1;
return i != -2 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
static bool isDay (const std::string& name, int& i)
{
i = ISO8601d::dayOfWeek (name);
return i != -1 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
static void easter (struct tm* t)
{
int Y = t->tm_year + 1900;
int a = Y % 19;
int b = Y / 100;
int c = Y % 100;
int d = b / 4;
int e = b % 4;
int f = (b + 8) / 25;
int g = (b - f + 1) / 3;
int h = (19 * a + b - d - g + 15) % 30;
int i = c / 4;
int k = c % 4;
int L = (32 + 2 * e + 2 * i - h - k) % 7;
int m = (a + 11 * h + 22 * L) / 451;
int month = (h + L - 7 * m + 114) / 31;
int day = ((h + L - 7 * m + 114) % 31) + 1;
t->tm_isdst = -1; // Requests that mktime determine summer time effect.
t->tm_mday = day;
t->tm_mon = month - 1;
t->tm_year = Y - 1900;
t->tm_isdst = -1;
t->tm_hour = t->tm_min = t->tm_sec = 0;
}
////////////////////////////////////////////////////////////////////////////////
static void midsommar (struct tm* t)
{
t->tm_mon = 5; // June.
t->tm_mday = 20; // Saturday after 20th.
t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
t->tm_isdst = -1; // Probably DST, but check.
time_t then = mktime (t); // Obtain the weekday of June 20th.
struct tm* mid = localtime (&then);
t->tm_mday += 6 - mid->tm_wday; // How many days after 20th.
}
////////////////////////////////////////////////////////////////////////////////
static void midsommarafton (struct tm* t)
{
t->tm_mon = 5; // June.
t->tm_mday = 19; // Saturday after 20th.
t->tm_hour = t->tm_min = t->tm_sec = 0; // Midnight.
t->tm_isdst = -1; // Probably DST, but check.
time_t then = mktime (t); // Obtain the weekday of June 19th.
struct tm* mid = localtime (&then);
t->tm_mday += 5 - mid->tm_wday; // How many days after 19th.
}
////////////////////////////////////////////////////////////////////////////////
// Note how these are all single words:
// <day>
// <month>
// Nth
// socy, eocy
// socq, eocq
// socm, eocm
// som, eom
// soq, eoq
// soy, eoy
// socw, eocw
// sow, eow
// soww, eoww
// sod, eod
// yesterday
// today
// now
// tomorrow
// later = midnight, Jan 18th, 2038.
// someday = midnight, Jan 18th, 2038.
// easter
// eastermonday
// ascension
// pentecost
// goodfriday
// midsommar = midnight, 1st Saturday after 20th June
// midsommarafton = midnight, 1st Friday after 19th June
//
bool namedDates (const std::string& name, Variant& value)
{
time_t now = time (NULL);
struct tm* t = localtime (&now);
int i;
int minimum = CLI2::minimumMatchLength;
if (minimum == 0)
minimum = 3;
// Dynamics.
if (closeEnough ("now", name, minimum))
{
value = Variant (now, Variant::type_date);
}
else if (closeEnough ("today", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("sod", name, minimum))
{
t->tm_mday++;
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("eod", name, minimum))
{
t->tm_mday++;
t->tm_hour = t->tm_min = 0;
t->tm_sec = -1;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("tomorrow", name, minimum))
{
t->tm_mday++;
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("yesterday", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t) - 86400, Variant::type_date);
}
else if (isDay (name, i))
{
if (t->tm_wday >= i)
t->tm_mday += i - t->tm_wday + 7;
else
t->tm_mday += i - t->tm_wday;
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (isMonth (name, i))
{
if (t->tm_mon >= i)
t->tm_year++;
t->tm_mon = i;
t->tm_mday = 1;
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("later", name, minimum) ||
closeEnough ("someday", name, std::max (minimum, 4)))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_year = 138;
t->tm_mon = 0;
t->tm_mday = 18;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("eoy", name, minimum))
{
t->tm_hour = t->tm_min = 0;
t->tm_sec = -1;
t->tm_mon = 0;
t->tm_mday = 1;
t->tm_year++;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("soy", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mon = 0;
t->tm_mday = 1;
t->tm_year++;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("eoq", name, minimum))
{
t->tm_hour = t->tm_min = 0;
t->tm_sec = -1;
t->tm_mon += 3 - (t->tm_mon % 3);
if (t->tm_mon > 11)
{
t->tm_mon -= 12;
++t->tm_year;
}
t->tm_mday = 1;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("soq", name, minimum))
{
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;
}
t->tm_mday = 1;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("socm", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mday = 1;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("som", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mon++;
if (t->tm_mon == 12)
{
t->tm_year++;
t->tm_mon = 0;
}
t->tm_mday = 1;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("eom", name, minimum) ||
closeEnough ("eocm", name, minimum))
{
t->tm_hour = 24;
t->tm_min = 0;
t->tm_sec = -1;
t->tm_mday = ISO8601d::daysInMonth (t->tm_mon + 1, t->tm_year + 1900);
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("socw", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
int extra = t->tm_wday * 86400;
t->tm_isdst = -1;
value = Variant (mktime (t) - extra, Variant::type_date);
}
else if (closeEnough ("eow", name, minimum) ||
closeEnough ("eocw", name, minimum))
{
t->tm_hour = t->tm_min = 0;
t->tm_sec = -1;
int extra = (7 - t->tm_wday) * 86400;
t->tm_isdst = -1;
value = Variant (mktime (t) + extra, Variant::type_date);
}
else if (closeEnough ("sow", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
int extra = (7 - t->tm_wday) * 86400;
t->tm_isdst = -1;
value = Variant (mktime (t) + extra, Variant::type_date);
}
else if (closeEnough ("soww", name, minimum))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
int days = (8 - t->tm_wday) % 7;
int extra = days * 86400;
t->tm_isdst = -1;
value = Variant (mktime (t) + extra, Variant::type_date);
}
else if (closeEnough ("eoww", name, minimum))
{
t->tm_hour = 24;
t->tm_min = 0;
t->tm_sec = -1;
int extra = (5 - t->tm_wday) * 86400;
if (extra < 0)
extra += 7 * 86400;
t->tm_isdst = -1;
value = Variant (mktime (t) + extra, Variant::type_date);
}
// Support "21st" to indicate the next date that is the 21st day.
// 1st
// 2nd
// 3rd
// 4th
else if ((
name.length () == 3 &&
Lexer::isDigit (name[0]) &&
((name[1] == 's' && name[2] == 't') ||
(name[1] == 'n' && name[2] == 'd') ||
(name[1] == 'r' && name[2] == 'd') ||
(name[1] == 't' && name[2] == 'h'))
)
||
(
name.length () == 4 &&
Lexer::isDigit (name[0]) &&
Lexer::isDigit (name[1]) &&
((name[2] == 's' && name[3] == 't') ||
(name[2] == 'n' && name[3] == 'd') ||
(name[2] == 'r' && name[3] == 'd') ||
(name[2] == 't' && name[3] == 'h'))
)
)
{
int number;
std::string ordinal;
if (Lexer::isDigit (name[1]))
{
number = strtol (name.substr (0, 2).c_str (), NULL, 10);
ordinal = Lexer::lowerCase (name.substr (2));
}
else
{
number = strtol (name.substr (0, 1).c_str (), NULL, 10);
ordinal = Lexer::lowerCase (name.substr (1));
}
// Sanity check.
if (number <= 31)
{
int remainder1 = number % 10;
int remainder2 = number % 100;
if ((remainder2 != 11 && remainder1 == 1 && ordinal == "st") ||
(remainder2 != 12 && remainder1 == 2 && ordinal == "nd") ||
(remainder2 != 13 && remainder1 == 3 && ordinal == "rd") ||
((remainder2 == 11 ||
remainder2 == 12 ||
remainder2 == 13 ||
remainder1 == 0 ||
remainder1 > 3) && ordinal == "th"))
{
int y = t->tm_year + 1900;
int m = t->tm_mon + 1;
int d = t->tm_mday;
// If it is this month.
if (d < number &&
number <= ISO8601d::daysInMonth (m, y))
{
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mon = m - 1;
t->tm_mday = number;
t->tm_year = y - 1900;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
else
{
if (++m > 12)
{
m = 1;
y++;
}
t->tm_hour = t->tm_min = t->tm_sec = 0;
t->tm_mon = m - 1;
t->tm_mday = number;
t->tm_year = y - 1900;
t->tm_isdst = -1;
value = Variant (mktime (t), Variant::type_date);
}
}
}
}
else if (closeEnough ("easter", name, minimum) ||
closeEnough ("eastermonday", name, minimum) ||
closeEnough ("ascension", name, minimum) ||
closeEnough ("pentecost", name, minimum) ||
closeEnough ("goodfriday", name, minimum))
{
Variant valueNow = Variant (mktime (t), Variant::type_date);
easter (t);
value = Variant (mktime (t), Variant::type_date);
// If the result is earlier this year, then recalc for next year.
if (value < valueNow)
{
t = localtime (&now);
t->tm_year++;
easter (t);
}
if (closeEnough ("goodfriday", name, minimum)) t->tm_mday -= 2;
// DO NOT REMOVE THIS USELESS-LOOKING LINE.
// It is here to capture an exact match for 'easter', to prevent 'easter'
// being a partial match for 'eastermonday'.
else if (closeEnough ("easter", name, minimum)) ;
else if (closeEnough ("eastermonday", name, minimum)) t->tm_mday += 1;
else if (closeEnough ("ascension", name, minimum)) t->tm_mday += 39;
else if (closeEnough ("pentecost", name, minimum)) t->tm_mday += 49;
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("midsommar", name, minimum))
{
Variant valueNow = Variant (mktime (t), Variant::type_date);
midsommar (t);
value = Variant (mktime (t), Variant::type_date);
// If the result is earlier this year, then recalc for next year.
if (value < valueNow)
{
t = localtime (&now);
t->tm_year++;
midsommar (t);
}
value = Variant (mktime (t), Variant::type_date);
}
else if (closeEnough ("midsommarafton", name, minimum))
{
Variant valueNow = Variant (mktime (t), Variant::type_date);
midsommarafton (t);
value = Variant (mktime (t), Variant::type_date);
// If the result is earlier this year, then recalc for next year.
if (value < valueNow)
{
t = localtime (&now);
t->tm_year++;
midsommarafton (t);
}
value = Variant (mktime (t), Variant::type_date);
}
else
return false;
value.source (name);
return true;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -31,10 +31,9 @@
#include <Context.h>
#include <Task.h>
#include <Color.h>
#include <text.h>
#include <i18n.h>
#include <shared.h>
#include <format.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -127,15 +126,15 @@ void Eval::evaluateInfixExpression (const std::string& e, Variant& v) const
// Parse for syntax checking and operator replacement.
if (_debug)
context.debug ("FILTER Infix " + dump (tokens));
Context::getContext ().debug ("FILTER Infix " + dump (tokens));
infixParse (tokens);
if (_debug)
context.debug ("FILTER Infix parsed " + dump (tokens));
Context::getContext ().debug ("FILTER Infix parsed " + dump (tokens));
// Convert infix --> postfix.
infixToPostfix (tokens);
if (_debug)
context.debug ("FILTER Postfix " + dump (tokens));
Context::getContext ().debug ("FILTER Postfix " + dump (tokens));
// Call the postfix evaluator.
evaluatePostfixStack (tokens, v);
@@ -153,7 +152,7 @@ void Eval::evaluatePostfixExpression (const std::string& e, Variant& v) const
tokens.push_back (std::pair <std::string, Lexer::Type> (token, type));
if (_debug)
context.debug ("FILTER Postfix " + dump (tokens));
Context::getContext ().debug ("FILTER Postfix " + dump (tokens));
// Call the postfix evaluator.
evaluatePostfixStack (tokens, v);
@@ -167,15 +166,15 @@ void Eval::compileExpression (
// Parse for syntax checking and operator replacement.
if (_debug)
context.debug ("FILTER Infix " + dump (_compiled));
Context::getContext ().debug ("FILTER Infix " + dump (_compiled));
infixParse (_compiled);
if (_debug)
context.debug ("FILTER Infix parsed " + dump (_compiled));
Context::getContext ().debug ("FILTER Infix parsed " + dump (_compiled));
// Convert infix --> postfix.
infixToPostfix (_compiled);
if (_debug)
context.debug ("FILTER Postfix " + dump (_compiled));
Context::getContext ().debug ("FILTER Postfix " + dump (_compiled));
}
////////////////////////////////////////////////////////////////////////////////
@@ -220,7 +219,7 @@ void Eval::evaluatePostfixStack (
Variant& result) const
{
if (tokens.size () == 0)
throw std::string (STRING_EVAL_NO_EXPRESSION);
throw std::string ("No expression to evaluate.");
// This is stack used by the postfix evaluator.
std::vector <Variant> values;
@@ -231,20 +230,20 @@ void Eval::evaluatePostfixStack (
token.first == "!")
{
if (values.size () < 1)
throw std::string (STRING_EVAL_NO_EVAL);
throw std::string ("The expression could not be evaluated.");
Variant right = values.back ();
values.pop_back ();
Variant result = ! right;
values.push_back (result);
if (_debug)
context.debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
}
else if (token.second == Lexer::Type::op &&
token.first == "_neg_")
{
if (values.size () < 1)
throw std::string (STRING_EVAL_NO_EVAL);
throw std::string ("The expression could not be evaluated.");
Variant right = values.back ();
values.pop_back ();
@@ -254,21 +253,21 @@ void Eval::evaluatePostfixStack (
values.push_back (result);
if (_debug)
context.debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
Context::getContext ().debug (format ("Eval {1} ↓'{2}' → ↑'{3}'", token.first, (std::string) right, (std::string) result));
}
else if (token.second == Lexer::Type::op &&
token.first == "_pos_")
{
// The _pos_ operator is a NOP.
if (_debug)
context.debug (format ("[{1}] eval op {2} NOP", values.size (), token.first));
Context::getContext ().debug (format ("[{1}] eval op {2} NOP", values.size (), token.first));
}
// Binary operators.
else if (token.second == Lexer::Type::op)
{
if (values.size () < 2)
throw std::string (STRING_EVAL_NO_EVAL);
throw std::string ("The expression could not be evaluated.");
Variant right = values.back ();
values.pop_back ();
@@ -302,12 +301,12 @@ void Eval::evaluatePostfixStack (
else if (token.first == "_hastag_") result = left.operator_hastag (right, contextTask);
else if (token.first == "_notag_") result = left.operator_notag (right, contextTask);
else
throw format (STRING_EVAL_UNSUPPORTED, token.first);
throw format ("Unsupported operator '{1}'.", token.first);
values.push_back (result);
if (_debug)
context.debug (format ("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string) left, token.first, (std::string) right, (std::string) result));
Context::getContext ().debug (format ("Eval ↓'{1}' {2} ↓'{3}' → ↑'{4}'", (std::string) left, token.first, (std::string) right, (std::string) result));
}
// Literals and identifiers.
@@ -321,18 +320,18 @@ void Eval::evaluatePostfixStack (
{
v.cast (Variant::type_integer);
if (_debug)
context.debug (format ("Eval literal number ↑'{1}'", (std::string) v));
Context::getContext ().debug (format ("Eval literal number ↑'{1}'", (std::string) v));
}
else
{
v.cast (Variant::type_real);
if (_debug)
context.debug (format ("Eval literal decimal ↑'{1}'", (std::string) v));
Context::getContext ().debug (format ("Eval literal decimal ↑'{1}'", (std::string) v));
}
break;
case Lexer::Type::op:
throw std::string (STRING_EVAL_OP_EXPECTED);
throw std::string ("Operator expected.");
break;
case Lexer::Type::dom:
@@ -344,18 +343,18 @@ void Eval::evaluatePostfixStack (
if ((*source) (token.first, v))
{
if (_debug)
context.debug (format ("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string) v));
Context::getContext ().debug (format ("Eval identifier source '{1}' → ↑'{2}'", token.first, (std::string) v));
found = true;
break;
}
}
// An identifier that fails lookup is a string.
if (!found)
if (! found)
{
v.cast (Variant::type_string);
if (_debug)
context.debug (format ("Eval identifier source failed '{1}'", token.first));
Context::getContext ().debug (format ("Eval identifier source failed '{1}'", token.first));
}
}
break;
@@ -363,20 +362,20 @@ void Eval::evaluatePostfixStack (
case Lexer::Type::date:
v.cast (Variant::type_date);
if (_debug)
context.debug (format ("Eval literal date ↑'{1}'", (std::string) v));
Context::getContext ().debug (format ("Eval literal date ↑'{1}'", (std::string) v));
break;
case Lexer::Type::duration:
v.cast (Variant::type_duration);
if (_debug)
context.debug (format ("Eval literal duration ↑'{1}'", (std::string) v));
Context::getContext ().debug (format ("Eval literal duration ↑'{1}'", (std::string) v));
break;
// Nothing to do.
case Lexer::Type::string:
default:
if (_debug)
context.debug (format ("Eval literal string ↑'{1}'", (std::string) v));
Context::getContext ().debug (format ("Eval literal string ↑'{1}'", (std::string) v));
break;
}
@@ -387,7 +386,7 @@ void Eval::evaluatePostfixStack (
// If there is more than one variant left on the stack, then the original
// expression was not valid.
if (values.size () != 1)
throw std::string (STRING_EVAL_NO_EVAL);
throw std::string ("The value is not an expression.");
result = values[0];
}
@@ -795,7 +794,7 @@ void Eval::infixToPostfix (
{
if (op_stack.back ().first == "(" ||
op_stack.back ().first == ")")
throw std::string (STRING_PAREN_MISMATCH);
throw std::string ("Mismatched parentheses in expression");
postfix.push_back (op_stack.back ());
op_stack.pop_back ();

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,972 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <FS.h>
#include <fstream>
#include <glob.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <pwd.h>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <text.h>
#include <util.h>
#include <i18n.h>
#if defined SOLARIS || defined NETBSD || defined FREEBSD
#include <limits.h>
#endif
#if defined __APPLE__
#include <sys/syslimits.h>
#endif
// Fixes build with musl libc.
#ifndef GLOB_TILDE
#define GLOB_TILDE 0
#endif
#ifndef GLOB_BRACE
#define GLOB_BRACE 0
#endif
////////////////////////////////////////////////////////////////////////////////
Path::Path ()
{
}
////////////////////////////////////////////////////////////////////////////////
Path::Path (const Path& other)
{
if (this != &other)
{
_original = other._original;
_data = other._data;
}
}
////////////////////////////////////////////////////////////////////////////////
Path::Path (const std::string& in)
{
_original = in;
_data = expand (in);
}
////////////////////////////////////////////////////////////////////////////////
Path& Path::operator= (const Path& other)
{
if (this != &other)
{
this->_original = other._original;
this->_data = other._data;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::operator== (const Path& other)
{
return _data == other._data;
}
////////////////////////////////////////////////////////////////////////////////
Path& Path::operator+= (const std::string& dir)
{
_data += "/" + dir;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
Path::operator std::string () const
{
return _data;
}
////////////////////////////////////////////////////////////////////////////////
std::string Path::name () const
{
if (_data.length ())
{
auto slash = _data.rfind ('/');
if (slash != std::string::npos)
return _data.substr (slash + 1, std::string::npos);
}
return _data;
}
////////////////////////////////////////////////////////////////////////////////
std::string Path::parent () const
{
if (_data.length ())
{
auto slash = _data.rfind ('/');
if (slash != std::string::npos)
return _data.substr (0, slash);
}
return "";
}
////////////////////////////////////////////////////////////////////////////////
std::string Path::extension () const
{
if (_data.length ())
{
auto dot = _data.rfind ('.');
if (dot != std::string::npos)
return _data.substr (dot + 1, std::string::npos);
}
return "";
}
////////////////////////////////////////////////////////////////////////////////
bool Path::exists () const
{
return access (_data.c_str (), F_OK) ? false : true;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::is_directory () const
{
struct stat s {};
if (! stat (_data.c_str (), &s) &&
S_ISDIR (s.st_mode))
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::is_absolute () const
{
if (_data.length () && _data[0] == '/')
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::is_link () const
{
struct stat s {};
if (! lstat (_data.c_str (), &s) &&
S_ISLNK (s.st_mode))
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::readable () const
{
return access (_data.c_str (), R_OK) ? false : true;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::writable () const
{
return access (_data.c_str (), W_OK) ? false : true;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::executable () const
{
return access (_data.c_str (), X_OK) ? false : true;
}
////////////////////////////////////////////////////////////////////////////////
bool Path::rename (const std::string& new_name)
{
auto expanded = expand (new_name);
if (_data != expanded)
{
if (std::rename (_data.c_str (), expanded.c_str ()) == 0)
{
_data = expanded;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// ~ --> /home/user
// ~foo/x --> /home/foo/s
// ~/x --> /home/foo/x
// ./x --> $PWD/x
// x --> $PWD/x
std::string Path::expand (const std::string& in)
{
std::string copy = in;
auto tilde = copy.find ("~");
std::string::size_type slash;
if (tilde != std::string::npos)
{
const char *home = getenv("HOME");
if (home == nullptr)
{
struct passwd* pw = getpwuid (getuid ());
home = pw->pw_dir;
}
// Convert: ~ --> /home/user
if (copy.length () == 1)
copy = home;
// Convert: ~/x --> /home/user/x
else if (copy.length () > tilde + 1 &&
copy[tilde + 1] == '/')
{
copy.replace (tilde, 1, home);
}
// Convert: ~foo/x --> /home/foo/x
else if ((slash = copy.find ("/", tilde)) != std::string::npos)
{
std::string name = copy.substr (tilde + 1, slash - tilde - 1);
struct passwd* pw = getpwnam (name.c_str ());
if (pw)
copy.replace (tilde, slash - tilde, pw->pw_dir);
}
}
// Relative paths
else if (in.length () > 2 &&
in.substr (0, 2) == "./")
{
copy = Directory::cwd () + in.substr (1);
}
else if (in.length () > 1 &&
in[0] != '.' &&
in[0] != '/')
{
copy = Directory::cwd () + "/" + in;
}
return copy;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Path::glob (const std::string& pattern)
{
std::vector <std::string> results;
glob_t g;
#ifdef SOLARIS
if (!::glob (pattern.c_str (), GLOB_ERR, nullptr, &g))
#else
if (!::glob (pattern.c_str (), GLOB_ERR | GLOB_BRACE | GLOB_TILDE, nullptr, &g))
#endif
for (int i = 0; i < (int) g.gl_pathc; ++i)
results.push_back (g.gl_pathv[i]);
globfree (&g);
return results;
}
////////////////////////////////////////////////////////////////////////////////
File::File ()
: Path::Path ()
, _fh (nullptr)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const Path& other)
: Path::Path (other)
, _fh (nullptr)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const File& other)
: Path::Path (other)
, _fh (nullptr)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::File (const std::string& in)
: Path::Path (in)
, _fh (nullptr)
, _h (-1)
, _locked (false)
{
}
////////////////////////////////////////////////////////////////////////////////
File::~File ()
{
if (_fh)
close ();
}
////////////////////////////////////////////////////////////////////////////////
File& File::operator= (const File& other)
{
if (this != &other)
Path::operator= (other);
_locked = false;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool File::create (int mode /* = 0640 */)
{
if (open ())
{
fchmod (_h, mode);
close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::remove () const
{
return unlink (_data.c_str ()) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
std::string File::removeBOM (const std::string& input)
{
if (input[0] && input[0] == '\xEF' &&
input[1] && input[1] == '\xBB' &&
input[2] && input[2] == '\xBF')
return input.substr (3);
return input;
}
////////////////////////////////////////////////////////////////////////////////
bool File::open ()
{
if (_data != "")
{
if (! _fh)
{
bool already_exists = exists ();
if (already_exists)
if (!readable () || !writable ())
throw std::string (format (STRING_FILE_PERMS, _data));
_fh = fopen (_data.c_str (), (already_exists ? "r+" : "w+"));
if (_fh)
{
_h = fileno (_fh);
_locked = false;
return true;
}
}
else
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
void File::close ()
{
if (_fh)
{
if (_locked)
unlock ();
fclose (_fh);
_fh = nullptr;
_h = -1;
_locked = false;
}
}
////////////////////////////////////////////////////////////////////////////////
bool File::lock ()
{
_locked = false;
if (_fh && _h != -1)
{
// l_type l_whence l_start l_len l_pid
struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 };
fl.l_pid = getpid ();
if (fcntl (_h, F_SETLKW, &fl) == 0)
_locked = true;
}
return _locked;
}
////////////////////////////////////////////////////////////////////////////////
void File::unlock ()
{
if (_locked)
{
// l_type l_whence l_start l_len l_pid
struct flock fl = {F_UNLCK, SEEK_SET, 0, 0, 0 };
fl.l_pid = getpid ();
fcntl (_h, F_SETLK, &fl);
_locked = false;
}
}
////////////////////////////////////////////////////////////////////////////////
// Opens if necessary.
void File::read (std::string& contents)
{
contents = "";
contents.reserve (size ());
std::ifstream in (_data.c_str ());
if (in.good ())
{
bool first = true;
std::string line;
line.reserve (512 * 1024);
while (getline (in, line))
{
// Detect forbidden BOM on first line.
if (first)
{
line = File::removeBOM (line);
first = false;
}
contents += line + "\n";
}
in.close ();
}
}
////////////////////////////////////////////////////////////////////////////////
// Opens if necessary.
void File::read (std::vector <std::string>& contents)
{
contents.clear ();
std::ifstream in (_data.c_str ());
if (in.good ())
{
bool first = true;
std::string line;
line.reserve (512 * 1024);
while (getline (in, line))
{
// Detect forbidden BOM on first line.
if (first)
{
line = File::removeBOM (line);
first = false;
}
contents.push_back (line);
}
in.close ();
}
}
////////////////////////////////////////////////////////////////////////////////
// Opens if necessary.
void File::append (const std::string& line)
{
if (!_fh)
open ();
if (_fh)
{
fseek (_fh, 0, SEEK_END);
fputs (line.c_str (), _fh);
}
}
////////////////////////////////////////////////////////////////////////////////
// Opens if necessary.
void File::append (const std::vector <std::string>& lines)
{
if (!_fh)
open ();
if (_fh)
{
fseek (_fh, 0, SEEK_END);
for (auto& line : lines)
fputs (line.c_str (), _fh);
}
}
////////////////////////////////////////////////////////////////////////////////
void File::write_raw (const std::string& line)
{
if (!_fh)
open ();
if (_fh)
fputs (line.c_str (), _fh);
}
////////////////////////////////////////////////////////////////////////////////
void File::truncate ()
{
if (!_fh)
open ();
if (_fh)
(void) ftruncate (_h, 0);
}
////////////////////////////////////////////////////////////////////////////////
// S_IFMT 0170000 type of file
// S_IFIFO 0010000 named pipe (fifo)
// S_IFCHR 0020000 character special
// S_IFDIR 0040000 directory
// S_IFBLK 0060000 block special
// S_IFREG 0100000 regular
// S_IFLNK 0120000 symbolic link
// S_IFSOCK 0140000 socket
// S_IFWHT 0160000 whiteout
// S_ISUID 0004000 set user id on execution
// S_ISGID 0002000 set group id on execution
// S_ISVTX 0001000 save swapped text even after use
// S_IRUSR 0000400 read permission, owner
// S_IWUSR 0000200 write permission, owner
// S_IXUSR 0000100 execute/search permission, owner
mode_t File::mode ()
{
struct stat s;
if (!stat (_data.c_str (), &s))
return s.st_mode;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
size_t File::size () const
{
struct stat s;
if (!stat (_data.c_str (), &s))
return s.st_size;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
time_t File::mtime () const
{
struct stat s;
if (!stat (_data.c_str (), &s))
return s.st_mtime;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
time_t File::ctime () const
{
struct stat s;
if (!stat (_data.c_str (), &s))
return s.st_ctime;
return 0;
}
////////////////////////////////////////////////////////////////////////////////
time_t File::btime () const
{
struct stat s;
if (!stat (_data.c_str (), &s))
#ifdef HAVE_ST_BIRTHTIME
return s.st_birthtime;
#else
return s.st_ctime;
#endif
return 0;
}
////////////////////////////////////////////////////////////////////////////////
bool File::create (const std::string& name, int mode /* = 0640 */)
{
std::string full_name = expand (name);
std::ofstream out (full_name.c_str ());
if (out.good ())
{
out.close ();
chmod (full_name.c_str (), mode);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::read (const std::string& name, std::string& contents)
{
contents = "";
std::ifstream in (name.c_str ());
if (in.good ())
{
bool first = true;
std::string line;
line.reserve (1024);
while (getline (in, line))
{
// Detect forbidden BOM on first line.
if (first)
{
line = File::removeBOM (line);
first = false;
}
contents += line + "\n";
}
in.close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::read (const std::string& name, std::vector <std::string>& contents)
{
contents.clear ();
std::ifstream in (name.c_str ());
if (in.good ())
{
bool first = true;
std::string line;
line.reserve (1024);
while (getline (in, line))
{
// Detect forbidden BOM on first line.
if (first)
{
line = File::removeBOM (line);
first = false;
}
contents.push_back (line);
}
in.close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::write (const std::string& name, const std::string& contents)
{
std::ofstream out (expand (name).c_str (),
std::ios_base::out | std::ios_base::trunc);
if (out.good ())
{
out << contents;
out.close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::write (
const std::string& name,
const std::vector <std::string>& lines,
bool addNewlines /* = true */)
{
std::ofstream out (expand (name).c_str (),
std::ios_base::out | std::ios_base::trunc);
if (out.good ())
{
for (auto& line : lines)
{
out << line;
if (addNewlines)
out << "\n";
}
out.close ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::remove (const std::string& name)
{
return unlink (expand (name).c_str ()) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::copy (const std::string& from, const std::string& to)
{
// 'from' must exist.
if (! access (from.c_str (), F_OK))
{
std::ifstream src (from, std::ios::binary);
std::ofstream dst (to, std::ios::binary);
dst << src.rdbuf ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool File::move (const std::string& from, const std::string& to)
{
auto expanded = expand (to);
if (from != expanded)
if (std::rename (from.c_str (), to.c_str ()) == 0)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
Directory::Directory ()
{
}
////////////////////////////////////////////////////////////////////////////////
Directory::Directory (const Directory& other)
: File::File (other)
{
}
////////////////////////////////////////////////////////////////////////////////
Directory::Directory (const File& other)
: File::File (other)
{
}
////////////////////////////////////////////////////////////////////////////////
Directory::Directory (const Path& other)
: File::File (other)
{
}
////////////////////////////////////////////////////////////////////////////////
Directory::Directory (const std::string& in)
: File::File (in)
{
}
////////////////////////////////////////////////////////////////////////////////
Directory& Directory::operator= (const Directory& other)
{
if (this != &other)
File::operator= (other);
return *this;
}
////////////////////////////////////////////////////////////////////////////////
bool Directory::create (int mode /* = 0755 */)
{
return mkdir (_data.c_str (), mode) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool Directory::remove () const
{
return remove_directory (_data);
}
////////////////////////////////////////////////////////////////////////////////
bool Directory::remove_directory (const std::string& dir) const
{
DIR* dp = opendir (dir.c_str ());
if (dp != nullptr)
{
struct dirent* de;
while ((de = readdir (dp)) != nullptr)
{
if (!strcmp (de->d_name, ".") ||
!strcmp (de->d_name, ".."))
continue;
#if defined (SOLARIS) || defined (HAIKU)
struct stat s;
lstat ((dir + "/" + de->d_name).c_str (), &s);
if (S_ISDIR (s.st_mode))
remove_directory (dir + "/" + de->d_name);
else
unlink ((dir + "/" + de->d_name).c_str ());
#else
if (de->d_type == DT_UNKNOWN)
{
struct stat s;
lstat ((dir + "/" + de->d_name).c_str (), &s);
if (S_ISDIR (s.st_mode))
de->d_type = DT_DIR;
}
if (de->d_type == DT_DIR)
remove_directory (dir + "/" + de->d_name);
else
unlink ((dir + "/" + de->d_name).c_str ());
#endif
}
closedir (dp);
}
return rmdir (dir.c_str ()) ? false : true;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Directory::list ()
{
std::vector <std::string> files;
list (_data, files, false);
return files;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Directory::listRecursive ()
{
std::vector <std::string> files;
list (_data, files, true);
return files;
}
////////////////////////////////////////////////////////////////////////////////
std::string Directory::cwd ()
{
#ifdef HAVE_GET_CURRENT_DIR_NAME
char *buf = get_current_dir_name ();
if (buf == nullptr)
throw std::bad_alloc ();
std::string result (buf);
free (buf);
return result;
#else
char buf[PATH_MAX];
getcwd (buf, PATH_MAX - 1);
return std::string (buf);
#endif
}
////////////////////////////////////////////////////////////////////////////////
bool Directory::up ()
{
if (_data == "/")
return false;
auto slash = _data.rfind ('/');
if (slash == 0)
{
_data = "/"; // Root dir should retain the slash.
return true;
}
else if (slash != std::string::npos)
{
_data = _data.substr (0, slash);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Directory::cd () const
{
return chdir (_data.c_str ()) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void Directory::list (
const std::string& base,
std::vector <std::string>& results,
bool recursive)
{
DIR* dp = opendir (base.c_str ());
if (dp != nullptr)
{
struct dirent* de;
while ((de = readdir (dp)) != nullptr)
{
if (!strcmp (de->d_name, ".") ||
!strcmp (de->d_name, ".."))
continue;
#if defined (SOLARIS) || defined (HAIKU)
struct stat s;
stat ((base + "/" + de->d_name).c_str (), &s);
if (recursive && S_ISDIR (s.st_mode))
list (base + "/" + de->d_name, results, recursive);
else
results.push_back (base + "/" + de->d_name);
#else
if (recursive && de->d_type == DT_UNKNOWN)
{
struct stat s;
lstat ((base + "/" + de->d_name).c_str (), &s);
if (S_ISDIR (s.st_mode))
de->d_type = DT_DIR;
}
if (recursive && de->d_type == DT_DIR)
list (base + "/" + de->d_name, results, recursive);
else
results.push_back (base + "/" + de->d_name);
#endif
}
closedir (dp);
}
}
////////////////////////////////////////////////////////////////////////////////

145
src/FS.h
View File

@@ -1,145 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_FS
#define INCLUDED_FS
#include <stdio.h>
#include <string>
#include <vector>
#include <sys/stat.h>
class Path
{
public:
Path ();
explicit Path (const Path&);
Path (const std::string&);
Path& operator= (const Path&);
bool operator== (const Path&);
Path& operator+= (const std::string&);
operator std::string () const;
std::string name () const;
std::string parent () const;
std::string extension () const;
bool exists () const;
bool is_directory () const;
bool is_absolute () const;
bool is_link () const;
bool readable () const;
bool writable () const;
bool executable () const;
bool rename (const std::string&);
// Statics
static std::string expand (const std::string&);
static std::vector<std::string> glob (const std::string&);
public:
std::string _original;
std::string _data;
};
class File : public Path
{
public:
File ();
explicit File (const Path&);
explicit File (const File&);
File (const std::string&);
virtual ~File ();
File& operator= (const File&);
virtual bool create (int mode = 0640);
virtual bool remove () const;
bool open ();
void close ();
bool lock ();
void unlock ();
void read (std::string&);
void read (std::vector <std::string>&);
void append (const std::string&);
void append (const std::vector <std::string>&);
void write_raw (const std::string&);
void truncate ();
virtual mode_t mode ();
virtual size_t size () const;
virtual time_t mtime () const;
virtual time_t ctime () const;
virtual time_t btime () const;
static bool create (const std::string&, int mode = 0640);
static bool read (const std::string&, std::string&);
static bool read (const std::string&, std::vector <std::string>&);
static bool write (const std::string&, const std::string&);
static bool write (const std::string&, const std::vector <std::string>&, bool addNewlines = true);
static bool remove (const std::string&);
static bool copy (const std::string&, const std::string&);
static bool move (const std::string&, const std::string&);
static std::string removeBOM (const std::string&);
private:
FILE* _fh;
int _h;
bool _locked;
};
class Directory : public File
{
public:
Directory ();
explicit Directory (const Directory&);
explicit Directory (const File&);
explicit Directory (const Path&);
Directory (const std::string&);
Directory& operator= (const Directory&);
virtual bool create (int mode = 0755);
virtual bool remove () const;
std::vector <std::string> list ();
std::vector <std::string> listRecursive ();
static std::string cwd ();
bool up ();
bool cd () const;
private:
void list (const std::string&, std::vector <std::string>&, bool);
bool remove_directory (const std::string&) const;
};
#endif

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,15 +28,12 @@
#include <Filter.h>
#include <algorithm>
#include <Context.h>
#include <Timer.h>
#include <DOM.h>
#include <Eval.h>
#include <Variant.h>
#include <Dates.h>
#include <i18n.h>
#include <text.h>
#include <util.h>
extern Context context;
#include <format.h>
#include <shared.h>
////////////////////////////////////////////////////////////////////////////////
// Const iterator that can be derefenced into a Task by domSource.
@@ -59,13 +56,13 @@ bool domSource (const std::string& identifier, Variant& value)
// Take an input set of tasks and filter into a subset.
void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output)
{
context.timer_filter.start ();
Timer timer;
_startCount = (int) input.size ();
context.cli2.prepareFilter ();
Context::getContext ().cli2.prepareFilter ();
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : context.cli2._args)
for (auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
precompiled.push_back (std::pair <std::string, Lexer::Type> (a.getToken (), a._lextype));
@@ -73,11 +70,10 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
{
Eval eval;
eval.addSource (domSource);
eval.addSource (namedDates);
// Debug output from Eval during compilation is useful. During evaluation
// it is mostly noise.
eval.debug (context.config.getInteger ("debug.parser") >= 3 ? true : false);
eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false);
eval.compileExpression (precompiled);
for (auto& task : input)
@@ -97,20 +93,19 @@ void Filter::subset (const std::vector <Task>& input, std::vector <Task>& output
output = input;
_endCount = (int) output.size ();
context.debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
context.timer_filter.stop ();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [list subset]", _startCount, _endCount));
Context::getContext ().time_filter_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
// Take the set of all tasks and filter into a subset.
void Filter::subset (std::vector <Task>& output)
{
context.timer_filter.start ();
context.cli2.prepareFilter ();
Timer timer;
Context::getContext ().cli2.prepareFilter ();
std::vector <std::pair <std::string, Lexer::Type>> precompiled;
for (auto& a : context.cli2._args)
for (auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
precompiled.push_back (std::pair <std::string, Lexer::Type> (a.getToken (), a._lextype));
@@ -119,18 +114,17 @@ void Filter::subset (std::vector <Task>& output)
if (precompiled.size ())
{
context.timer_filter.stop ();
auto pending = context.tdb2.pending.get_tasks ();
context.timer_filter.start ();
Timer timer_pending;
auto pending = Context::getContext ().tdb2.pending.get_tasks ();
Context::getContext ().time_filter_us -= timer_pending.total_us ();
_startCount = (int) pending.size ();
Eval eval;
eval.addSource (domSource);
eval.addSource (namedDates);
// Debug output from Eval during compilation is useful. During evaluation
// it is mostly noise.
eval.debug (context.config.getInteger ("debug.parser") >= 3 ? true : false);
eval.debug (Context::getContext ().config.getInteger ("debug.parser") >= 3 ? true : false);
eval.compileExpression (precompiled);
output.clear ();
@@ -148,9 +142,9 @@ void Filter::subset (std::vector <Task>& output)
shortcut = pendingOnly ();
if (! shortcut)
{
context.timer_filter.stop ();
auto completed = context.tdb2.completed.get_tasks ();
context.timer_filter.start ();
Timer timer_completed;
auto completed = Context::getContext ().tdb2.completed.get_tasks ();
Context::getContext ().time_filter_us -= timer_completed.total_us ();
_startCount += (int) completed.size ();
for (auto& task : completed)
@@ -170,26 +164,25 @@ void Filter::subset (std::vector <Task>& output)
else
{
safety ();
context.timer_filter.stop ();
for (auto& task : context.tdb2.pending.get_tasks ())
Timer pending_completed;
for (auto& task : Context::getContext ().tdb2.pending.get_tasks ())
output.push_back (task);
for (auto& task : context.tdb2.completed.get_tasks ())
for (auto& task : Context::getContext ().tdb2.completed.get_tasks ())
output.push_back (task);
context.timer_filter.start ();
Context::getContext ().time_filter_us -= pending_completed.total_us ();
}
_endCount = (int) output.size ();
context.debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
context.timer_filter.stop ();
Context::getContext ().debug (format ("Filtered {1} tasks --> {2} tasks [{3}]", _startCount, _endCount, (shortcut ? "pending only" : "all tasks")));
Context::getContext ().time_filter_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
bool Filter::hasFilter () const
{
for (const auto& a : context.cli2._args)
for (const auto& a : Context::getContext ().cli2._args)
if (a.hasTag ("FILTER"))
return true;
@@ -203,7 +196,7 @@ bool Filter::hasFilter () const
bool Filter::pendingOnly () const
{
// When GC is off, there are no shortcuts.
if (! context.config.getBoolean ("gc"))
if (! Context::getContext ().config.getBoolean ("gc"))
return false;
// To skip loading completed.data, there should be:
@@ -216,13 +209,13 @@ bool Filter::pendingOnly () const
int countPending = 0;
int countWaiting = 0;
int countRecurring = 0;
int countId = (int) context.cli2._id_ranges.size ();
int countUUID = (int) context.cli2._uuid_list.size ();
int countId = (int) Context::getContext ().cli2._id_ranges.size ();
int countUUID = (int) Context::getContext ().cli2._uuid_list.size ();
int countOr = 0;
int countXor = 0;
int countNot = 0;
for (const auto& a : context.cli2._args)
for (const auto& a : Context::getContext ().cli2._args)
{
if (a.hasTag ("FILTER"))
{
@@ -268,7 +261,7 @@ void Filter::safety () const
{
bool readonly = true;
bool filter = false;
for (const auto& a : context.cli2._args)
for (const auto& a : Context::getContext ().cli2._args)
{
if (a.hasTag ("CMD") &&
! a.hasTag ("READONLY"))
@@ -281,16 +274,16 @@ void Filter::safety () const
if (! readonly &&
! filter)
{
if (! context.config.getBoolean ("allow.empty.filter"))
throw std::string (STRING_TASK_SAFETY_ALLOW);
if (! Context::getContext ().config.getBoolean ("allow.empty.filter"))
throw std::string ("You did not specify a filter, and with the 'allow.empty.filter' value, no action is taken.");
// If user is willing to be asked, this can be avoided.
if (context.config.getBoolean ("confirmation") &&
confirm (STRING_TASK_SAFETY_VALVE))
if (Context::getContext ().config.getBoolean ("confirmation") &&
confirm ("This command has no filter, and will modify all (including completed and deleted) tasks. Are you sure?"))
return;
// Sound the alarm.
throw std::string (STRING_TASK_SAFETY_FAIL);
throw std::string ("Command prevented from running.");
}
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -39,22 +39,43 @@
#include <Context.h>
#include <Variant.h>
#include <DOM.h>
#include <Lexer.h>
#include <JSON.h>
#include <Timer.h>
#include <text.h>
#include <FS.h>
#include <format.h>
#include <shared.h>
#include <util.h>
#include <i18n.h>
extern Context context;
#define STRING_HOOK_ERROR_OBJECT "Hook Error: JSON Object '{...}' expected from hook script: {1}"
#define STRING_HOOK_ERROR_NODESC "Hook Error: JSON Object missing 'description' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_NOUUID "Hook Error: JSON Object missing 'uuid' attribute from hook script: {1}"
#define STRING_HOOK_ERROR_SYNTAX "Hook Error: JSON syntax error in: {1}"
#define STRING_HOOK_ERROR_JSON "Hook Error: JSON "
#define STRING_HOOK_ERROR_NOPARSE "Hook Error: JSON failed to parse: "
#define STRING_HOOK_ERROR_BAD_NUM "Hook Error: Expected {1} JSON task(s), found {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_SAME1 "Hook Error: JSON must be for the same task: {1}, in hook script: {2}"
#define STRING_HOOK_ERROR_SAME2 "Hook Error: JSON must be for the same task: {1} != {2}, in hook script: {3}"
#define STRING_HOOK_ERROR_NOFEEDBACK "Hook Error: Expected feedback from failing hook script: {1}"
////////////////////////////////////////////////////////////////////////////////
void Hooks::initialize ()
{
_debug = context.config.getInteger ("debug.hooks");
_debug = Context::getContext ().config.getInteger ("debug.hooks");
// Scan <rc.hooks.location>
// <rc.data.location>/hooks
Directory d;
if (Context::getContext ().config.has ("hooks.location"))
{
d = Directory (Context::getContext ().config.get ("hooks.location"));
}
else
{
d = Directory (Context::getContext ().config.get ("data.location"));
d += "hooks";
}
// Scan <rc.data.location>/hooks
Directory d (context.config.get ("data.location"));
d += "hooks";
if (d.is_directory () &&
d.readable ())
{
@@ -73,17 +94,17 @@ void Hooks::initialize ()
name.substr (0, 9) == "on-modify" ||
name.substr (0, 9) == "on-launch" ||
name.substr (0, 7) == "on-exit")
context.debug ("Found hook script " + i);
Context::getContext ().debug ("Found hook script " + i);
else
context.debug ("Found misnamed hook script " + i);
Context::getContext ().debug ("Found misnamed hook script " + i);
}
}
}
}
else if (_debug >= 1)
context.debug ("Hook directory not readable: " + d._data);
Context::getContext ().debug ("Hook directory not readable: " + d._data);
_enabled = context.config.getBoolean ("hooks");
_enabled = Context::getContext ().config.getBoolean ("hooks");
}
////////////////////////////////////////////////////////////////////////////////
@@ -111,7 +132,7 @@ void Hooks::onLaunch () const
if (! _enabled)
return;
context.timer_hooks.start ();
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-launch");
if (matchingScripts.size ())
@@ -126,25 +147,25 @@ void Hooks::onLaunch () const
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
assertNTasks (outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
context.footnote (message);
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback);
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
context.error (message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing.
}
}
}
context.timer_hooks.stop ();
Context::getContext ().time_hooks_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -164,14 +185,14 @@ void Hooks::onExit () const
if (! _enabled)
return;
context.timer_hooks.start ();
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-exit");
if (matchingScripts.size ())
{
// Get the set of changed tasks.
std::vector <Task> tasks;
context.tdb2.get_changes (tasks);
Context::getContext ().tdb2.get_changes (tasks);
// Convert to a vector of strings.
std::vector <std::string> input;
@@ -188,25 +209,25 @@ void Hooks::onExit () const
std::vector <std::string> outputFeedback;
separateOutput (output, outputJSON, outputFeedback);
assertNTasks (outputJSON, 0);
assertNTasks (outputJSON, 0, script);
if (status == 0)
{
for (auto& message : outputFeedback)
context.footnote (message);
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback);
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
context.error (message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing.
}
}
}
context.timer_hooks.stop ();
Context::getContext ().time_hooks_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -226,7 +247,7 @@ void Hooks::onAdd (Task& task) const
if (! _enabled)
return;
context.timer_hooks.start ();
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-add");
if (matchingScripts.size ())
@@ -247,21 +268,21 @@ void Hooks::onAdd (Task& task) const
if (status == 0)
{
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, task);
assertNTasks (outputJSON, 1, script);
assertValidJSON (outputJSON, script);
assertSameTask (outputJSON, task, script);
// Propagate forward to the next script.
input[0] = outputJSON[0];
for (auto& message : outputFeedback)
context.footnote (message);
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback);
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
context.error (message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing.
}
@@ -271,7 +292,7 @@ void Hooks::onAdd (Task& task) const
task = Task (input[0]);
}
context.timer_hooks.stop ();
Context::getContext ().time_hooks_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -292,7 +313,7 @@ void Hooks::onModify (const Task& before, Task& after) const
if (! _enabled)
return;
context.timer_hooks.start ();
Timer timer;
std::vector <std::string> matchingScripts = scripts ("on-modify");
if (matchingScripts.size ())
@@ -314,21 +335,21 @@ void Hooks::onModify (const Task& before, Task& after) const
if (status == 0)
{
assertNTasks (outputJSON, 1);
assertValidJSON (outputJSON);
assertSameTask (outputJSON, before);
assertNTasks (outputJSON, 1, script);
assertValidJSON (outputJSON, script);
assertSameTask (outputJSON, before, script);
// Propagate accepted changes forward to the next script.
input[1] = outputJSON[0];
for (auto& message : outputFeedback)
context.footnote (message);
Context::getContext ().footnote (message);
}
else
{
assertFeedback (outputFeedback);
assertFeedback (outputFeedback, script);
for (auto& message : outputFeedback)
context.error (message);
Context::getContext ().error (message);
throw 0; // This is how hooks silently terminate processing.
}
@@ -337,7 +358,7 @@ void Hooks::onModify (const Task& before, Task& after) const
after = Task (input[1]);
}
context.timer_hooks.stop ();
Context::getContext ().time_hooks_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -387,7 +408,9 @@ bool Hooks::isJSON (const std::string& input) const
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertValidJSON (const std::vector <std::string>& input) const
void Hooks::assertValidJSON (
const std::vector <std::string>& input,
const std::string& script) const
{
for (auto& i : input)
{
@@ -395,7 +418,7 @@ void Hooks::assertValidJSON (const std::vector <std::string>& input) const
i[0] != '{' ||
i[i.length () - 1] != '}')
{
context.error (STRING_HOOK_ERROR_OBJECT);
Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ()));
throw 0;
}
@@ -404,19 +427,19 @@ void Hooks::assertValidJSON (const std::vector <std::string>& input) const
json::value* root = json::parse (i);
if (root->type () != json::j_object)
{
context.error (STRING_HOOK_ERROR_OBJECT);
Context::getContext ().error (format (STRING_HOOK_ERROR_OBJECT, Path (script).name ()));
throw 0;
}
if (((json::object*)root)->_data.find ("description") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NODESC);
Context::getContext ().error (format (STRING_HOOK_ERROR_NODESC, Path (script).name ()));
throw 0;
}
if (((json::object*)root)->_data.find ("uuid") == ((json::object*)root)->_data.end ())
{
context.error (STRING_HOOK_ERROR_NOUUID);
Context::getContext ().error (format (STRING_HOOK_ERROR_NOUUID, Path (script).name ()));
throw 0;
}
@@ -425,32 +448,38 @@ void Hooks::assertValidJSON (const std::vector <std::string>& input) const
catch (const std::string& e)
{
context.error (format (STRING_HOOK_ERROR_SYNTAX, i));
Context::getContext ().error (format (STRING_HOOK_ERROR_SYNTAX, i));
if (_debug)
context.error (STRING_HOOK_ERROR_JSON + e);
Context::getContext ().error (STRING_HOOK_ERROR_JSON + e);
throw 0;
}
catch (...)
{
context.error (STRING_HOOK_ERROR_NOPARSE + i);
Context::getContext ().error (STRING_HOOK_ERROR_NOPARSE + i);
throw 0;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertNTasks (const std::vector <std::string>& input, unsigned int n) const
void Hooks::assertNTasks (
const std::vector <std::string>& input,
unsigned int n,
const std::string& script) const
{
if (input.size () != n)
{
context.error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size ()));
Context::getContext ().error (format (STRING_HOOK_ERROR_BAD_NUM, n, (int) input.size (), Path (script).name ()));
throw 0;
}
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertSameTask (const std::vector <std::string>& input, const Task& task) const
void Hooks::assertSameTask (
const std::vector <std::string>& input,
const Task& task,
const std::string& script) const
{
std::string uuid = task.get ("uuid");
@@ -463,15 +492,17 @@ void Hooks::assertSameTask (const std::vector <std::string>& input, const Task&
if (u == root_obj->_data.end () ||
u->second->type () != json::j_string)
{
context.error (format (STRING_HOOK_ERROR_SAME1, uuid));
Context::getContext ().error (format (STRING_HOOK_ERROR_SAME1, uuid, Path (script).name ()));
throw 0;
}
json::string* up = (json::string*) u->second;
std::string json_uuid = json::decode (unquoteText (up->dump ()));
auto text = up->dump ();
Lexer::dequote (text);
std::string json_uuid = json::decode (text);
if (json_uuid != uuid)
{
context.error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid));
Context::getContext ().error (format (STRING_HOOK_ERROR_SAME2, uuid, json_uuid, Path (script).name ()));
throw 0;
}
@@ -480,7 +511,9 @@ void Hooks::assertSameTask (const std::vector <std::string>& input, const Task&
}
////////////////////////////////////////////////////////////////////////////////
void Hooks::assertFeedback (const std::vector <std::string>& input) const
void Hooks::assertFeedback (
const std::vector <std::string>& input,
const std::string& script) const
{
bool foundSomething = false;
for (auto& i : input)
@@ -489,7 +522,7 @@ void Hooks::assertFeedback (const std::vector <std::string>& input) const
if (! foundSomething)
{
context.error (STRING_HOOK_ERROR_NOFEEDBACK);
Context::getContext ().error (format (STRING_HOOK_ERROR_NOFEEDBACK, Path (script).name ()));
throw 0;
}
}
@@ -507,13 +540,13 @@ std::vector <std::string>& Hooks::buildHookScriptArgs (std::vector <std::string>
args.push_back ("args:" + std::string (v));
// Command to be executed.
args.push_back ("command:" + context.cli2.getCommand ());
args.push_back ("command:" + Context::getContext ().cli2.getCommand ());
// rc file used after applying all overrides.
args.push_back ("rc:" + context.rc_file._data);
args.push_back ("rc:" + Context::getContext ().rc_file._data);
// Directory containing *.data files.
args.push_back ("data:" + context.data_dir._data);
args.push_back ("data:" + Context::getContext ().data_dir._data);
// Taskwarrior version, same as returned by "task --version"
args.push_back ("version:" + std::string(VERSION));
@@ -528,13 +561,13 @@ int Hooks::callHookScript (
std::vector <std::string>& output) const
{
if (_debug >= 1)
context.debug ("Hook: Calling " + script);
Context::getContext ().debug ("Hook: Calling " + script);
if (_debug >= 2)
{
context.debug ("Hook: input");
Context::getContext ().debug ("Hook: input");
for (const auto& i : input)
context.debug (" " + i);
Context::getContext ().debug (" " + i);
}
std::string inputStr;
@@ -545,9 +578,9 @@ int Hooks::callHookScript (
buildHookScriptArgs (args);
if (_debug >= 2)
{
context.debug ("Hooks: args");
Context::getContext ().debug ("Hooks: args");
for (const auto& arg: args)
context.debug (" " + arg);
Context::getContext ().debug (" " + arg);
}
// Measure time for each hook if running in debug
@@ -555,25 +588,24 @@ int Hooks::callHookScript (
std::string outputStr;
if (_debug >= 2)
{
Timer timer_per_hook("Hooks::execute (" + script + ")");
timer_per_hook.start();
Timer timer;
status = execute (script, args, inputStr, outputStr);
Context::getContext ().debugTiming (format ("Hooks::execute ({1})", script), timer);
}
else
status = execute (script, args, inputStr, outputStr);
split (output, outputStr, '\n');
output = split (outputStr, '\n');
if (_debug >= 2)
{
context.debug ("Hook: output");
Context::getContext ().debug ("Hook: output");
for (const auto& i : output)
if (i != "")
context.debug (" " + i);
Context::getContext ().debug (" " + i);
context.debug (format ("Hook: Completed with status {1}", status));
context.debug (" "); // Blank line
Context::getContext ().debug (format ("Hook: Completed with status {1}", status));
Context::getContext ().debug (" "); // Blank line
}
return status;

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -47,10 +47,10 @@ private:
std::vector <std::string> scripts (const std::string&) const;
void separateOutput (const std::vector <std::string>&, std::vector <std::string>&, std::vector <std::string>&) const;
bool isJSON (const std::string&) const;
void assertValidJSON (const std::vector <std::string>&) const;
void assertNTasks (const std::vector <std::string>&, unsigned int) const;
void assertSameTask (const std::vector <std::string>&, const Task&) const;
void assertFeedback (const std::vector <std::string>&) const;
void assertValidJSON (const std::vector <std::string>&, const std::string&) const;
void assertNTasks (const std::vector <std::string>&, unsigned int, const std::string&) const;
void assertSameTask (const std::vector <std::string>&, const Task&, const std::string&) const;
void assertFeedback (const std::vector <std::string>&, const std::string&) const;
std::vector <std::string>& buildHookScriptArgs (std::vector <std::string>&) const;
int callHookScript (const std::string&, const std::vector <std::string>&, std::vector <std::string>&) const;

File diff suppressed because it is too large Load Diff

View File

@@ -1,178 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_ISO8601
#define INCLUDED_ISO8601
#include <Nibbler.h>
#include <time.h>
// Date
class ISO8601d
{
public:
static std::string weekstart;
static int minimumMatchLength;
static bool isoEnabled;
ISO8601d ();
ISO8601d (const std::string&, const std::string& format = "");
ISO8601d (time_t);
ISO8601d (const int, const int, const int);
ISO8601d (const int, const int, const int, const int, const int, const int);
~ISO8601d ();
bool parse (const std::string&, std::string::size_type&, const std::string& format = "");
time_t toEpoch () const;
std::string toEpochString () const;
std::string toISO () const;
std::string toISOLocalExtended () const;
double toJulian () const;
void toMDY (int&, int&, int&) const;
const std::string toString (const std::string& format = "m/d/Y") const;
ISO8601d startOfDay () const;
ISO8601d startOfWeek () const;
ISO8601d startOfMonth () const;
ISO8601d startOfYear () const;
static bool valid (const std::string&, const std::string& format = "");
static bool valid (const int, const int, const int, const int, const int, const int);
static bool valid (const int, const int, const int);
static bool valid (const int, const int);
static bool leapYear (int);
static int daysInMonth (int, int);
static int daysInYear (int);
static std::string monthName (int);
static std::string monthNameShort (int);
static std::string dayName (int);
static std::string dayNameShort (int);
static int dayOfWeek (const std::string&);
static int dayOfWeek (int, int, int);
static int monthOfYear (const std::string&);
static int length (const std::string&);
int month () const;
int week () const;
int day () const;
int year () const;
int weekOfYear (int) const;
int dayOfWeek () const;
int dayOfYear () const;
int hour () const;
int minute () const;
int second () const;
bool operator== (const ISO8601d&) const;
bool operator!= (const ISO8601d&) const;
bool operator< (const ISO8601d&) const;
bool operator> (const ISO8601d&) const;
bool operator<= (const ISO8601d&) const;
bool operator>= (const ISO8601d&) const;
bool sameHour (const ISO8601d&) const;
bool sameDay (const ISO8601d&) const;
bool sameWeek (const ISO8601d&) const;
bool sameMonth (const ISO8601d&) const;
bool sameYear (const ISO8601d&) const;
ISO8601d operator+ (const int);
ISO8601d operator- (const int);
ISO8601d& operator+= (const int);
ISO8601d& operator-= (const int);
time_t operator- (const ISO8601d&);
void operator-- (); // Prefix
void operator-- (int); // Postfix
void operator++ (); // Prefix
void operator++ (int); // Postfix
private:
void clear ();
bool parse_formatted (Nibbler&, const std::string&);
bool parse_named (Nibbler&);
bool parse_epoch (Nibbler&);
bool parse_date_time (Nibbler&);
bool parse_date_time_ext (Nibbler&);
bool parse_date_ext (Nibbler&);
bool parse_off_ext (Nibbler&);
bool parse_time_ext (Nibbler&);
bool parse_time_utc_ext (Nibbler&);
bool parse_time_off_ext (Nibbler&);
bool validate ();
void resolve ();
std::string dump () const;
public:
int _year;
int _month;
int _week;
int _weekday;
int _julian;
int _day;
int _seconds;
int _offset;
bool _utc;
time_t _date;
};
// Period
class ISO8601p
{
public:
static bool isoEnabled;
ISO8601p ();
ISO8601p (time_t);
ISO8601p (const std::string&);
~ISO8601p ();
ISO8601p (const ISO8601p&); // Unimplemented
ISO8601p& operator= (const ISO8601p&);
bool operator< (const ISO8601p&);
bool operator> (const ISO8601p&);
operator std::string () const;
operator time_t () const;
bool parse (const std::string&, std::string::size_type&);
const std::string format () const;
const std::string formatVague () const;
private:
void clear ();
bool parse_designated (Nibbler&);
bool validate ();
void resolve ();
std::string dump () const;
public:
int _year;
int _month;
int _day;
int _hours;
int _minutes;
int _seconds;
time_t _period;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,494 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <JSON.h>
#include <text.h>
#include <i18n.h>
#include <utf8.h>
const char *json_encode[] = {
"\x00", "\x01", "\x02", "\x03", "\x04", "\x05", "\x06", "\x07",
"\\b", "\\t", "\\n", "\x0b", "\\f", "\\r", "\x0e", "\x0f",
"\x10", "\x11", "\x12", "\x13", "\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1a", "\x1b", "\x1c", "\x1d", "\x1e", "\x1f",
"\x20", "\x21", "\\\"", "\x23", "\x24", "\x25", "\x26", "\x27",
"\x28", "\x29", "\x2a", "\x2b", "\x2c", "\x2d", "\x2e", "\\/",
"\x30", "\x31", "\x32", "\x33", "\x34", "\x35", "\x36", "\x37",
"\x38", "\x39", "\x3a", "\x3b", "\x3c", "\x3d", "\x3e", "\x3f",
"\x40", "\x41", "\x42", "\x43", "\x44", "\x45", "\x46", "\x47",
"\x48", "\x49", "\x4a", "\x4b", "\x4c", "\x4d", "\x4e", "\x4f",
"\x50", "\x51", "\x52", "\x53", "\x54", "\x55", "\x56", "\x57",
"\x58", "\x59", "\x5a", "\x5b", "\\\\", "\x5d", "\x5e", "\x5f",
"\x60", "\x61", "\x62", "\x63", "\x64", "\x65", "\x66", "\x67",
"\x68", "\x69", "\x6a", "\x6b", "\x6c", "\x6d", "\x6e", "\x6f",
"\x70", "\x71", "\x72", "\x73", "\x74", "\x75", "\x76", "\x77",
"\x78", "\x79", "\x7a", "\x7b", "\x7c", "\x7d", "\x7e", "\x7f",
"\x80", "\x81", "\x82", "\x83", "\x84", "\x85", "\x86", "\x87",
"\x88", "\x89", "\x8a", "\x8b", "\x8c", "\x8d", "\x8e", "\x8f",
"\x90", "\x91", "\x92", "\x93", "\x94", "\x95", "\x96", "\x97",
"\x98", "\x99", "\x9a", "\x9b", "\x9c", "\x9d", "\x9e", "\x9f",
"\xa0", "\xa1", "\xa2", "\xa3", "\xa4", "\xa5", "\xa6", "\xa7",
"\xa8", "\xa9", "\xaa", "\xab", "\xac", "\xad", "\xae", "\xaf",
"\xb0", "\xb1", "\xb2", "\xb3", "\xb4", "\xb5", "\xb6", "\xb7",
"\xb8", "\xb9", "\xba", "\xbb", "\xbc", "\xbd", "\xbe", "\xbf",
"\xc0", "\xc1", "\xc2", "\xc3", "\xc4", "\xc5", "\xc6", "\xc7",
"\xc8", "\xc9", "\xca", "\xcb", "\xcc", "\xcd", "\xce", "\xcf",
"\xd0", "\xd1", "\xd2", "\xd3", "\xd4", "\xd5", "\xd6", "\xd7",
"\xd8", "\xd9", "\xda", "\xdb", "\xdc", "\xdd", "\xde", "\xdf",
"\xe0", "\xe1", "\xe2", "\xe3", "\xe4", "\xe5", "\xe6", "\xe7",
"\xe8", "\xe9", "\xea", "\xeb", "\xec", "\xed", "\xee", "\xef",
"\xf0", "\xf1", "\xf2", "\xf3", "\xf4", "\xf5", "\xf6", "\xf7",
"\xf8", "\xf9", "\xfa", "\xfb", "\xfc", "\xfd", "\xfe", "\xff"
};
////////////////////////////////////////////////////////////////////////////////
json::value* json::value::parse (Nibbler& nibbler)
{
json::value* v;
if ((v = json::object::parse (nibbler)) ||
(v = json::array::parse (nibbler)) ||
(v = json::string::parse (nibbler)) ||
(v = json::number::parse (nibbler)) ||
(v = json::literal::parse (nibbler)))
return v;
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::value::type ()
{
return json::j_value;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::value::dump () const
{
return "<value>";
}
////////////////////////////////////////////////////////////////////////////////
json::string::string (const std::string& other)
{
_data = other;
}
////////////////////////////////////////////////////////////////////////////////
json::string* json::string::parse (Nibbler& nibbler)
{
std::string value;
if (nibbler.getQuoted ('"', value))
{
json::string* s = new json::string ();
s->_data = value;
return s;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::string::type ()
{
return json::j_string;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::string::dump () const
{
return std::string ("\"") + _data + "\"";
}
////////////////////////////////////////////////////////////////////////////////
json::number* json::number::parse (Nibbler& nibbler)
{
double d;
if (nibbler.getNumber (d))
{
json::number* s = new json::number ();
s->_dvalue = d;
return s;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::number::type ()
{
return json::j_number;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::number::dump () const
{
return format (_dvalue);
}
////////////////////////////////////////////////////////////////////////////////
json::number::operator double () const
{
return _dvalue;
}
////////////////////////////////////////////////////////////////////////////////
json::literal* json::literal::parse (Nibbler& nibbler)
{
if (nibbler.getLiteral ("null"))
{
json::literal* s = new json::literal ();
s->_lvalue = nullvalue;
return s;
}
else if (nibbler.getLiteral ("false"))
{
json::literal* s = new json::literal ();
s->_lvalue = falsevalue;
return s;
}
else if (nibbler.getLiteral ("true"))
{
json::literal* s = new json::literal ();
s->_lvalue = truevalue;
return s;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::literal::type ()
{
return json::j_literal;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::literal::dump () const
{
if (_lvalue == nullvalue) return "null";
else if (_lvalue == falsevalue) return "false";
else return "true";
}
////////////////////////////////////////////////////////////////////////////////
json::array::~array ()
{
for (auto& i : _data)
delete i;
}
////////////////////////////////////////////////////////////////////////////////
json::array* json::array::parse (Nibbler& nibbler)
{
Nibbler n (nibbler);
n.skipWS ();
if (n.skip ('['))
{
n.skipWS ();
json::array* arr = new json::array ();
json::value* value;
if ((value = json::value::parse (n)))
{
arr->_data.push_back (value);
value = NULL; // Not a leak. Looks like a leak.
n.skipWS ();
while (n.skip (','))
{
n.skipWS ();
if ((value = json::value::parse (n)))
{
arr->_data.push_back (value);
n.skipWS ();
}
else
{
delete arr;
throw format (STRING_JSON_MISSING_VALUE, (int) n.cursor ());
}
}
}
if (n.skip (']'))
{
nibbler = n;
return arr;
}
else
throw format (STRING_JSON_MISSING_BRACKET, (int) n.cursor ());
delete arr;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::array::type ()
{
return json::j_array;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::array::dump () const
{
std::string output;
output += "[";
for (auto i = _data.begin (); i != _data.end (); ++i)
{
if (i != _data.begin ())
output += ",";
output += (*i)->dump ();
}
output += "]";
return output;
}
////////////////////////////////////////////////////////////////////////////////
json::object::~object ()
{
for (auto& i : _data)
delete i.second;
}
////////////////////////////////////////////////////////////////////////////////
json::object* json::object::parse (Nibbler& nibbler)
{
Nibbler n (nibbler);
n.skipWS ();
if (n.skip ('{'))
{
n.skipWS ();
json::object* obj = new json::object ();
std::string name;
json::value* value;
if (json::object::parse_pair (n, name, value))
{
obj->_data.insert (std::pair <std::string, json::value*> (name, value));
value = NULL; // Not a leak. Looks like a leak.
n.skipWS ();
while (n.skip (','))
{
n.skipWS ();
if (json::object::parse_pair (n, name, value))
{
obj->_data.insert (std::pair <std::string, json::value*> (name, value));
n.skipWS ();
}
else
{
delete obj;
throw format (STRING_JSON_MISSING_VALUE, (int) n.cursor ());
}
}
}
if (n.skip ('}'))
{
nibbler = n;
return obj;
}
else
throw format (STRING_JSON_MISSING_BRACE, (int) n.cursor ());
delete obj;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
bool json::object::parse_pair (
Nibbler& nibbler,
std::string& name,
json::value*& val)
{
Nibbler n (nibbler);
if (n.getQuoted ('"', name))
{
n.skipWS ();
if (n.skip (':'))
{
n.skipWS ();
if ((val = json::value::parse (n)))
{
nibbler = n;
return true;
}
else
throw format (STRING_JSON_MISSING_VALUE2, (int) n.cursor ());
}
else
throw format (STRING_JSON_MISSING_COLON, (int) n.cursor ());
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
json::jtype json::object::type ()
{
return json::j_object;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::object::dump () const
{
std::string output;
output += "{";
for (auto i = _data.begin (); i != _data.end (); ++i)
{
if (i != _data.begin ())
output += ",";
output += "\"" + i->first + "\":";
output += i->second->dump ();
}
output += "}";
return output;
}
////////////////////////////////////////////////////////////////////////////////
json::value* json::parse (const std::string& input)
{
json::value* root = NULL;
Nibbler n (input);
n.skipWS ();
if (n.next () == '{') root = json::object::parse (n);
else if (n.next () == '[') root = json::array::parse (n);
else
throw format (STRING_JSON_MISSING_OPEN, (int) n.cursor ());
// Check for end condition.
n.skipWS ();
if (!n.depleted ())
{
delete root;
throw format (STRING_JSON_EXTRA_CHARACTERS, (int) n.cursor ());
}
return root;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::encode (const std::string& input)
{
std::string output;
output.reserve ((input.size () * 6) / 5); // 20% increase.
auto last = input.begin ();
for (auto i = input.begin (); i != input.end (); ++i)
{
switch (*i)
{
// Simple translations.
case '"':
case '\\':
case '/':
case '\b':
case '\f':
case '\n':
case '\r':
case '\t':
output.append (last, i);
output += json_encode[(unsigned char)(*i)];
last = i + 1;
// Default NOP.
}
}
output.append (last, input.end ());
return output;
}
////////////////////////////////////////////////////////////////////////////////
std::string json::decode (const std::string& input)
{
std::string output;
output.reserve (input.size ()); // Same size.
size_t pos = 0;
while (pos < input.length ())
{
if (input[pos] == '\\')
{
++pos;
switch (input[pos])
{
// Simple translations.
case '"': output += '"'; break;
case '\\': output += '\\'; break;
case '/': output += '/'; break;
case 'b': output += '\b'; break;
case 'f': output += '\f'; break;
case 'n': output += '\n'; break;
case 'r': output += '\r'; break;
case 't': output += '\t'; break;
// Compose a UTF8 unicode character.
case 'u':
output += utf8_character (utf8_codepoint (input.substr (++pos)));
pos += 3;
break;
// If it is an unrecognized sequence, do nothing.
default:
output += '\\';
output += input[pos];
break;
}
++pos;
}
else
{
size_t next_backslash = input.find ('\\', pos);
output.append (input, pos, next_backslash - pos);
pos = next_backslash;
}
}
return output;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,135 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_JSON
#define INCLUDED_JSON
#include <map>
#include <vector>
#include <string>
#include <Nibbler.h>
namespace json
{
enum jtype
{
j_value, // 0
j_object, // 1
j_array, // 2
j_string, // 3
j_number, // 4
j_literal // 5
};
class value
{
public:
value () {}
virtual ~value () {}
static value* parse (Nibbler&);
virtual jtype type ();
virtual std::string dump () const;
};
class string : public value
{
public:
string () {}
string (const std::string&);
~string () {}
static string* parse (Nibbler&);
jtype type ();
std::string dump () const;
public:
std::string _data;
};
class number : public value
{
public:
number () : _dvalue (0.0) {}
~number () {}
static number* parse (Nibbler&);
jtype type ();
std::string dump () const;
operator double () const;
public:
double _dvalue;
};
class literal : public value
{
public:
literal () : _lvalue (none) {}
~literal () {}
static literal* parse (Nibbler&);
jtype type ();
std::string dump () const;
public:
enum literal_value {none, nullvalue, falsevalue, truevalue};
literal_value _lvalue;
};
class array : public value
{
public:
array () {}
~array ();
static array* parse (Nibbler&);
jtype type ();
std::string dump () const;
public:
std::vector <value*> _data;
};
class object : public value
{
public:
object () {}
~object ();
static object* parse (Nibbler&);
static bool parse_pair (Nibbler&, std::string&, value*&);
jtype type ();
std::string dump () const;
public:
std::map <std::string, value*> _data;
};
// Parser entry point.
value* parse (const std::string&);
// Encode/decode for JSON entities.
std::string encode (const std::string&);
std::string decode (const std::string&);
}
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,7 +28,9 @@
#include <Lexer.h>
#include <algorithm>
#include <ctype.h>
#include <ISO8601.h>
#include <Datetime.h>
#include <Duration.h>
#include <unicode.h>
#include <utf8.h>
static const std::string uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
@@ -58,7 +60,7 @@ Lexer::~Lexer ()
bool Lexer::token (std::string& token, Lexer::Type& type)
{
// Eat white space.
while (isWhitespace (_text[_cursor]))
while (unicodeWhitespace (_text[_cursor]))
utf8_next_char (_text, _cursor);
// Terminate at EOS.
@@ -141,79 +143,12 @@ const std::string Lexer::typeName (const Lexer::Type& type)
return "unknown";
}
////////////////////////////////////////////////////////////////////////////////
// Complete Unicode whitespace list.
//
// http://en.wikipedia.org/wiki/Whitespace_character
// Updated 2015-09-13
// Static
//
// TODO This list should be derived from the Unicode database.
bool Lexer::isWhitespace (int c)
{
return (c == 0x0020 || // space Common Separator, space
c == 0x0009 || // Common Other, control HT, Horizontal Tab
c == 0x000A || // Common Other, control LF, Line feed
c == 0x000B || // Common Other, control VT, Vertical Tab
c == 0x000C || // Common Other, control FF, Form feed
c == 0x000D || // Common Other, control CR, Carriage return
c == 0x0085 || // Common Other, control NEL, Next line
c == 0x00A0 || // no-break space Common Separator, space
c == 0x1680 || // ogham space mark Ogham Separator, space
c == 0x180E || // mongolian vowel separator Mongolian Separator, space
c == 0x2000 || // en quad Common Separator, space
c == 0x2001 || // em quad Common Separator, space
c == 0x2002 || // en space Common Separator, space
c == 0x2003 || // em space Common Separator, space
c == 0x2004 || // three-per-em space Common Separator, space
c == 0x2005 || // four-per-em space Common Separator, space
c == 0x2006 || // six-per-em space Common Separator, space
c == 0x2007 || // figure space Common Separator, space
c == 0x2008 || // punctuation space Common Separator, space
c == 0x2009 || // thin space Common Separator, space
c == 0x200A || // hair space Common Separator, space
c == 0x200B || // zero width space
c == 0x200C || // zero width non-joiner
c == 0x200D || // zero width joiner
c == 0x2028 || // line separator Common Separator, line
c == 0x2029 || // paragraph separator Common Separator, paragraph
c == 0x202F || // narrow no-break space Common Separator, space
c == 0x205F || // medium mathematical space Common Separator, space
c == 0x2060 || // word joiner
c == 0x3000); // ideographic space Common Separator, space
}
////////////////////////////////////////////////////////////////////////////////
bool Lexer::isAlpha (int c)
{
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
}
////////////////////////////////////////////////////////////////////////////////
// Digits 0-9.
//
// TODO This list should be derived from the Unicode database.
bool Lexer::isDigit (int c)
{
return c >= 0x30 && c <= 0x39;
}
////////////////////////////////////////////////////////////////////////////////
// Digits 0-9 a-f A-F.
bool Lexer::isHexDigit (int c)
{
return (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
////////////////////////////////////////////////////////////////////////////////
bool Lexer::isIdentifierStart (int c)
{
return c && // Include null character check.
! isWhitespace (c) &&
! isDigit (c) &&
! unicodeWhitespace (c) &&
! unicodeLatinDigit (c) &&
! isSingleCharOperator (c) &&
! isPunctuation (c);
}
@@ -224,7 +159,7 @@ bool Lexer::isIdentifierNext (int c)
return c && // Include null character check.
c != ':' && // Used in isPair.
c != '=' && // Used in isPair.
! isWhitespace (c) &&
! unicodeWhitespace (c) &&
! isSingleCharOperator (c);
}
@@ -271,15 +206,15 @@ bool Lexer::isTripleCharOperator (int c0, int c1, int c2, int c3)
bool Lexer::isBoundary (int left, int right)
{
// EOS
if (right == '\0') return true;
if (right == '\0') return true;
// XOR
if (isAlpha (left) != isAlpha (right)) return true;
if (isDigit (left) != isDigit (right)) return true;
if (isWhitespace (left) != isWhitespace (right)) return true;
if (unicodeLatinAlpha (left) != unicodeLatinAlpha (right)) return true;
if (unicodeLatinDigit (left) != unicodeLatinDigit (right)) return true;
if (unicodeWhitespace (left) != unicodeWhitespace (right)) return true;
// OR
if (isPunctuation (left) || isPunctuation (right)) return true;
if (isPunctuation (left) || isPunctuation (right)) return true;
return false;
}
@@ -288,7 +223,8 @@ bool Lexer::isBoundary (int left, int right)
bool Lexer::isHardBoundary (int left, int right)
{
// EOS
if (right == '\0') return true;
if (right == '\0')
return true;
// FILTER operators that don't need to be surrounded by whitespace.
if (left == '(' ||
@@ -303,14 +239,14 @@ bool Lexer::isHardBoundary (int left, int right)
////////////////////////////////////////////////////////////////////////////////
bool Lexer::isPunctuation (int c)
{
return isprint (c) &&
c != ' ' &&
c != '@' &&
c != '#' &&
c != '$' &&
c != '_' &&
! isDigit (c) &&
! isAlpha (c);
return isprint (c) &&
c != ' ' &&
c != '@' &&
c != '#' &&
c != '$' &&
c != '_' &&
! unicodeLatinDigit (c) &&
! unicodeLatinAlpha (c);
}
////////////////////////////////////////////////////////////////////////////////
@@ -423,7 +359,7 @@ std::string Lexer::commify (const std::string& data)
int i;
for (int i = 0; i < (int) data.length (); ++i)
{
if (Lexer::isDigit (data[i]))
if (unicodeLatinDigit (data[i]))
end = i;
if (data[i] == '.')
@@ -441,11 +377,11 @@ std::string Lexer::commify (const std::string& data)
int consecutiveDigits = 0;
for (; i >= 0; --i)
{
if (Lexer::isDigit (data[i]))
if (unicodeLatinDigit (data[i]))
{
result += data[i];
if (++consecutiveDigits == 3 && i && Lexer::isDigit (data[i - 1]))
if (++consecutiveDigits == 3 && i && unicodeLatinDigit (data[i - 1]))
{
result += ',';
consecutiveDigits = 0;
@@ -465,11 +401,11 @@ std::string Lexer::commify (const std::string& data)
int consecutiveDigits = 0;
for (; i >= 0; --i)
{
if (Lexer::isDigit (data[i]))
if (unicodeLatinDigit (data[i]))
{
result += data[i];
if (++consecutiveDigits == 3 && i && Lexer::isDigit (data[i - 1]))
if (++consecutiveDigits == 3 && i && unicodeLatinDigit (data[i - 1]))
{
result += ',';
consecutiveDigits = 0;
@@ -553,12 +489,12 @@ bool Lexer::isString (std::string& token, Lexer::Type& type, const std::string&
////////////////////////////////////////////////////////////////////////////////
// Lexer::Type::date
// <ISO8601d>
// <Datetime>
bool Lexer::isDate (std::string& token, Lexer::Type& type)
{
// Try an ISO date parse.
std::size_t iso_i = 0;
ISO8601d iso;
Datetime iso;
if (iso.parse (_text.substr (_cursor), iso_i, Lexer::dateFormat))
{
type = Lexer::Type::date;
@@ -572,21 +508,21 @@ bool Lexer::isDate (std::string& token, Lexer::Type& type)
////////////////////////////////////////////////////////////////////////////////
// Lexer::Type::duration
// <ISO8106p> | <Duration>
// <Duration>
bool Lexer::isDuration (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
std::string extractedToken;
Lexer::Type extractedType;
if (isOperator(extractedToken, extractedType))
if (isOperator (extractedToken, extractedType))
{
_cursor = marker;
return false;
}
marker = 0;
ISO8601p iso;
Duration iso;
if (iso.parse (_text.substr (_cursor), marker))
{
type = Lexer::Type::duration;
@@ -620,17 +556,17 @@ bool Lexer::isUUID (std::string& token, Lexer::Type& type, bool endBoundary)
{
if (uuid_pattern[i] == 'x')
{
if (! isHexDigit (_text[marker + i]))
if (! unicodeHexDigit (_text[marker + i]))
break;
}
else if (uuid_pattern[i] != _text[marker + i])
break;
}
if (i >= uuid_min_length &&
(! endBoundary ||
! _text[marker + i] ||
isWhitespace (_text[marker + i]) ||
if (i >= uuid_min_length &&
(! endBoundary ||
! _text[marker + i] ||
unicodeWhitespace (_text[marker + i]) ||
isSingleCharOperator (_text[marker + i])))
{
token = _text.substr (_cursor, i);
@@ -655,7 +591,7 @@ bool Lexer::isHexNumber (std::string& token, Lexer::Type& type)
{
marker += 2;
while (isHexDigit (_text[marker]))
while (unicodeHexDigit (_text[marker]))
++marker;
if (marker - _cursor > 2)
@@ -680,19 +616,19 @@ bool Lexer::isNumber (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
if (isDigit (_text[marker]))
if (unicodeLatinDigit (_text[marker]))
{
++marker;
while (isDigit (_text[marker]))
while (unicodeLatinDigit (_text[marker]))
utf8_next_char (_text, marker);
if (_text[marker] == '.')
{
++marker;
if (isDigit (_text[marker]))
if (unicodeLatinDigit (_text[marker]))
{
++marker;
while (isDigit (_text[marker]))
while (unicodeLatinDigit (_text[marker]))
utf8_next_char (_text, marker);
}
}
@@ -706,29 +642,29 @@ bool Lexer::isNumber (std::string& token, Lexer::Type& type)
_text[marker] == '-')
++marker;
if (isDigit (_text[marker]))
if (unicodeLatinDigit (_text[marker]))
{
++marker;
while (isDigit (_text[marker]))
while (unicodeLatinDigit (_text[marker]))
utf8_next_char (_text, marker);
if (_text[marker] == '.')
{
++marker;
if (isDigit (_text[marker]))
if (unicodeLatinDigit (_text[marker]))
{
++marker;
while (isDigit (_text[marker]))
while (unicodeLatinDigit (_text[marker]))
utf8_next_char (_text, marker);
}
}
}
}
// Lookahread: !<isWhitespace> | !<isSingleCharOperator>
// Lookahead: !<unicodeWhitespace> | !<isSingleCharOperator>
// If there is an immediately consecutive character, that is not an operator, fail.
if (_eos > marker &&
! isWhitespace (_text[marker]) &&
! unicodeWhitespace (_text[marker]) &&
! isSingleCharOperator (_text[marker]))
return false;
@@ -748,10 +684,10 @@ bool Lexer::isInteger (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
if (isDigit (_text[marker]))
if (unicodeLatinDigit (_text[marker]))
{
++marker;
while (isDigit (_text[marker]))
while (unicodeLatinDigit (_text[marker]))
utf8_next_char (_text, marker);
token = _text.substr (_cursor, marker - _cursor);
@@ -805,7 +741,7 @@ bool Lexer::isURL (std::string& token, Lexer::Type& type)
marker += 3;
while (marker < _eos &&
! isWhitespace (_text[marker]))
! unicodeWhitespace (_text[marker]))
utf8_next_char (_text, marker);
token = _text.substr (_cursor, marker - _cursor);
@@ -846,7 +782,7 @@ bool Lexer::isPair (std::string& token, Lexer::Type& type)
if (readWord (_text, "'\"", _cursor, ignoredToken) ||
readWord (_text, _cursor, ignoredToken) ||
isEOS () ||
isWhitespace (_text[_cursor]))
unicodeWhitespace (_text[_cursor]))
{
token = _text.substr (marker, _cursor - marker);
type = Lexer::Type::pair;
@@ -900,7 +836,7 @@ bool Lexer::isSet (std::string& token, Lexer::Type& type)
// Success is multiple numbers, matching the pattern.
if (count > 1 &&
(isEOS () ||
isWhitespace (_text[_cursor]) ||
unicodeWhitespace (_text[_cursor]) ||
isHardBoundary (_text[_cursor], _text[_cursor + 1])))
{
token = _text.substr (marker, _cursor - marker);
@@ -915,7 +851,7 @@ bool Lexer::isSet (std::string& token, Lexer::Type& type)
////////////////////////////////////////////////////////////////////////////////
// Lexer::Type::tag
// ^ | '(' | ')' | <isWhitespace>
// ^ | '(' | ')' | <unicodeWhitespace>
// [ +|- ] <isIdentifierStart> [ <isIdentifierNext> ]*
bool Lexer::isTag (std::string& token, Lexer::Type& type)
{
@@ -923,7 +859,7 @@ bool Lexer::isTag (std::string& token, Lexer::Type& type)
// Lookbehind: Assert ^ or preceded by whitespace, (, or ).
if (marker > 0 &&
! isWhitespace (_text[marker - 1]) &&
! unicodeWhitespace (_text[marker - 1]) &&
_text[marker - 1] != '(' &&
_text[marker - 1] != ')')
return false;
@@ -969,12 +905,12 @@ bool Lexer::isPath (std::string& token, Lexer::Type& type)
break;
if (_text[marker] &&
! isWhitespace (_text[marker]) &&
! unicodeWhitespace (_text[marker]) &&
_text[marker] != '/')
{
utf8_next_char (_text, marker);
while (_text[marker] &&
! isWhitespace (_text[marker]) &&
! unicodeWhitespace (_text[marker]) &&
_text[marker] != '/')
utf8_next_char (_text, marker);
}
@@ -996,7 +932,7 @@ bool Lexer::isPath (std::string& token, Lexer::Type& type)
////////////////////////////////////////////////////////////////////////////////
// Lexer::Type::substitution
// / <unquoted-string> / <unquoted-string> / [g] <EOS> | <isWhitespace>
// / <unquoted-string> / <unquoted-string> / [g] <EOS> | <unicodeWhitespace>
bool Lexer::isSubstitution (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
@@ -1011,9 +947,9 @@ bool Lexer::isSubstitution (std::string& token, Lexer::Type& type)
if (_text[_cursor] == 'g')
++_cursor;
// Lookahread: <EOS> | <isWhitespace>
// Lookahread: <EOS> | <unicodeWhitespace>
if (_text[_cursor] == '\0' ||
isWhitespace (_text[_cursor]))
unicodeWhitespace (_text[_cursor]))
{
token = _text.substr (marker, _cursor - marker);
type = Lexer::Type::substitution;
@@ -1028,7 +964,7 @@ bool Lexer::isSubstitution (std::string& token, Lexer::Type& type)
////////////////////////////////////////////////////////////////////////////////
// Lexer::Type::pattern
// / <unquoted-string> / <EOS> | <isWhitespace>
// / <unquoted-string> / <EOS> | <unicodeWhitespace>
bool Lexer::isPattern (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
@@ -1036,7 +972,7 @@ bool Lexer::isPattern (std::string& token, Lexer::Type& type)
std::string word;
if (readWord (_text, "/", _cursor, word) &&
(isEOS () ||
isWhitespace (_text[_cursor])))
unicodeWhitespace (_text[_cursor])))
{
token = _text.substr (marker, _cursor - marker);
type = Lexer::Type::pattern;
@@ -1132,10 +1068,15 @@ bool Lexer::isOperator (std::string& token, Lexer::Type& type)
// rc.<name>
//
// System:
// context.program
// context.args
// context.width
// context.height
// tw.syncneeded
// tw.program
// tw.args
// tw.width
// tw.height
// context.program // 2017-02-25 Deprecated in 2.6.0
// context.args // 2017-02-25 Deprecated in 2.6.0
// context.width // 2017-02-25 Deprecated in 2.6.0
// context.height // 2017-02-25 Deprecated in 2.6.0
// system.version
// system.os
//
@@ -1159,6 +1100,7 @@ bool Lexer::isOperator (std::string& token, Lexer::Type& type)
// <date>.second
//
// Annotations (entry is a date):
// annotations.count
// annotations.<N>.entry
// annotations.<N>.description
//
@@ -1166,6 +1108,7 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
{
std::size_t marker = _cursor;
// rc. ...
std::string partialToken;
Lexer::Type partialType;
if (isLiteral ("rc.", false, false) &&
@@ -1178,7 +1121,14 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
else
_cursor = marker;
if (isOneOf ({"context.program",
// Literals
if (isOneOf ({"tw.syncneeded",
"tw.program",
"tw.args",
"tw.width",
"tw.height",
"tw.version",
"context.program",
"context.args",
"context.width",
"context.height",
@@ -1220,7 +1170,7 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
else
_cursor = checkpoint;
// [prefix]attribute
// [prefix]attribute (bounded)
if (isOneOf (attributes, false, true))
{
token = _text.substr (marker, _cursor - marker);
@@ -1228,7 +1178,7 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
return true;
}
// [prefix]attribute.
// [prefix]attribute. (unbounded)
if (isOneOf (attributes, false, false))
{
if (isLiteral (".", false, false))
@@ -1246,19 +1196,32 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
type = Lexer::Type::dom;
return true;
}
_cursor = checkpoint;
}
else
// Lookahead: !<alpha>
else if (! unicodeLatinAlpha (_text[marker]))
{
token = _text.substr (marker, _cursor - marker);
type = Lexer::Type::dom;
return true;
}
_cursor = checkpoint;
}
// [prefix]annotations.
if (isLiteral ("annotations", true, false) &&
isLiteral (".", false, false))
{
if (isLiteral ("count", false, false))
{
token = _text.substr (marker, _cursor - marker);
type = Lexer::Type::dom;
return true;
}
std::string extractedToken;
Lexer::Type extractedType;
if (isInteger (extractedToken, extractedType))
@@ -1290,6 +1253,8 @@ bool Lexer::isDOM (std::string& token, Lexer::Type& type)
}
}
}
else
_cursor = checkpoint;
}
_cursor = marker;
@@ -1327,7 +1292,7 @@ bool Lexer::isWord (std::string& token, Lexer::Type& type)
std::size_t marker = _cursor;
while (_text[marker] &&
! isWhitespace (_text[marker]) &&
! unicodeWhitespace (_text[marker]) &&
! isSingleCharOperator (_text[marker]))
utf8_next_char (_text, marker);
@@ -1363,7 +1328,7 @@ bool Lexer::isLiteral (
// End boundary conditions must be met.
if (endBoundary &&
_text[_cursor + common] &&
! Lexer::isWhitespace (_text[_cursor + common]) &&
! unicodeWhitespace (_text[_cursor + common]) &&
! Lexer::isSingleCharOperator (_text[_cursor + common]))
return false;
@@ -1430,6 +1395,7 @@ bool Lexer::isAllDigits (const std::string& text)
}
////////////////////////////////////////////////////////////////////////////////
// This is intentionally looking for a single token.
bool Lexer::isDOM (const std::string& text)
{
Lexer lex (text);
@@ -1478,10 +1444,10 @@ bool Lexer::readWord (
else if (eos - cursor >= 6 &&
((text[cursor + 0] == 'U' && text[cursor + 1] == '+') ||
(text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) &&
isHexDigit (text[cursor + 2]) &&
isHexDigit (text[cursor + 3]) &&
isHexDigit (text[cursor + 4]) &&
isHexDigit (text[cursor + 5]))
unicodeHexDigit (text[cursor + 2]) &&
unicodeHexDigit (text[cursor + 3]) &&
unicodeHexDigit (text[cursor + 4]) &&
unicodeHexDigit (text[cursor + 5]))
{
word += utf8_character (
hexToInt (
@@ -1536,7 +1502,7 @@ bool Lexer::readWord (
//
// Ends at:
// Lexer::isEOS
// Lexer::isWhitespace
// unicodeWhitespace
// Lexer::isHardBoundary
bool Lexer::readWord (
const std::string& text,
@@ -1551,7 +1517,7 @@ bool Lexer::readWord (
while ((c = text[cursor])) // Handles EOS.
{
// Unquoted word ends on white space.
if (Lexer::isWhitespace (c))
if (unicodeWhitespace (c))
break;
// Parentheses mostly.
@@ -1562,10 +1528,10 @@ bool Lexer::readWord (
else if (eos - cursor >= 6 &&
((text[cursor + 0] == 'U' && text[cursor + 1] == '+') ||
(text[cursor + 0] == '\\' && text[cursor + 1] == 'u')) &&
isHexDigit (text[cursor + 2]) &&
isHexDigit (text[cursor + 3]) &&
isHexDigit (text[cursor + 4]) &&
isHexDigit (text[cursor + 5]))
unicodeHexDigit (text[cursor + 2]) &&
unicodeHexDigit (text[cursor + 3]) &&
unicodeHexDigit (text[cursor + 4]) &&
unicodeHexDigit (text[cursor + 5]))
{
word += utf8_character (
hexToInt (

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -61,10 +61,6 @@ public:
// Static helpers.
static const std::string typeName (const Lexer::Type&);
static bool isWhitespace (int);
static bool isAlpha (int);
static bool isDigit (int);
static bool isHexDigit (int);
static bool isIdentifierStart (int);
static bool isIdentifierNext (int);
static bool isSingleCharOperator (int);

View File

@@ -1,109 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Msg.h>
#include <Lexer.h>
#include <text.h>
////////////////////////////////////////////////////////////////////////////////
void Msg::set (const std::string& name, const std::string& value)
{
_header[name] = value;
}
////////////////////////////////////////////////////////////////////////////////
std::string Msg::get (const std::string& name) const
{
auto i = _header.find (name);
if (i != _header.end ())
return i->second;
return "";
}
////////////////////////////////////////////////////////////////////////////////
void Msg::setPayload (const std::string& payload)
{
_payload = payload;
}
////////////////////////////////////////////////////////////////////////////////
std::string Msg::getPayload () const
{
return _payload;
}
////////////////////////////////////////////////////////////////////////////////
std::vector <std::string> Msg::all () const
{
std::vector <std::string> names;
for (auto& i : _header)
names.push_back (i.first);
return names;
}
////////////////////////////////////////////////////////////////////////////////
std::string Msg::serialize () const
{
std::string output;
for (auto& i : _header)
output += i.first + ": " + i.second + "\n";
output += "\n" + _payload + "\n";
return output;
}
////////////////////////////////////////////////////////////////////////////////
bool Msg::parse (const std::string& input)
{
_header.clear ();
_payload = "";
auto separator = input.find ("\n\n");
if (separator == std::string::npos)
throw std::string ("ERROR: Malformed message");
// Parse header.
std::vector <std::string> lines;
split (lines, input.substr (0, separator), '\n');
for (auto& i : lines)
{
auto delimiter = i.find (':');
if (delimiter == std::string::npos)
throw std::string ("ERROR: Malformed message header '") + i + "'";
_header[Lexer::trim (i.substr (0, delimiter))] = Lexer::trim (i.substr (delimiter + 1));
}
// Parse payload.
_payload = input.substr (separator + 2);
return true;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,53 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_MSG
#define INCLUDED_MSG
#include <map>
#include <vector>
#include <string>
class Msg
{
public:
void set (const std::string&, const std::string&);
std::string get (const std::string&) const;
void setPayload (const std::string&);
std::string getPayload () const;
std::vector <std::string> all () const;
std::string serialize () const;
bool parse (const std::string&);
private:
std::map <std::string, std::string> _header {};
std::string _payload {""};
};
#endif

View File

@@ -1,626 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Nibbler.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <Lexer.h>
#include <util.h>
static const char* _uuid_pattern = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
static const unsigned int _uuid_min_length = 8;
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler ()
: _length (0)
, _cursor (0)
, _saved (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler (const std::string& input)
: _input (std::make_shared <std::string> (input))
, _length (input.length ())
, _cursor (0)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::Nibbler (const Nibbler& other)
: _input (other._input)
, _length (other._length)
, _cursor (other._cursor)
{
}
////////////////////////////////////////////////////////////////////////////////
Nibbler& Nibbler::operator= (const Nibbler& other)
{
if (this != &other)
{
_input = other._input;
_length = other._length;
_cursor = other._cursor;
}
return *this;
}
////////////////////////////////////////////////////////////////////////////////
Nibbler::~Nibbler ()
{
}
////////////////////////////////////////////////////////////////////////////////
// Extract up until the next c (but not including) or EOS.
bool Nibbler::getUntil (char c, std::string& result)
{
if (_cursor < _length)
{
auto i = _input->find (c, _cursor);
if (i != std::string::npos)
{
result = _input->substr (_cursor, i - _cursor);
_cursor = i;
}
else
{
result = _input->substr (_cursor);
_cursor = _length;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntil (const std::string& terminator, std::string& result)
{
if (_cursor < _length)
{
auto i = _input->find (terminator, _cursor);
if (i != std::string::npos)
{
result = _input->substr (_cursor, i - _cursor);
_cursor = i;
}
else
{
result = _input->substr (_cursor);
_cursor = _length;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilOneOf (const std::string& chars, std::string& result)
{
if (_cursor < _length)
{
auto i = _input->find_first_of (chars, _cursor);
if (i != std::string::npos)
{
result = _input->substr (_cursor, i - _cursor);
_cursor = i;
}
else
{
result = _input->substr (_cursor);
_cursor = _length;
}
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilWS (std::string& result)
{
return this->getUntilOneOf (" \t\r\n\f", result);
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUntilEOS (std::string& result)
{
if (_cursor < _length)
{
result = _input->substr (_cursor);
_cursor = _length;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getN (const int quantity, std::string& result)
{
if (_cursor + quantity <= _length)
{
result = _input->substr (_cursor, quantity);
_cursor += quantity;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Gets quote content: "foobar" -> foobar (for c = '"')
// Handles escaped quotes: "foo\"bar" -> foo\"bar (for c = '"')
// Returns false if first character is not c, or if there is no closing c
bool Nibbler::getQuoted (char c, std::string& result)
{
result = "";
if (_cursor >= _length ||
(*_input)[_cursor] != c)
{
return false;
}
std::string::size_type start = _cursor + 1; // Skip first quote char
std::string::size_type i = start;
while (i < _length)
{
i = (*_input).find (c, i);
if (i == std::string::npos)
return false; // Unclosed quote
if (i == start)
{
// Empty quote
_cursor += 2; // Skip both quote chars
return true;
}
if ((*_input)[i-1] == '\\')
{
// Check for escaped backslashes. Backtracking like this is not very
// efficient, but is only done in extreme corner cases.
auto j = i-2; // Start one character further left
bool is_escaped_quote = true;
while (j >= start && (*_input)[j] == '\\')
{
// Toggle flag for each further backslash encountered.
is_escaped_quote = is_escaped_quote ? false : true;
--j;
}
if (is_escaped_quote)
{
// Keep searching
++i;
continue;
}
}
// None of the above applied, we must have found the closing quote char.
result.assign ((*_input), start, i - start);
_cursor = i + 1; // Skip closing quote char
return true;
}
// This should never be reached. We could throw here instead.
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getDigit (int& result)
{
if (_cursor < _length &&
Lexer::isDigit ((*_input)[_cursor]))
{
result = (*_input)[_cursor++] - '0';
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getDigit4 (int& result)
{
auto i = _cursor;
if (i < _length &&
_length - i >= 4)
{
if (Lexer::isDigit ((*_input)[i + 0]) &&
Lexer::isDigit ((*_input)[i + 1]) &&
Lexer::isDigit ((*_input)[i + 2]) &&
Lexer::isDigit ((*_input)[i + 3]))
{
result = strtoimax (_input->substr (_cursor, 4).c_str (), NULL, 10);
_cursor += 4;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getDigit3 (int& result)
{
auto i = _cursor;
if (i < _length &&
_length - i >= 3)
{
if (Lexer::isDigit ((*_input)[i + 0]) &&
Lexer::isDigit ((*_input)[i + 1]) &&
Lexer::isDigit ((*_input)[i + 2]))
{
result = strtoimax (_input->substr (_cursor, 3).c_str (), NULL, 10);
_cursor += 3;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getDigit2 (int& result)
{
auto i = _cursor;
if (i < _length &&
_length - i >= 2)
{
if (Lexer::isDigit ((*_input)[i + 0]) &&
Lexer::isDigit ((*_input)[i + 1]))
{
result = strtoimax (_input->substr (_cursor, 2).c_str (), NULL, 10);
_cursor += 2;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getInt (int& result)
{
auto i = _cursor;
if (i < _length)
{
if ((*_input)[i] == '-')
++i;
else if ((*_input)[i] == '+')
++i;
}
// TODO Potential for use of find_first_not_of
while (i < _length && Lexer::isDigit ((*_input)[i]))
++i;
if (i > _cursor)
{
result = strtoimax (_input->substr (_cursor, i - _cursor).c_str (), NULL, 10);
_cursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getUnsignedInt (int& result)
{
auto i = _cursor;
// TODO Potential for use of find_first_not_of
while (i < _length && Lexer::isDigit ((*_input)[i]))
++i;
if (i > _cursor)
{
result = strtoimax (_input->substr (_cursor, i - _cursor).c_str (), NULL, 10);
_cursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// number:
// int frac? exp?
//
// int:
// (-|+)? digit+
//
// frac:
// . digit+
//
// exp:
// e digit+
//
// e:
// e|E (+|-)?
//
bool Nibbler::getNumber (std::string& result)
{
auto i = _cursor;
// [+-]?
if (i < _length && ((*_input)[i] == '-' || (*_input)[i] == '+'))
++i;
// digit+
if (i < _length && Lexer::isDigit ((*_input)[i]))
{
++i;
while (i < _length && Lexer::isDigit ((*_input)[i]))
++i;
// ( . digit+ )?
if (i < _length && (*_input)[i] == '.')
{
++i;
while (i < _length && Lexer::isDigit ((*_input)[i]))
++i;
}
// ( [eE] [+-]? digit+ )?
if (i < _length && ((*_input)[i] == 'e' || (*_input)[i] == 'E'))
{
++i;
if (i < _length && ((*_input)[i] == '+' || (*_input)[i] == '-'))
++i;
if (i < _length && Lexer::isDigit ((*_input)[i]))
{
++i;
while (i < _length && Lexer::isDigit ((*_input)[i]))
++i;
result = _input->substr (_cursor, i - _cursor);
_cursor = i;
return true;
}
return false;
}
result = _input->substr (_cursor, i - _cursor);
_cursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getNumber (double &result)
{
std::string s;
if (getNumber (s))
{
result = strtof (s.c_str (), NULL);
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getLiteral (const std::string& literal)
{
if (_cursor < _length &&
_input->find (literal, _cursor) == _cursor)
{
_cursor += literal.length ();
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::getPartialUUID (std::string& result)
{
std::string::size_type i;
for (i = 0; i < 36 && i < (_length - _cursor); i++)
{
if (_uuid_pattern[i] == 'x' && !isxdigit ((*_input)[_cursor + i]))
break;
else if (_uuid_pattern[i] == '-' && (*_input)[_cursor + i] != '-')
break;
}
// If the partial match found is long enough, consider it a match.
if (i >= _uuid_min_length)
{
// Fail if there is another hex digit.
if (_cursor + i < _length &&
isxdigit ((*_input)[_cursor + i]))
return false;
result = _input->substr (_cursor, i);
_cursor += i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Assumes that the options are sorted by decreasing length, so that if the
// options contain 'fourteen' and 'four', the stream is first matched against
// the longer entry.
bool Nibbler::getOneOf (
const std::vector <std::string>& options,
std::string& found)
{
for (auto& option : options)
{
if (getLiteral (option))
{
found = option;
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skipN (const int quantity /* = 1 */)
{
if (_cursor < _length &&
_cursor <= _length - quantity)
{
_cursor += quantity;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skip (char c)
{
if (_cursor < _length &&
(*_input)[_cursor] == c)
{
++_cursor;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skipWS ()
{
return this->skipAllOneOf (" \t\n\r\f");
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::skipAllOneOf (const std::string& chars)
{
if (_cursor < _length)
{
auto i = _input->find_first_not_of (chars, _cursor);
if (i == _cursor)
return false;
if (i == std::string::npos)
_cursor = _length; // Yes, off the end.
else
_cursor = i;
return true;
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
// Peeks ahead - does not move cursor.
char Nibbler::next ()
{
if (_cursor < _length)
return (*_input)[_cursor];
return '\0';
}
////////////////////////////////////////////////////////////////////////////////
std::string::size_type Nibbler::cursor ()
{
return _cursor;
}
////////////////////////////////////////////////////////////////////////////////
// Peeks ahead - does not move cursor.
std::string Nibbler::next (const int quantity)
{
if ( _cursor < _length &&
(unsigned) quantity <= _length &&
_cursor <= _length - quantity)
return _input->substr (_cursor, quantity);
return "";
}
////////////////////////////////////////////////////////////////////////////////
std::string::size_type Nibbler::save ()
{
return _saved = _cursor;
}
////////////////////////////////////////////////////////////////////////////////
std::string::size_type Nibbler::restore ()
{
return _cursor = _saved;
}
////////////////////////////////////////////////////////////////////////////////
const std::string& Nibbler::str () const
{
return *_input;
}
////////////////////////////////////////////////////////////////////////////////
bool Nibbler::depleted ()
{
if (_cursor >= _length)
return true;
return false;
}
////////////////////////////////////////////////////////////////////////////////
std::string Nibbler::dump ()
{
return std::string ("Nibbler ")
+ _input->substr (_cursor)
+ "";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,89 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_NIBBLER
#define INCLUDED_NIBBLER
#include <string>
#include <vector>
#include <time.h>
#include <memory>
class Nibbler
{
public:
Nibbler (); // Default constructor
Nibbler (const std::string&); // Constructor
Nibbler (const Nibbler&); // Copy constructor
Nibbler& operator= (const Nibbler&); // Assignment operator
~Nibbler (); // Destructor
bool getUntil (char, std::string&);
bool getUntil (const std::string&, std::string&);
bool getUntilOneOf (const std::string&, std::string&);
bool getUntilWS (std::string&);
bool getUntilEOS (std::string&);
bool getN (const int, std::string&);
bool getQuoted (char, std::string&);
bool getDigit (int&);
bool getDigit4 (int&);
bool getDigit3 (int&);
bool getDigit2 (int&);
bool getInt (int&);
bool getUnsignedInt (int&);
bool getNumber (std::string&);
bool getNumber (double&);
bool getLiteral (const std::string&);
bool getPartialUUID (std::string&);
bool getOneOf (const std::vector <std::string>&, std::string&);
bool skipN (const int quantity = 1);
bool skip (char);
bool skipAllOneOf (const std::string&);
bool skipWS ();
char next ();
std::string next (const int quantity);
std::string::size_type cursor ();
std::string::size_type save ();
std::string::size_type restore ();
const std::string& str () const;
bool depleted ();
std::string dump ();
private:
std::shared_ptr<std::string> _input;
std::string::size_type _length;
std::string::size_type _cursor;
std::string::size_type _saved;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,159 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <RX.h>
#include <cstdlib>
#include <cstring>
////////////////////////////////////////////////////////////////////////////////
RX::RX ()
{
}
////////////////////////////////////////////////////////////////////////////////
RX::RX (
const std::string& pattern,
bool case_sensitive /* = true */)
: _compiled (false)
, _pattern (pattern)
, _case_sensitive (case_sensitive)
{
compile ();
}
////////////////////////////////////////////////////////////////////////////////
RX::RX (const RX& other)
{
_compiled = false;
_pattern = other._pattern;
_case_sensitive = other._case_sensitive;
}
////////////////////////////////////////////////////////////////////////////////
RX::~RX ()
{
if (_compiled)
regfree (&_regex);
}
////////////////////////////////////////////////////////////////////////////////
RX& RX::operator= (const RX& other)
{
_compiled = false;
_pattern = other._pattern;
_case_sensitive = other._case_sensitive;
return *this;
}
////////////////////////////////////////////////////////////////////////////////
void RX::compile ()
{
if (! _compiled)
{
memset (&_regex, 0, sizeof (regex_t));
int result;
if ((result = regcomp (&_regex, _pattern.c_str (),
#if defined REG_ENHANCED
REG_ENHANCED | REG_EXTENDED | REG_NEWLINE |
#else
REG_EXTENDED | REG_NEWLINE |
#endif
(_case_sensitive ? 0 : REG_ICASE))) != 0)
{
char message[256];
regerror (result, &_regex, message, 256);
throw std::string (message);
}
_compiled = true;
}
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (const std::string& in)
{
if (! _compiled)
compile ();
return regexec (&_regex, in.c_str (), 0, nullptr, 0) == 0 ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (
std::vector<std::string>& matches,
const std::string& in)
{
if (! _compiled)
compile ();
regmatch_t rm[2];
int offset = 0;
int length = in.length ();
while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
offset < length)
{
matches.push_back (in.substr (rm[0].rm_so + offset, rm[0].rm_eo - rm[0].rm_so));
offset += rm[0].rm_eo;
// Protection against zero-width patterns causing infinite loops.
if (rm[0].rm_so == rm[0].rm_eo)
++offset;
}
return matches.size () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
bool RX::match (
std::vector <int>& start,
std::vector <int>& end,
const std::string& in)
{
if (! _compiled)
compile ();
regmatch_t rm[2];
int offset = 0;
int length = in.length ();
while (regexec (&_regex, in.c_str () + offset, 2, &rm[0], 0) == 0 &&
offset < length)
{
start.push_back (rm[0].rm_so + offset);
end.push_back (rm[0].rm_eo + offset);
offset += rm[0].rm_eo;
// Protection against zero-width patterns causing infinite loops.
if (rm[0].rm_so == rm[0].rm_eo)
++offset;
}
return start.size () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,58 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_RX
#define INCLUDED_RX
#include <string>
#include <vector>
#include <regex.h>
class RX
{
public:
RX ();
RX (const std::string&, bool caseSensitive = true);
RX (const RX&);
~RX ();
RX& operator= (const RX&);
bool match (const std::string&);
bool match (std::vector<std::string>&, const std::string&);
bool match (std::vector <int>&, std::vector <int>&, const std::string&);
private:
void compile ();
private:
bool _compiled {false};
std::string _pattern {};
bool _case_sensitive {false};
regex_t _regex;
};
#endif

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -35,13 +35,14 @@
#include <signal.h>
#include <Context.h>
#include <Color.h>
#include <ISO8601.h>
#include <i18n.h>
#include <text.h>
#include <util.h>
#include <Datetime.h>
#include <Table.h>
#include <shared.h>
#include <format.h>
#include <main.h>
#include <util.h>
extern Context context;
#define STRING_TDB2_REVERTED "Modified task reverted."
bool TDB2::debug_mode = false;
@@ -60,8 +61,7 @@ TF2::TF2 ()
TF2::~TF2 ()
{
if (_dirty && TDB2::debug_mode)
std::cout << format (STRING_TDB2_DIRTY_EXIT, std::string (_file))
<< "\n";
std::cout << format ("Exiting with unwritten changes to {1}\n", std::string (_file));
}
////////////////////////////////////////////////////////////////////////////////
@@ -169,7 +169,7 @@ void TF2::add_task (Task& task)
_added_tasks.push_back (task); // For commit/synch
// For faster lookup
if (context.cli2.getCommand () == "import")
if (Context::getContext ().cli2.getCommand () == "import")
_tasks_map.insert (std::pair<std::string, Task> (task.get("uuid"), task));
Task::status status = task.getStatus ();
@@ -178,7 +178,7 @@ void TF2::add_task (Task& task)
status == Task::recurring ||
status == Task::waiting))
{
task.id = context.tdb2.next_id ();
task.id = Context::getContext ().tdb2.next_id ();
}
_I2U[task.id] = task.get ("uuid");
@@ -192,7 +192,7 @@ bool TF2::modify_task (const Task& task)
{
std::string uuid = task.get ("uuid");
if (context.cli2.getCommand () == "import")
if (Context::getContext ().cli2.getCommand () == "import")
{
// Update map used for faster lookup
auto i = _tasks_map.find (uuid);
@@ -218,6 +218,21 @@ bool TF2::modify_task (const Task& task)
return false;
}
////////////////////////////////////////////////////////////////////////////////
bool TF2::purge_task (const Task& task)
{
// Bail out if task is not found in this file
std::string uuid = task.get ("uuid");
if (!has (uuid))
return false;
// Mark the task to be purged
_purged_tasks.insert (uuid);
_dirty = true;
return true;
}
////////////////////////////////////////////////////////////////////////////////
void TF2::add_line (const std::string& line)
{
@@ -248,12 +263,12 @@ void TF2::commit ()
if (_dirty)
{
// Special case: added but no modified means just append to the file.
if (!_modified_tasks.size () &&
if (!_modified_tasks.size () && !_purged_tasks.size () &&
(_added_tasks.size () || _added_lines.size ()))
{
if (_file.open ())
{
if (context.config.getBoolean ("locking"))
if (Context::getContext ().config.getBoolean ("locking"))
_file.lock ();
// Write out all the added tasks.
@@ -275,7 +290,7 @@ void TF2::commit ()
{
if (_file.open ())
{
if (context.config.getBoolean ("locking"))
if (Context::getContext ().config.getBoolean ("locking"))
_file.lock ();
// Truncate the file and rewrite.
@@ -284,7 +299,9 @@ void TF2::commit ()
// Only write out _tasks, because any deltas have already been applied.
_file.append (std::string("")); // Seek to end of file
for (auto& task : _tasks)
_file.write_raw (task.composeF4 () + "\n");
// Skip over the tasks that are marked to be purged
if (_purged_tasks.find (task.get ("uuid")) == _purged_tasks.end ())
_file.write_raw (task.composeF4 () + '\n');
// Write out all the added lines.
_file.append (_added_lines);
@@ -308,9 +325,9 @@ Task TF2::load_task (const std::string& line)
{
Task::status status = task.getStatus ();
// Completed / deleted tasks in pending.data get an ID if GC is off.
if (! context.run_gc ||
if (! Context::getContext ().run_gc ||
(status != Task::completed && status != Task::deleted))
task.id = context.tdb2.next_id ();
task.id = Context::getContext ().tdb2.next_id ();
}
// Maintain mapping for ease of link/dependency resolution.
@@ -330,41 +347,41 @@ Task TF2::load_task (const std::string& line)
// or needs to be 'woken'.
void TF2::load_gc (Task& task)
{
ISO8601d now;
Datetime now;
std::string status = task.get ("status");
if (status == "pending" ||
status == "recurring")
{
context.tdb2.pending._tasks.push_back (task);
Context::getContext ().tdb2.pending._tasks.push_back (task);
}
else if (status == "waiting")
{
ISO8601d wait (task.get_date ("wait"));
Datetime wait (task.get_date ("wait"));
if (wait < now)
{
task.set ("status", "pending");
task.remove ("wait");
// Unwaiting pending tasks is the only case not caught by the size()
// checks in TDB2::gc(), so we need to signal it here.
context.tdb2.pending._dirty = true;
Context::getContext ().tdb2.pending._dirty = true;
if (context.verbose ("unwait"))
context.footnote (format (STRING_TDB2_UNWAIT, task.get ("description")));
if (Context::getContext ().verbose ("unwait"))
Context::getContext ().footnote (format ("Un-waiting task {1} '{2}'", task.id, task.get ("description")));
}
context.tdb2.pending._tasks.push_back (task);
Context::getContext ().tdb2.pending._tasks.push_back (task);
}
else
{
context.tdb2.completed._tasks.push_back (task);
Context::getContext ().tdb2.completed._tasks.push_back (task);
}
}
////////////////////////////////////////////////////////////////////////////////
void TF2::load_tasks (bool from_gc /* = false */)
{
context.timer_load.start ();
Timer timer;
if (! _loaded_lines)
{
@@ -392,7 +409,7 @@ void TF2::load_tasks (bool from_gc /* = false */)
else
_tasks.push_back (task);
if (context.cli2.getCommand () == "import") // For faster lookup only
if (Context::getContext ().cli2.getCommand () == "import") // For faster lookup only
_tasks_map.insert (std::pair<std::string, Task> (task.get("uuid"), task));
}
@@ -405,10 +422,10 @@ void TF2::load_tasks (bool from_gc /* = false */)
catch (const std::string& e)
{
throw e + format (STRING_TDB2_PARSE_ERROR, _file._data, line_number);
throw e + format (" in {1} at line {2}", _file._data, line_number);
}
context.timer_load.stop ();
Context::getContext ().time_load_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -416,7 +433,7 @@ void TF2::load_lines ()
{
if (_file.open ())
{
if (context.config.getBoolean ("locking"))
if (Context::getContext ().config.getBoolean ("locking"))
_file.lock ();
_file.read (_lines);
@@ -492,6 +509,7 @@ void TF2::clear ()
_tasks.clear ();
_added_tasks.clear ();
_modified_tasks.clear ();
_purged_tasks.clear ();
_lines.clear ();
_added_lines.clear ();
_I2U.clear ();
@@ -509,10 +527,7 @@ void TF2::dependency_scan ()
{
if (left.has ("depends"))
{
std::vector <std::string> deps;
left.getDependencies (deps);
for (auto& dep : deps)
for (auto& dep : left.getDependencyUUIDs ())
{
for (auto& right : _tasks)
{
@@ -553,8 +568,8 @@ const std::string TF2::dump ()
label = rightJustify (_file._data.substr (slash + 1), 14);
// File mode.
std::string mode = std::string (_file.readable () ? "r" : "-") +
std::string (_file.writable () ? "w" : "-");
std::string mode = std::string (_file.exists () && _file.readable () ? "r" : "-") +
std::string (_file.exists () && _file.writable () ? "w" : "-");
if (mode == "r-") mode = red.colorize (mode);
else if (mode == "rw") mode = green.colorize (mode);
else mode = yellow.colorize (mode);
@@ -565,17 +580,19 @@ const std::string TF2::dump ()
std::string tasks = green.colorize (rightJustifyZero ((int) _tasks.size (), 4));
std::string tasks_added = red.colorize (rightJustifyZero ((int) _added_tasks.size (), 3));
std::string tasks_modified = yellow.colorize (rightJustifyZero ((int) _modified_tasks.size (), 3));
std::string tasks_purged = red.colorize (rightJustifyZero ((int) _purged_tasks.size (), 3));
std::string lines = green.colorize (rightJustifyZero ((int) _lines.size (), 4));
std::string lines_added = red.colorize (rightJustifyZero ((int) _added_lines.size (), 3));
char buffer[256]; // Composed string is actually 246 bytes. Yikes.
snprintf (buffer, 256, "%14s %s %s T%s+%s~%s L%s+%s",
snprintf (buffer, 256, "%14s %s %s T%s+%s~%s-%s L%s+%s",
label.c_str (),
mode.c_str (),
hygiene.c_str (),
tasks.c_str (),
tasks_added.c_str (),
tasks_modified.c_str (),
tasks_purged.c_str (),
lines.c_str (),
lines_added.c_str ());
@@ -621,12 +638,12 @@ void TDB2::add (Task& task, bool add_to_backlog /* = true */)
// If the tasks are loaded, then verify that this uuid is not already in
// the file.
if (!verifyUniqueUUID (uuid))
throw format (STRING_TDB2_UUID_NOT_UNIQUE, uuid);
throw format ("Cannot add task because the uuid '{1}' is not unique.", uuid);
// Only locally-added tasks trigger hooks. This means that tasks introduced
// via 'sync' do not trigger hooks.
if (add_to_backlog)
context.hooks.onAdd (task);
Context::getContext ().hooks.onAdd (task);
update (task, add_to_backlog, true);
}
@@ -643,12 +660,19 @@ void TDB2::modify (Task& task, bool add_to_backlog /* = true */)
{
Task original;
get (uuid, original);
context.hooks.onModify (original, task);
Context::getContext ().hooks.onModify (original, task);
}
update (task, add_to_backlog);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::purge (Task& task)
{
// Delete the task from completed.data
completed.purge_task (task);
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::update (
Task& task,
@@ -681,9 +705,9 @@ void TDB2::update (
// old <task>
// new <task>
// ---
undo.add_line ("time " + ISO8601d ().toEpochString () + "\n");
undo.add_line ("old " + original.composeF4 () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("time " + Datetime ().toEpochString () + '\n');
undo.add_line ("old " + original.composeF4 () + '\n');
undo.add_line ("new " + task.composeF4 () + '\n');
undo.add_line ("---\n");
}
else
@@ -700,19 +724,21 @@ void TDB2::update (
// time <time>
// new <task>
// ---
undo.add_line ("time " + ISO8601d ().toEpochString () + "\n");
undo.add_line ("new " + task.composeF4 () + "\n");
undo.add_line ("time " + Datetime ().toEpochString () + '\n');
undo.add_line ("new " + task.composeF4 () + '\n');
undo.add_line ("---\n");
}
// Add task to backlog.
if (add_to_backlog)
backlog.add_line (task.composeJSON () + "\n");
backlog.add_line (task.composeJSON () + '\n');
}
////////////////////////////////////////////////////////////////////////////////
void TDB2::commit ()
{
Timer timer;
// Ignore harmful signals.
signal (SIGHUP, SIG_IGN);
signal (SIGINT, SIG_IGN);
@@ -722,10 +748,7 @@ void TDB2::commit ()
signal (SIGUSR2, SIG_IGN);
dump ();
context.timer_commit.start ();
gather_changes ();
pending.commit ();
completed.commit ();
undo.commit ();
@@ -739,7 +762,7 @@ void TDB2::commit ()
signal (SIGUSR1, SIG_DFL);
signal (SIGUSR2, SIG_DFL);
context.timer_commit.stop ();
Context::getContext ().time_commit_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -778,8 +801,8 @@ void TDB2::revert ()
// Display diff and confirm.
show_diff (current, prior, when);
if (! context.config.getBoolean ("confirmation") ||
confirm (STRING_TDB2_UNDO_CONFIRM))
if (! Context::getContext ().config.getBoolean ("confirmation") ||
confirm ("The undo command is not reversible. Are you sure you want to revert to the previous state?"))
{
// There are six kinds of change possible. Determine which one, and act
// accordingly.
@@ -834,7 +857,7 @@ void TDB2::revert ()
File::write (backlog._file._data, b);
}
else
std::cout << STRING_CMD_CONFIG_NO_CHANGE << "\n";
std::cout << "No changes made.\n";
}
////////////////////////////////////////////////////////////////////////////////
@@ -846,7 +869,7 @@ void TDB2::revert_undo (
std::string& prior)
{
if (u.size () < 3)
throw std::string (STRING_TDB2_NO_UNDO);
throw std::string ("There are no recorded transactions to undo.");
// pop last tx
u.pop_back (); // separator.
@@ -873,7 +896,7 @@ void TDB2::revert_undo (
if (uuidAtt != std::string::npos)
uuid = current.substr (uuidAtt + 6, 36); // "uuid:"<uuid>" --> <uuid>
else
throw std::string (STRING_TDB2_MISSING_UUID);
throw std::string ("Cannot locate UUID in task to undo.");
}
////////////////////////////////////////////////////////////////////////////////
@@ -882,25 +905,25 @@ void TDB2::revert_pending (
const std::string& uuid,
const std::string& prior)
{
std::string uuid_att = "uuid:\"" + uuid + "\"";
std::string uuid_att = "uuid:\"" + uuid + '"';
// is 'current' in pending?
for (auto task = p.begin (); task != p.end (); ++task)
{
if (task->find (uuid_att) != std::string::npos)
{
context.debug ("TDB::revert - task found in pending.data");
Context::getContext ().debug ("TDB::revert - task found in pending.data");
// Either revert if there was a prior state, or remove the task.
if (prior != "")
{
*task = prior;
std::cout << STRING_TDB2_REVERTED << "\n";
std::cout << STRING_TDB2_REVERTED << '\n';
}
else
{
p.erase (task);
std::cout << STRING_TDB2_REMOVED << "\n";
std::cout << "Task removed.\n";
}
break;
@@ -915,14 +938,14 @@ void TDB2::revert_completed (
const std::string& uuid,
const std::string& prior)
{
std::string uuid_att = "uuid:\"" + uuid + "\"";
std::string uuid_att = "uuid:\"" + uuid + '"';
// is 'current' in completed?
for (auto task = c.begin (); task != c.end (); ++task)
{
if (task->find (uuid_att) != std::string::npos)
{
context.debug ("TDB::revert_completed - task found in completed.data");
Context::getContext ().debug ("TDB::revert_completed - task found in completed.data");
// Either revert if there was a prior state, or remove the task.
if (prior != "")
@@ -934,24 +957,24 @@ void TDB2::revert_completed (
{
c.erase (task);
p.push_back (prior);
std::cout << STRING_TDB2_REVERTED << "\n";
context.debug ("TDB::revert_completed - task belongs in pending.data");
std::cout << STRING_TDB2_REVERTED << '\n';
Context::getContext ().debug ("TDB::revert_completed - task belongs in pending.data");
}
else
{
std::cout << STRING_TDB2_REVERTED << "\n";
context.debug ("TDB::revert_completed - task belongs in completed.data");
std::cout << STRING_TDB2_REVERTED << '\n';
Context::getContext ().debug ("TDB::revert_completed - task belongs in completed.data");
}
}
else
{
c.erase (task);
std::cout << STRING_TDB2_REVERTED << "\n";
context.debug ("TDB::revert_completed - task removed");
std::cout << STRING_TDB2_REVERTED << '\n';
Context::getContext ().debug ("TDB::revert_completed - task removed");
}
std::cout << STRING_TDB2_UNDO_COMPLETE << "\n";
std::cout << "Undo complete.\n";
break;
}
}
@@ -964,14 +987,14 @@ void TDB2::revert_backlog (
const std::string& current,
const std::string& prior)
{
std::string uuid_att = "\"uuid\":\"" + uuid + "\"";
std::string uuid_att = "\"uuid\":\"" + uuid + '"';
bool found = false;
for (auto task = b.rbegin (); task != b.rend (); ++task)
{
if (task->find (uuid_att) != std::string::npos)
{
context.debug ("TDB::revert_backlog - task found in backlog.data");
Context::getContext ().debug ("TDB::revert_backlog - task found in backlog.data");
found = true;
// If this is a new task (no prior), then just remove it from the backlog.
@@ -994,7 +1017,7 @@ void TDB2::revert_backlog (
}
if (!found)
throw std::string (STRING_TDB2_UNDO_SYNCED);
throw std::string ("Cannot undo change because the task was already synced. Modify the task instead.");
}
////////////////////////////////////////////////////////////////////////////////
@@ -1003,29 +1026,27 @@ void TDB2::show_diff (
const std::string& prior,
const std::string& when)
{
ISO8601d lastChange (strtol (when.c_str (), NULL, 10));
Datetime lastChange (strtol (when.c_str (), nullptr, 10));
// Set the colors.
Color color_red (context.color () ? context.config.get ("color.undo.before") : "");
Color color_green (context.color () ? context.config.get ("color.undo.after") : "");
Color color_red (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.before") : "");
Color color_green (Context::getContext ().color () ? Context::getContext ().config.get ("color.undo.after") : "");
if (context.config.get ("undo.style") == "side")
if (Context::getContext ().config.get ("undo.style") == "side")
{
std::cout << "\n"
<< format (STRING_TDB2_LAST_MOD, lastChange.toString ())
<< "\n";
std::cout << '\n'
<< format ("The last modification was made {1}", lastChange.toString ())
<< '\n';
// Attributes are all there is, so figure the different attribute names
// between before and after.
ViewText view;
view.width (context.getWidth ());
Table view;
view.width (Context::getContext ().getWidth ());
view.intraPadding (2);
view.add (Column::factory ("string", ""));
view.add (Column::factory ("string", STRING_TDB2_UNDO_PRIOR));
view.add (Column::factory ("string", STRING_TDB2_UNDO_CURRENT));
Color label (context.config.get ("color.label"));
view.colorHeader (label);
view.add ("");
view.add ("Prior Values");
view.add ("Current Values");
setHeaderUnderline (view);
Task after (current);
@@ -1087,9 +1108,9 @@ void TDB2::show_diff (
}
}
std::cout << "\n"
std::cout << '\n'
<< view.render ()
<< "\n";
<< '\n';
}
// This style looks like this:
@@ -1105,7 +1126,7 @@ void TDB2::show_diff (
// - name:
// + name: new // att added
//
else if (context.config.get ("undo.style") == "diff")
else if (Context::getContext ().config.get ("undo.style") == "diff")
{
// Create reference tasks.
Task before;
@@ -1115,26 +1136,26 @@ void TDB2::show_diff (
Task after (current);
// Generate table header.
ViewText view;
view.width (context.getWidth ());
Table view;
view.width (Context::getContext ().getWidth ());
view.intraPadding (2);
view.add (Column::factory ("string", ""));
view.add (Column::factory ("string", ""));
view.add ("");
view.add ("");
int row = view.addRow ();
view.set (row, 0, STRING_TDB2_DIFF_PREV, color_red);
view.set (row, 1, STRING_TDB2_DIFF_PREV_DESC, color_red);
view.set (row, 0, "--- previous state", color_red);
view.set (row, 1, "Undo will restore this state", color_red);
row = view.addRow ();
view.set (row, 0, STRING_TDB2_DIFF_CURR, color_green); // Note trailing space.
view.set (row, 1, format (STRING_TDB2_DIFF_CURR_DESC,
lastChange.toString (context.config.get ("dateformat"))),
view.set (row, 0, "+++ current state ", color_green);
view.set (row, 1, format ("Change made {1}",
lastChange.toString (Context::getContext ().config.get ("dateformat"))),
color_green);
view.addRow ();
// Add rows to table showing diffs.
std::vector <std::string> all = context.getColumns ();
std::vector <std::string> all = Context::getContext ().getColumns ();
// Now factor in the annotation attributes.
for (auto& it : before.data)
@@ -1176,21 +1197,21 @@ void TDB2::show_diff (
else if (before_att != "" && after_att == "")
{
row = view.addRow ();
view.set (row, 0, "-" + a + ":", color_red);
view.set (row, 0, '-' + a + ':', color_red);
view.set (row, 1, before_att, color_red);
row = view.addRow ();
view.set (row, 0, "+" + a + ":", color_green);
view.set (row, 0, '+' + a + ':', color_green);
}
// Attribute added.
else if (before_att == "" && after_att != "")
{
row = view.addRow ();
view.set (row, 0, "-" + a + ":", color_red);
view.set (row, 0, '-' + a + ':', color_red);
row = view.addRow ();
view.set (row, 0, "+" + a + ":", color_green);
view.set (row, 0, '+' + a + ':', color_green);
view.set (row, 1, after_att, color_green);
}
@@ -1198,19 +1219,19 @@ void TDB2::show_diff (
else
{
row = view.addRow ();
view.set (row, 0, "-" + a + ":", color_red);
view.set (row, 0, '-' + a + ':', color_red);
view.set (row, 1, before_att, color_red);
row = view.addRow ();
view.set (row, 0, "+" + a + ":", color_green);
view.set (row, 0, '+' + a + ':', color_green);
view.set (row, 1, after_att, color_green);
}
}
}
std::cout << "\n"
std::cout << '\n'
<< view.render ()
<< "\n";
<< '\n';
}
}
@@ -1226,11 +1247,10 @@ void TDB2::show_diff (
// - waiting task in pending that needs to be un-waited
void TDB2::gc ()
{
context.timer_gc.start ();
unsigned long load_start = context.timer_load.total ();
Timer timer;
// Allowed as an override, but not recommended.
if (context.config.getBoolean ("gc"))
if (Context::getContext ().config.getBoolean ("gc"))
{
// Load pending, check whether completed changes size
auto size_before = completed._tasks.size ();
@@ -1264,10 +1284,7 @@ void TDB2::gc ()
completed.dependency_scan ();
}
// Stop and remove accumulated load time from the GC time, because they
// overlap.
context.timer_gc.stop ();
context.timer_gc.subtract (context.timer_load.total () - load_start);
Context::getContext ().time_gc_us += timer.total_us ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -1381,7 +1398,7 @@ const std::vector <Task> TDB2::children (Task& task)
if (i.getStatus () != Task::completed &&
i.getStatus () != Task::deleted)
{
// If task has the same parent, it is a sibling.
// If task has the given task as a parent, it is a child task.
if (i.get ("parent") == parent)
results.push_back (i);
}
@@ -1444,13 +1461,13 @@ void TDB2::clear ()
////////////////////////////////////////////////////////////////////////////////
void TDB2::dump ()
{
if (context.config.getBoolean ("debug"))
if (Context::getContext ().config.getBoolean ("debug"))
{
context.debug (pending.dump ());
context.debug (completed.dump ());
context.debug (undo.dump ());
context.debug (backlog.dump ());
context.debug (" ");
Context::getContext ().debug (pending.dump ());
Context::getContext ().debug (completed.dump ());
Context::getContext ().debug (undo.dump ());
Context::getContext ().debug (backlog.dump ());
Context::getContext ().debug (" ");
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,11 +28,11 @@
#define INCLUDED_TDB2
#include <map>
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
#include <stdio.h>
#include <ViewText.h>
#include <FS.h>
#include <Task.h>
@@ -54,6 +54,7 @@ public:
void add_task (Task&);
bool modify_task (const Task&);
bool purge_task (const Task&);
void add_line (const std::string&);
void clear_tasks ();
void clear_lines ();
@@ -90,6 +91,7 @@ public:
std::vector <Task> _added_tasks;
std::vector <Task> _modified_tasks;
std::unordered_set <std::string> _purged_tasks;
std::vector <std::string> _lines;
std::vector <std::string> _added_lines;
File _file;
@@ -110,6 +112,7 @@ public:
void set_location (const std::string&);
void add (Task&, bool add_to_backlog = true);
void modify (Task&, bool add_to_backlog = true);
void purge (Task&);
void commit ();
void get_changes (std::vector <Task>&);
void revert ();

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -39,50 +39,44 @@
#include <arpa/inet.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <gnutls/x509.h>
#include <text.h>
#include <i18n.h>
#include <shared.h>
#include <format.h>
#define MAX_BUF 16384
#if GNUTLS_VERSION_NUMBER < 0x030406
#if GNUTLS_VERSION_NUMBER >= 0x020a00
static int verify_certificate_callback (gnutls_session_t);
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
static void gnutls_log_function (int level, const char* message)
{
std::cout << "c: " << level << " " << message;
std::cout << "c: " << level << ' ' << message;
}
////////////////////////////////////////////////////////////////////////////////
#if GNUTLS_VERSION_NUMBER < 0x030406
#if GNUTLS_VERSION_NUMBER >= 0x020a00
static int verify_certificate_callback (gnutls_session_t session)
{
const TLSClient* client = (TLSClient*) gnutls_session_get_ptr (session);
const TLSClient* client = (TLSClient*) gnutls_session_get_ptr (session); // All
return client->verify_certificate ();
}
////////////////////////////////////////////////////////////////////////////////
TLSClient::TLSClient ()
: _ca ("")
, _cert ("")
, _key ("")
, _host ("")
, _port ("")
, _session(0)
, _socket (0)
, _limit (0)
, _debug (false)
, _trust(strict)
{
}
#endif
#endif
////////////////////////////////////////////////////////////////////////////////
TLSClient::~TLSClient ()
{
gnutls_deinit (_session);
gnutls_certificate_free_credentials (_credentials);
gnutls_global_deinit ();
gnutls_deinit (_session); // All
gnutls_certificate_free_credentials (_credentials); // All
#if GNUTLS_VERSION_NUMBER < 0x030300
gnutls_global_deinit (); // All
#endif
if (_socket)
{
@@ -105,8 +99,8 @@ void TLSClient::debug (int level)
if (level)
_debug = true;
gnutls_global_set_log_function (gnutls_log_function);
gnutls_global_set_log_level (level);
gnutls_global_set_log_function (gnutls_log_function); // All
gnutls_global_set_log_level (level); // All
}
////////////////////////////////////////////////////////////////////////////////
@@ -140,52 +134,74 @@ void TLSClient::init (
_cert = cert;
_key = key;
int ret = gnutls_global_init ();
int ret;
#if GNUTLS_VERSION_NUMBER < 0x030300
ret = gnutls_global_init (); // All
if (ret < 0)
throw format ("TLS init error. {1}", gnutls_strerror (ret));
throw format ("TLS init error. {1}", gnutls_strerror (ret)); // All
#endif
ret = gnutls_certificate_allocate_credentials (&_credentials);
ret = gnutls_certificate_allocate_credentials (&_credentials); // All
if (ret < 0)
throw format ("TLS allocation error. {1}", gnutls_strerror (ret));
throw format ("TLS allocation error. {1}", gnutls_strerror (ret)); // All
if (_ca != "" &&
(ret = gnutls_certificate_set_x509_trust_file (_credentials, _ca.c_str (), GNUTLS_X509_FMT_PEM)) < 0)
throw format ("Bad CA file. {1}", gnutls_strerror (ret));
#if GNUTLS_VERSION_NUMBER >= 0x030014
// Automatic loading of system installed CA certificates.
ret = gnutls_certificate_set_x509_system_trust (_credentials); // 3.0.20
if (ret < 0)
throw format ("Bad System Trust. {1}", gnutls_strerror (ret)); // All
#endif
if (_ca != "")
{
// The gnutls_certificate_set_x509_key_file call returns number of
// certificates parsed on success (including 0, when no certificate was
// found) and negative values on error
ret = gnutls_certificate_set_x509_trust_file (_credentials, _ca.c_str (), GNUTLS_X509_FMT_PEM); // All
if (ret == 0)
throw format ("CA file {1} contains no certificate.", _ca);
else if (ret < 0)
throw format ("Bad CA file: {1}", gnutls_strerror (ret)); // All
}
// TODO This may need 0x030111 protection.
if (_cert != "" &&
_key != "" &&
(ret = gnutls_certificate_set_x509_key_file (_credentials, _cert.c_str (), _key.c_str (), GNUTLS_X509_FMT_PEM)) < 0)
throw format ("Bad CERT file. {1}", gnutls_strerror (ret));
(ret = gnutls_certificate_set_x509_key_file (_credentials, _cert.c_str (), _key.c_str (), GNUTLS_X509_FMT_PEM)) < 0) // 3.1.11
throw format ("Bad client CERT/KEY file. {1}", gnutls_strerror (ret)); // All
#if GNUTLS_VERSION_NUMBER >= 0x02090a
#if GNUTLS_VERSION_NUMBER < 0x030406
#if GNUTLS_VERSION_NUMBER >= 0x020a00
// The automatic verification for the server certificate with
// gnutls_certificate_set_verify_function only works with gnutls
// >=2.9.10. So with older versions we should call the verify function
// manually after the gnutls handshake.
gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback);
gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback); // 2.10.0
#endif
ret = gnutls_init (&_session, GNUTLS_CLIENT);
#endif
ret = gnutls_init (&_session, GNUTLS_CLIENT); // All
if (ret < 0)
throw format ("TLS client init error. {1}", gnutls_strerror (ret));
throw format ("TLS client init error. {1}", gnutls_strerror (ret)); // All
// Use default priorities unless overridden.
if (_ciphers == "")
_ciphers = "NORMAL";
const char *err;
ret = gnutls_priority_set_direct (_session, _ciphers.c_str (), &err);
ret = gnutls_priority_set_direct (_session, _ciphers.c_str (), &err); // All
if (ret < 0)
{
if (_debug && ret == GNUTLS_E_INVALID_REQUEST)
std::cout << "c: ERROR Priority error at: " << err << "\n";
std::cout << "c: ERROR Priority error at: " << err << '\n';
throw format (STRING_TLS_INIT_FAIL, gnutls_strerror (ret));
throw format ("Error initializing TLS. {1}", gnutls_strerror (ret)); // All
}
// Apply the x509 credentials to the current session.
ret = gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials);
ret = gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials); // All
if (ret < 0)
throw format ("TLS credentials error. {1}", gnutls_strerror (ret));
throw format ("TLS credentials error. {1}", gnutls_strerror (ret)); // All
}
////////////////////////////////////////////////////////////////////////////////
@@ -194,9 +210,29 @@ void TLSClient::connect (const std::string& host, const std::string& port)
_host = host;
_port = port;
int ret;
#if GNUTLS_VERSION_NUMBER >= 0x030406
// For _trust == TLSClient::allow_all we perform no action
if (_trust == TLSClient::ignore_hostname)
gnutls_session_set_verify_cert (_session, nullptr, 0); // 3.4.6
else if (_trust == TLSClient::strict)
gnutls_session_set_verify_cert (_session, _host.c_str (), 0); // 3.4.6
#endif
// SNI. Only permitted when _host is a DNS name, not an IPv4/6 address.
std::string dummyAddress;
int dummyPort;
if (! isIPv4Address (_host, dummyAddress, dummyPort) &&
! isIPv6Address (_host, dummyAddress, dummyPort))
{
ret = gnutls_server_name_set (_session, GNUTLS_NAME_DNS, _host.c_str (), _host.length ()); // All
if (ret < 0)
throw format ("TLS SNI error. {1}", gnutls_strerror (ret)); // All
}
// 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);
// it during the handshake below and call the verification method.
gnutls_session_set_ptr (_session, (void*) this); // All
// use IPv4 or IPv6, does not matter.
struct addrinfo hints {};
@@ -205,13 +241,13 @@ void TLSClient::connect (const std::string& host, const std::string& port)
hints.ai_flags = AI_PASSIVE; // use my IP
struct addrinfo* res;
int ret = ::getaddrinfo (host.c_str (), port.c_str (), &hints, &res);
ret = ::getaddrinfo (host.c_str (), port.c_str (), &hints, &res);
if (ret != 0)
throw std::string (::gai_strerror (ret));
// Try them all, stop on success.
struct addrinfo* p;
for (p = res; p != NULL; p = p->ai_next)
for (p = res; p != nullptr; p = p->ai_next)
{
if ((_socket = ::socket (p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
continue;
@@ -235,43 +271,65 @@ void TLSClient::connect (const std::string& host, const std::string& port)
free (res);
if (p == NULL)
throw format (STRING_CMD_SYNC_CONNECT, host, port);
if (p == nullptr)
throw format ("Could not connect to {1} {2}", host, port);
#if GNUTLS_VERSION_NUMBER >= 0x030100
gnutls_handshake_set_timeout (_session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); // 3.1.0
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030109
gnutls_transport_set_int (_session, _socket);
gnutls_transport_set_int (_session, _socket); // 3.1.9
#else
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (intptr_t) _socket);
gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (intptr_t) _socket); // All
#endif
// Perform the TLS handshake
do
{
ret = gnutls_handshake (_session);
ret = gnutls_handshake (_session); // All
}
while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
if (ret < 0)
throw format (STRING_CMD_SYNC_HANDSHAKE, gnutls_strerror (ret));
while (ret < 0 && gnutls_error_is_fatal (ret) == 0); // All
#if GNUTLS_VERSION_NUMBER < 0x02090a
if (ret < 0)
{
#if GNUTLS_VERSION_NUMBER >= 0x030406
if (ret == GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR)
{
auto type = gnutls_certificate_type_get (_session); // All
auto status = gnutls_session_get_verify_cert_status (_session); // 3.4.6
gnutls_datum_t out;
gnutls_certificate_verification_status_print (status, type, &out, 0); // 3.1.4
std::string error {(const char*) out.data};
gnutls_free (out.data); // All
throw format ("Handshake failed. {1}", error); // All
}
#else
throw format ("Handshake failed. {1}", gnutls_strerror (ret)); // All
#endif
}
#if GNUTLS_VERSION_NUMBER < 0x020a00
// 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
// >=2.10.0. 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 format (STRING_TLS_INIT_FAIL, gnutls_strerror (ret));
throw format ("Error initializing TLS. {1}", gnutls_strerror (ret)); // All
}
#endif
if (_debug)
{
#if GNUTLS_VERSION_NUMBER >= 0x03010a
char* desc = gnutls_session_get_desc (_session);
std::cout << "c: INFO Handshake was completed: " << desc << "\n";
char* desc = gnutls_session_get_desc (_session); // 3.1.10
std::cout << "c: INFO Handshake was completed: " << desc << '\n';
gnutls_free (desc);
#else
std::cout << "c: INFO Handshake was completed.\n";
@@ -282,7 +340,7 @@ void TLSClient::connect (const std::string& host, const std::string& port)
////////////////////////////////////////////////////////////////////////////////
void TLSClient::bye ()
{
gnutls_bye (_session, GNUTLS_SHUT_RDWR);
gnutls_bye (_session, GNUTLS_SHUT_RDWR); // All
}
////////////////////////////////////////////////////////////////////////////////
@@ -300,13 +358,13 @@ int TLSClient::verify_certificate () const
const char* hostname = _host.c_str();
#if GNUTLS_VERSION_NUMBER >= 0x030104
if (_trust == TLSClient::ignore_hostname)
hostname = NULL;
hostname = nullptr;
int ret = gnutls_certificate_verify_peers3 (_session, hostname, &status);
int ret = gnutls_certificate_verify_peers3 (_session, hostname, &status); // 3.1.4
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR Certificate verification peers3 failed. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR Certificate verification peers3 failed. " << gnutls_strerror (ret) << '\n'; // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
@@ -316,56 +374,56 @@ int TLSClient::verify_certificate () const
// GNUTLS_CERT_UNEXPECTED_OWNER 1<<14 Hostname does not match
if (_debug && status)
std::cout << "c: ERROR Certificate status=" << status << "\n";
std::cout << "c: ERROR Certificate status=" << status << '\n';
#else
int ret = gnutls_certificate_verify_peers2 (_session, &status);
int ret = gnutls_certificate_verify_peers2 (_session, &status); // All
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR Certificate verification peers2 failed. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR Certificate verification peers2 failed. " << gnutls_strerror (ret) << '\n'; // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (_debug && status)
std::cout << "c: ERROR Certificate status=" << status << "\n";
std::cout << "c: ERROR Certificate status=" << status << '\n';
if ((status == 0) && (_trust != TLSClient::ignore_hostname))
{
if (gnutls_certificate_type_get (_session) == GNUTLS_CRT_X509)
if (gnutls_certificate_type_get (_session) == GNUTLS_CRT_X509) // All
{
const gnutls_datum* cert_list;
unsigned int cert_list_size;
gnutls_x509_crt cert;
cert_list = gnutls_certificate_get_peers (_session, &cert_list_size);
cert_list = gnutls_certificate_get_peers (_session, &cert_list_size); // All
if (cert_list_size == 0)
{
if (_debug)
std::cout << "c: ERROR Certificate get peers failed. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR Certificate get peers failed. " << gnutls_strerror (ret) << '\n'; // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
ret = gnutls_x509_crt_init (&cert);
ret = gnutls_x509_crt_init (&cert); // All
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR x509 init failed. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR x509 init failed. " << gnutls_strerror (ret) << '\n'; // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER);
ret = gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER); // All
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR x509 cert import. " << gnutls_strerror (ret) << "\n";
gnutls_x509_crt_deinit(cert);
std::cout << "c: ERROR x509 cert import. " << gnutls_strerror (ret) << '\n'; // All
gnutls_x509_crt_deinit(cert); // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (gnutls_x509_crt_check_hostname (cert, hostname) == 0)
if (gnutls_x509_crt_check_hostname (cert, hostname) == 0) // All
{
if (_debug)
std::cout << "c: ERROR x509 cert check hostname. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR x509 cert check hostname. " << gnutls_strerror (ret) << '\n'; // All
gnutls_x509_crt_deinit(cert);
return GNUTLS_E_CERTIFICATE_ERROR;
}
@@ -376,18 +434,18 @@ int TLSClient::verify_certificate () const
#endif
#if GNUTLS_VERSION_NUMBER >= 0x030104
gnutls_certificate_type_t type = gnutls_certificate_type_get (_session);
gnutls_certificate_type_t type = gnutls_certificate_type_get (_session); // All
gnutls_datum_t out;
ret = gnutls_certificate_verification_status_print (status, type, &out, 0);
ret = gnutls_certificate_verification_status_print (status, type, &out, 0); // 3.1.4
if (ret < 0)
{
if (_debug)
std::cout << "c: ERROR certificate verification status. " << gnutls_strerror (ret) << "\n";
std::cout << "c: ERROR certificate verification status. " << gnutls_strerror (ret) << '\n'; // All
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (_debug)
std::cout << "c: INFO " << out.data << "\n";
std::cout << "c: INFO " << out.data << '\n';
gnutls_free (out.data);
#endif
@@ -418,7 +476,7 @@ void TLSClient::send (const std::string& data)
int status;
do
{
status = gnutls_record_send (_session, packet.c_str () + total, remaining);
status = gnutls_record_send (_session, packet.c_str () + total, remaining); // All
}
while (errno == GNUTLS_E_INTERRUPTED ||
errno == GNUTLS_E_AGAIN);
@@ -447,7 +505,7 @@ void TLSClient::recv (std::string& data)
unsigned char header[4] {};
do
{
received = gnutls_record_recv (_session, header, 4);
received = gnutls_record_recv (_session, header, 4); // All
}
while (received > 0 &&
(errno == GNUTLS_E_INTERRUPTED ||
@@ -475,7 +533,7 @@ void TLSClient::recv (std::string& data)
{
do
{
received = gnutls_record_recv (_session, buffer, MAX_BUF - 1);
received = gnutls_record_recv (_session, buffer, MAX_BUF - 1); // All
}
while (received > 0 &&
(errno == GNUTLS_E_INTERRUPTED ||
@@ -490,13 +548,13 @@ void TLSClient::recv (std::string& data)
}
// Something happened.
if (received < 0 && gnutls_error_is_fatal (received) == 0)
if (received < 0 && gnutls_error_is_fatal (received) == 0) // All
{
if (_debug)
std::cout << "c: WARNING " << gnutls_strerror (received) << "\n";
std::cout << "c: WARNING " << gnutls_strerror (received) << '\n'; // All
}
else if (received < 0)
throw std::string (gnutls_strerror (received));
throw std::string (gnutls_strerror (received)); // All
buffer [received] = '\0';
data += buffer;

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_TLSCLIENT
@@ -36,7 +36,7 @@ class TLSClient
public:
enum trust_level { strict, ignore_hostname, allow_all };
TLSClient ();
TLSClient () = default;
~TLSClient ();
void limit (int);
void debug (int);
@@ -51,18 +51,18 @@ public:
void recv (std::string&);
private:
std::string _ca;
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;
std::string _ca {""};
std::string _cert {""};
std::string _key {""};
std::string _ciphers {""};
std::string _host {""};
std::string _port {""};
gnutls_certificate_credentials_t _credentials {};
gnutls_session_t _session {nullptr};
int _socket {0};
int _limit {0};
bool _debug {false};
enum trust_level _trust {strict};
};
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -39,6 +39,7 @@ class Task
public:
static std::string defaultProject;
static std::string defaultDue;
static std::string defaultScheduled;
static bool searchCaseSensitive;
static bool regex;
static std::map <std::string, std::string> attributes; // name -> type
@@ -107,6 +108,7 @@ public:
bool is_duetomorrow () const;
bool is_dueweek () const;
bool is_duemonth () const;
bool is_duequarter () const;
bool is_dueyear () const;
bool is_overdue () const;
bool is_udaPresent () const;
@@ -124,11 +126,12 @@ public:
bool hasTag (const std::string&) const;
void addTag (const std::string&);
void addTags (const std::vector <std::string>&);
void getTags (std::vector<std::string>&) const;
std::vector <std::string> getTags () const;
void removeTag (const std::string&);
int getAnnotationCount () const;
bool hasAnnotations () const;
void getAnnotations (std::map <std::string, std::string>&) const;
std::map <std::string, std::string> getAnnotations () const;
void setAnnotations (const std::map <std::string, std::string>&);
void addAnnotation (const std::string&);
void removeAnnotations ();
@@ -140,10 +143,11 @@ public:
#ifdef PRODUCT_TASKWARRIOR
void removeDependency (int);
void removeDependency (const std::string&);
void getDependencies (std::vector <int>&) const;
void getDependencies (std::vector <std::string>&) const;
std::vector <int> getDependencyIDs () const;
std::vector <std::string> getDependencyUUIDs () const;
std::vector <Task> getDependencyTasks () const;
void getUDAOrphans (std::vector <std::string>&) const;
std::vector <std::string> getUDAOrphanUUIDs () const;
void substitute (const std::string&, const std::string&, const std::string&);
#endif
@@ -168,17 +172,17 @@ private:
const std::string decode (const std::string&) const;
public:
float urgency_project () const;
float urgency_active () const;
float urgency_scheduled () const;
float urgency_waiting () const;
float urgency_blocked () const;
float urgency_inherit () const;
float urgency_project () const;
float urgency_active () const;
float urgency_scheduled () const;
float urgency_waiting () const;
float urgency_blocked () const;
float urgency_inherit () const;
float urgency_annotations () const;
float urgency_tags () const;
float urgency_due () const;
float urgency_blocking () const;
float urgency_age () const;
float urgency_tags () const;
float urgency_due () const;
float urgency_blocking () const;
float urgency_age () const;
};
#endif

View File

@@ -1,113 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <Timer.h>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <Context.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
Timer::Timer ()
{
}
////////////////////////////////////////////////////////////////////////////////
// Timer starts when the object is constructed with a description.
Timer::Timer (const std::string& description)
: _description (description)
{
start ();
}
////////////////////////////////////////////////////////////////////////////////
// Timer stops when the object is destructed.
Timer::~Timer ()
{
stop ();
std::stringstream s;
s << "Timer "
<< _description
<< " "
<< std::setprecision (6)
<< std::fixed
<< _total / 1000000.0
<< " sec";
context.debug (s.str ());
}
////////////////////////////////////////////////////////////////////////////////
void Timer::start ()
{
if (!_running)
{
gettimeofday (&_start, nullptr);
_running = true;
}
}
////////////////////////////////////////////////////////////////////////////////
void Timer::stop ()
{
if (_running)
{
struct timeval end;
gettimeofday (&end, nullptr);
_running = false;
_total += (end.tv_sec - _start.tv_sec) * 1000000
+ (end.tv_usec - _start.tv_usec);
}
}
////////////////////////////////////////////////////////////////////////////////
unsigned long Timer::total () const
{
return _total;
}
////////////////////////////////////////////////////////////////////////////////
void Timer::subtract (unsigned long value)
{
if (value > _total)
_total = 0;
else
_total -= value;
}
////////////////////////////////////////////////////////////////////////////////
unsigned long Timer::now ()
{
struct timeval now;
gettimeofday (&now, nullptr);
return now.tv_sec * 1000000 + now.tv_usec;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,55 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_TIMER
#define INCLUDED_TIMER
#include <string>
#include <sys/time.h>
// Timer is a scope-activated timer that dumps to std::cout at end of scope.
class Timer
{
public:
Timer ();
explicit Timer (const std::string&);
~Timer ();
void start ();
void stop ();
unsigned long total () const;
void subtract (unsigned long);
static unsigned long now ();
private:
std::string _description {"-"};
bool _running {false};
struct timeval _start {};
unsigned long _total {0};
};
#endif

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -31,11 +31,62 @@
#include <math.h>
#include <stdlib.h>
#include <Variant.h>
#include <ISO8601.h>
#include <Datetime.h>
#include <Duration.h>
#include <Lexer.h>
#include <RX.h>
#include <text.h>
#include <i18n.h>
#include <shared.h>
// These are all error messages generated by the expression evaluator, and are
// mostly concerned with how various operators interact with the different
// data types.
#define STRING_VARIANT_TIME_T "Cannot instantiate this type with a time_t value."
#define STRING_VARIANT_EXP_BOOL "Cannot exponentiate Booleans"
#define STRING_VARIANT_EXP_NON_INT "Cannot exponentiate to a non-integer power"
#define STRING_VARIANT_EXP_STRING "Cannot exponentiate strings"
#define STRING_VARIANT_EXP_DATE "Cannot exponentiate dates"
#define STRING_VARIANT_EXP_DURATION "Cannot exponentiate durations"
#define STRING_VARIANT_SUB_BOOL "Cannot subtract from a Boolean value"
#define STRING_VARIANT_SUB_STRING "Cannot subtract strings"
#define STRING_VARIANT_SUB_DATE "Cannot subtract a date"
#define STRING_VARIANT_ADD_BOOL "Cannot add two Boolean values"
#define STRING_VARIANT_ADD_DATE "Cannot add two date values"
#define STRING_VARIANT_MUL_BOOL "Cannot multiply Boolean values"
#define STRING_VARIANT_MUL_DATE "Cannot multiply date values"
#define STRING_VARIANT_MUL_REAL_STR "Cannot multiply real numbers by strings"
#define STRING_VARIANT_MUL_STR_REAL "Cannot multiply strings by real numbers"
#define STRING_VARIANT_MUL_STR_STR "Cannot multiply strings by strings"
#define STRING_VARIANT_MUL_STR_DATE "Cannot multiply strings by dates"
#define STRING_VARIANT_MUL_STR_DUR "Cannot multiply strings by durations"
#define STRING_VARIANT_MUL_DUR_STR "Cannot multiply durations by strings"
#define STRING_VARIANT_MUL_DUR_DATE "Cannot multiply durations by dates"
#define STRING_VARIANT_MUL_DUR_DUR "Cannot multiply durations by durations"
#define STRING_VARIANT_DIV_BOOL "Cannot divide Boolean values"
#define STRING_VARIANT_DIV_INT_BOOL "Cannot divide integers by Boolean values"
#define STRING_VARIANT_DIV_ZERO "Cannot divide by zero"
#define STRING_VARIANT_DIV_INT_STR "Cannot divide integer by string"
#define STRING_VARIANT_DIV_INT_DATE "Cannot divide integer by date values"
#define STRING_VARIANT_DIV_REAL_BOOL "Cannot divide real by Boolean"
#define STRING_VARIANT_DIV_REAL_STR "Cannot divide real numbers by strings"
#define STRING_VARIANT_DIV_REAL_DATE "Cannot divide real numbers by dates"
#define STRING_VARIANT_DIV_DUR_BOOL "Cannot divide duration by Boolean"
#define STRING_VARIANT_DIV_DUR_STR "Cannot divide durations by strings"
#define STRING_VARIANT_DIV_DUR_DATE "Cannot divide durations by dates"
#define STRING_VARIANT_DIV_DUR_DUR "Cannot divide durations by durations"
#define STRING_VARIANT_MOD_BOOL "Cannot modulo Booleans"
#define STRING_VARIANT_MOD_DATE "Cannot modulo date values"
#define STRING_VARIANT_MOD_DUR "Cannot modulo duration values"
#define STRING_VARIANT_MOD_INT_BOOL "Cannot modulo integer by Boolean"
#define STRING_VARIANT_MOD_INT_DATE "Cannot modulo integer by date values"
#define STRING_VARIANT_MOD_INT_DUR "Cannot modulo integer by duration values"
#define STRING_VARIANT_MOD_INT_STR "Cannot modulo integer by string"
#define STRING_VARIANT_MOD_REAL_BOOL "Cannot modulo real by Boolean"
#define STRING_VARIANT_MOD_REAL_DUR "Cannot modulo real by duration values"
#define STRING_VARIANT_MOD_REAL_DATE "Cannot modulo real numbers by dates"
#define STRING_VARIANT_MOD_REAL_STR "Cannot modulo real numbers by strings"
#define STRING_VARIANT_MOD_STR "Cannot modulo string values"
#define STRING_VARIANT_MOD_ZERO "Cannot modulo zero"
#define STRING_VARIANT_SQRT_NEG "Cannot take the square root of a negative number."
std::string Variant::dateFormat = "";
bool Variant::searchCaseSensitive = true;
@@ -872,10 +923,7 @@ bool Variant::operator_match (const Variant& other, const Task& task) const
// in the annotations.
if (left.source () == "description")
{
std::map <std::string, std::string> annotations;
task.getAnnotations (annotations);
for (auto& a : annotations)
for (auto& a : task.getAnnotations ())
if (r.match (a.second))
return true;
}
@@ -907,10 +955,7 @@ bool Variant::operator_match (const Variant& other, const Task& task) const
// in the annotations.
if (left.source () == "description")
{
std::map <std::string, std::string> annotations;
task.getAnnotations (annotations);
for (auto& a : annotations)
for (auto& a : task.getAnnotations ())
if (find (a.second, pattern, searchCaseSensitive) != std::string::npos)
return true;
}
@@ -956,8 +1001,8 @@ bool Variant::operator_partial (const Variant& other) const
case type_date:
{
left.cast (type_date);
ISO8601d left_date (left._date);
ISO8601d right_date (right._date);
Datetime left_date (left._date);
Datetime right_date (right._date);
return left_date.sameDay (right_date);
}
@@ -977,8 +1022,8 @@ bool Variant::operator_partial (const Variant& other) const
case type_date:
{
left.cast (type_date);
ISO8601d left_date (left._date);
ISO8601d right_date (right._date);
Datetime left_date (left._date);
Datetime right_date (right._date);
return left_date.sameDay (right_date);
}
@@ -1003,8 +1048,8 @@ bool Variant::operator_partial (const Variant& other) const
case type_date:
{
left.cast (type_date);
ISO8601d left_date (left._date);
ISO8601d right_date (right._date);
Datetime left_date (left._date);
Datetime right_date (right._date);
return left_date.sameDay (right_date);
}
@@ -1049,8 +1094,8 @@ bool Variant::operator_partial (const Variant& other) const
return false;
left.cast (type_date);
ISO8601d left_date (left._date);
ISO8601d right_date (right._date);
Datetime left_date (left._date);
Datetime right_date (right._date);
return left_date.sameDay (right_date);
}
@@ -1075,8 +1120,8 @@ bool Variant::operator_partial (const Variant& other) const
case type_duration:
{
right.cast (type_date);
ISO8601d left_date (left._date);
ISO8601d right_date (right._date);
Datetime left_date (left._date);
Datetime right_date (right._date);
return left_date.sameDay (right_date);
}
}
@@ -1233,7 +1278,18 @@ Variant& Variant::operator-= (const Variant& other)
break;
case type_string:
throw std::string (STRING_VARIANT_SUB_STRING);
switch (right._type)
{
case type_string:
cast (type_string); _string += '-' + right._string; break;
case type_boolean:
case type_integer:
case type_real:
case type_date:
case type_duration:
throw std::string (STRING_VARIANT_SUB_STRING);
break;
}
break;
case type_date:
@@ -1730,10 +1786,10 @@ Variant::operator std::string () const
return _string;
case type_date:
return ISO8601d (_date).toISOLocalExtended ();
return Datetime (_date).toISOLocalExtended ();
case type_duration:
return ISO8601p (_duration).format ();
return Duration (_duration).formatISO ();
}
return "";
@@ -1782,7 +1838,7 @@ void Variant::cast (const enum type new_type)
case type_string:
{
char temp[24];
sprintf (temp, "%d", _integer);
snprintf (temp, 24, "%d", _integer);
_string = temp;
}
break;
@@ -1800,7 +1856,7 @@ void Variant::cast (const enum type new_type)
case type_string:
{
char temp[24];
sprintf (temp, "%g", _real);
snprintf (temp, 24, "%g", _real);
_string = temp;
}
break;
@@ -1819,14 +1875,14 @@ void Variant::cast (const enum type new_type)
_string == "0.0") ? false : true;
break;
case type_integer:
_integer = (int) strtol (_string.c_str (), NULL, (_string.substr (0, 2) == "0x" ? 16 : 10));
_integer = (int) strtol (_string.c_str (), nullptr, (_string.substr (0, 2) == "0x" ? 16 : 10));
break;
case type_real: _real = strtod (_string.c_str (), NULL); break;
case type_real: _real = strtod (_string.c_str (), nullptr); break;
case type_string: break;
case type_date:
{
_date = 0;
ISO8601d iso;
Datetime iso;
std::string::size_type pos = 0;
if (iso.parse (_string, pos, dateFormat) &&
pos == _string.length ())
@@ -1836,17 +1892,17 @@ void Variant::cast (const enum type new_type)
}
pos = 0;
ISO8601p isop;
Duration isop;
if (isop.parse (_string, pos) &&
pos == _string.length ())
{
_date = ISO8601d ().toEpoch () + (time_t) isop;
_date = Datetime ().toEpoch () + isop.toTime_t ();
break;
}
if (dateFormat != "")
{
_date = ISO8601d (_string, dateFormat).toEpoch ();
_date = Datetime (_string, dateFormat).toEpoch ();
break;
}
}
@@ -1855,11 +1911,11 @@ void Variant::cast (const enum type new_type)
{
_duration = 0;
std::string::size_type pos = 0;
ISO8601p iso;
Duration iso;
if (iso.parse (_string, pos) &&
pos == _string.length ())
{
_duration = (time_t) iso;
_duration = iso.toTime_t ();
}
}
break;
@@ -1908,8 +1964,6 @@ bool Variant::trivial () const
(_type == type_string && _string == "") ||
(_type == type_date && _date == 0) ||
(_type == type_duration && _duration == 0);
return false;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2013 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,14 +28,11 @@
#include <ViewTask.h>
#include <numeric>
#include <Context.h>
#include <Timer.h>
#include <text.h>
#include <format.h>
#include <util.h>
#include <utf8.h>
#include <i18n.h>
#include <main.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ViewTask::ViewTask ()
: _width (0)
@@ -111,10 +108,10 @@ ViewTask::~ViewTask ()
//
std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& sequence)
{
context.timer_render.start ();
Timer timer;
bool const obfuscate = context.config.getBoolean ("obfuscate");
bool const print_empty_columns = context.config.getBoolean ("print.empty.columns");
bool const obfuscate = Context::getContext ().config.getBoolean ("obfuscate");
bool const print_empty_columns = Context::getContext ().config.getBoolean ("print.empty.columns");
std::vector <Column*> nonempty_columns;
std::vector <bool> nonempty_sort;
@@ -191,11 +188,11 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Calculate final column widths.
int overage = _width - sum_minimal - all_extra;
context.debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}",
sum_minimal + all_extra,
sum_ideal + all_extra,
overage,
_width));
Context::getContext ().debug (format ("ViewTask::render min={1} ideal={2} overage={3} width={4}",
sum_minimal + all_extra,
sum_ideal + all_extra,
overage,
_width));
std::vector <int> widths;
@@ -208,6 +205,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Not enough for minimum.
else if (overage < 0)
{
Context::getContext ().error (format ("The report has a minimum width of {1} and does not fit in the available width of {2}.", sum_minimal + all_extra, _width));
widths = minimal;
}
@@ -256,10 +254,10 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
std::string extra = std::string (_extra_padding, ' ');
std::string intra = std::string (_intra_padding, ' ');
std::string extra_odd = context.color () ? _extra_odd.colorize (extra) : extra;
std::string extra_even = context.color () ? _extra_even.colorize (extra) : extra;
std::string intra_odd = context.color () ? _intra_odd.colorize (intra) : intra;
std::string intra_even = context.color () ? _intra_even.colorize (intra) : intra;
std::string extra_odd = Context::getContext ().color () ? _extra_odd.colorize (extra) : extra;
std::string extra_even = Context::getContext ().color () ? _extra_even.colorize (extra) : extra;
std::string intra_odd = Context::getContext ().color () ? _intra_odd.colorize (intra) : intra;
std::string intra_even = Context::getContext ().color () ? _intra_even.colorize (intra) : intra;
std::string out;
_lines = 0;
@@ -287,7 +285,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
context.timer_render.stop ();
Context::getContext ().time_render_us += timer.total_us ();
return out;
}
}
@@ -306,7 +304,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Alternate rows based on |s % 2|
bool odd = (s % 2) ? true : false;
Color row_color;
if (context.color ())
if (Context::getContext ().color ())
{
row_color = odd ? _odd : _even;
row_color.blend (rule_color);
@@ -374,7 +372,7 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
{
context.timer_render.stop ();
Context::getContext ().time_render_us += timer.total_us ();
return out;
}
}
@@ -384,12 +382,12 @@ std::string ViewTask::render (std::vector <Task>& data, std::vector <int>& seque
// Stop if the row limit is exceeded.
if (++_rows >= _truncate_rows && _truncate_rows != 0)
{
context.timer_render.stop ();
Context::getContext ().time_render_us += timer.total_us ();
return out;
}
}
context.timer_render.stop ();
Context::getContext ().time_render_us += timer.total_us ();
return out;
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,315 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ViewText.h>
#include <Context.h>
#include <text.h>
#include <utf8.h>
#include <main.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ViewText::ViewText ()
: _width (0)
, _left_margin (0)
, _header (0)
, _odd (0)
, _even (0)
, _intra_padding (1)
, _intra_odd (0)
, _intra_even (0)
, _extra_padding (0)
, _extra_odd (0)
, _extra_even (0)
, _truncate_lines (0)
, _truncate_rows (0)
, _lines (0)
, _rows (0)
{
}
////////////////////////////////////////////////////////////////////////////////
ViewText::~ViewText ()
{
for (auto& col : _columns)
delete col;
_columns.clear ();
}
////////////////////////////////////////////////////////////////////////////////
int ViewText::addRow ()
{
_data.push_back (std::vector <std::string> (_columns.size (), ""));
_color.push_back (std::vector <Color> (_columns.size (), Color::nocolor));
return _data.size () - 1;
}
////////////////////////////////////////////////////////////////////////////////
void ViewText::set (int row, int col, const std::string& value, Color color)
{
_data[row][col] = value;
if (color.nontrivial () &&
context.color ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
void ViewText::set (int row, int col, int value, Color color)
{
std::string string_value = format (value);
_data[row][col] = string_value;
if (color.nontrivial () &&
context.color ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
void ViewText::set (int row, int col, Color color)
{
if (color.nontrivial () &&
context.color ())
_color[row][col] = color;
}
////////////////////////////////////////////////////////////////////////////////
std::string ViewText::render ()
{
bool const obfuscate = context.config.getBoolean ("obfuscate");
// Determine minimal, ideal column widths.
std::vector <int> minimal;
std::vector <int> ideal;
for (unsigned int col = 0; col < _columns.size (); ++col)
{
// Headers factor in to width calculations.
unsigned int global_min = utf8_width (_columns[col]->label ());
unsigned int global_ideal = global_min;
for (unsigned int row = 0; row < _data.size (); ++row)
{
// Determine minimum and ideal width for this column.
unsigned int min = 0;
unsigned int ideal = 0;
_columns[col]->measure (_data[row][col], min, ideal);
if (min > global_min) global_min = min;
if (ideal > global_ideal) global_ideal = ideal;
// If a fixed-width column was just measured, there is no point repeating
// the measurement for all tasks.
if (_columns[col]->is_fixed_width ())
break;
}
minimal.push_back (global_min);
ideal.push_back (global_ideal);
}
// Sum the minimal widths.
int sum_minimal = 0;
for (auto& c : minimal)
sum_minimal += c;
// Sum the ideal widths.
int sum_ideal = 0;
for (auto& c : ideal)
sum_ideal += c;
// Calculate final column widths.
int overage = _width
- _left_margin
- (2 * _extra_padding)
- ((_columns.size () - 1) * _intra_padding);
std::vector <int> widths;
if (sum_ideal <= overage)
widths = ideal;
else if (sum_minimal > overage || overage < 0)
widths = minimal;
else if (overage > 0)
{
widths = minimal;
overage -= sum_minimal;
// Spread 'overage' among columns where width[i] < ideal[i]
while (overage)
{
for (unsigned int i = 0; i < _columns.size () && overage; ++i)
{
if (widths[i] < ideal[i])
{
++widths[i];
--overage;
}
}
}
}
// Compose column headers.
unsigned int max_lines = 0;
std::vector <std::vector <std::string>> headers;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
headers.push_back (std::vector <std::string> ());
_columns[c]->renderHeader (headers[c], widths[c], _header);
if (headers[c].size () > max_lines)
max_lines = headers[c].size ();
}
// Output string.
std::string out;
_lines = 0;
// Render column headers.
std::string left_margin = std::string (_left_margin, ' ');
std::string extra = std::string (_extra_padding, ' ');
std::string intra = std::string (_intra_padding, ' ');
std::string extra_odd = context.color () ? _extra_odd.colorize (extra) : extra;
std::string extra_even = context.color () ? _extra_even.colorize (extra) : extra;
std::string intra_odd = context.color () ? _intra_odd.colorize (intra) : intra;
std::string intra_even = context.color () ? _intra_even.colorize (intra) : intra;
for (unsigned int i = 0; i < max_lines; ++i)
{
out += left_margin + extra;
for (unsigned int c = 0; c < _columns.size (); ++c)
{
if (c)
out += intra;
if (headers[c].size () < max_lines - i)
out += _header.colorize (std::string (widths[c], ' '));
else
out += headers[c][i];
}
out += extra;
// Trim right.
out.erase (out.find_last_not_of (" ") + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
return out;
}
// Compose, render columns, in sequence.
_rows = 0;
std::vector <std::vector <std::string>> cells;
for (unsigned int row = 0; row < _data.size (); ++row)
{
max_lines = 0;
// Alternate rows based on |s % 2|
bool odd = (row % 2) ? true : false;
Color row_color = odd ? _odd : _even;
// TODO row_color.blend (provided color);
// TODO Problem: colors for columns are specified, not rows,
// therefore there are only cell colors, not intra colors.
Color cell_color;
for (unsigned int col = 0; col < _columns.size (); ++col)
{
if (context.color ())
{
cell_color = row_color;
cell_color.blend (_color[row][col]);
}
cells.push_back (std::vector <std::string> ());
_columns[col]->render (cells[col], _data[row][col], widths[col], cell_color);
if (cells[col].size () > max_lines)
max_lines = cells[col].size ();
if (obfuscate)
if (_columns[col]->type () == "string")
for (unsigned int line = 0; line < cells[col].size (); ++line)
cells[col][line] = obfuscateText (cells[col][line]);
}
for (unsigned int i = 0; i < max_lines; ++i)
{
out += left_margin + (odd ? extra_odd : extra_even);
for (unsigned int col = 0; col < _columns.size (); ++col)
{
if (col)
{
if (row_color.nontrivial ())
out += row_color.colorize (intra);
else
out += (odd ? intra_odd : intra_even);
}
if (i < cells[col].size ())
out += cells[col][i];
else
{
if (context.color ())
{
cell_color = row_color;
cell_color.blend (_color[row][col]);
out += cell_color.colorize (std::string (widths[col], ' '));
}
else
out += std::string (widths[col], ' ');
}
}
out += (odd ? extra_odd : extra_even);
// Trim right.
out.erase (out.find_last_not_of (" ") + 1);
out += "\n";
// Stop if the line limit is exceeded.
if (++_lines >= _truncate_lines && _truncate_lines != 0)
return out;
}
cells.clear ();
// Stop if the row limit is exceeded.
if (++_rows >= _truncate_rows && _truncate_rows != 0)
return out;
}
return out;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,92 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_VIEWTEXT
#define INCLUDED_VIEWTEXT
#include <string>
#include <vector>
#include <Task.h>
#include <Color.h>
#include <Column.h>
class ViewText
{
public:
ViewText ();
~ViewText ();
// View specifications.
void add (Column* col) { _columns.push_back (col); }
void width (int width) { _width = width; }
void leftMargin (int margin) { _left_margin = margin; }
void colorHeader (Color& c) { _header = c; }
void colorOdd (Color& c) { _odd = c; }
void colorEven (Color& c) { _even = c; }
void intraPadding (int padding) { _intra_padding = padding; }
void intraColorOdd (Color& c) { _intra_odd = c; }
void intraColorEven (Color& c) { _intra_even = c; }
void extraPadding (int padding) { _extra_padding = padding; }
void extraColorOdd (Color& c) { _extra_odd = c; }
void extraColorEven (Color& c) { _extra_even = c; }
void truncateLines (int n) { _truncate_lines = n; }
void truncateRows (int n) { _truncate_rows = n; }
int lines () { return _lines; }
int rows () { return (int) _data.size (); }
// Data provision.
int addRow ();
void set (int, int, const std::string&, Color color = Color::nocolor);
void set (int, int, int, Color color = Color::nocolor);
void set (int, int, Color);
// View rendering.
std::string render ();
private:
std::vector <std::vector <std::string>> _data;
std::vector <std::vector <Color>> _color;
std::vector <Column*> _columns;
int _width;
int _left_margin;
Color _header;
Color _odd;
Color _even;
int _intra_padding;
Color _intra_odd;
Color _intra_even;
int _extra_padding;
Color _extra_odd;
Color _extra_even;
int _truncate_lines;
int _truncate_rows;
int _lines;
int _rows;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2013 - 2016, Göteborg Bit Factory.
// Copyright 2013 - 2020, Göteborg Bit Factory.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -30,12 +30,12 @@
#include <stdlib.h>
#include <string.h>
#include <Eval.h>
#include <Dates.h>
#include <Context.h>
#include <text.h>
#include <i18n.h>
Context context;
#include <Task.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
// Constants.
@@ -56,63 +56,49 @@ int main (int argc, char** argv)
try
{
bool infix = true;
Context globalContext;
Context::setContext (&globalContext);
// Same operating parameters as Context::staticInitialization.
Datetime::standaloneDateEnabled = false;
Datetime::standaloneTimeEnabled = false;
Duration::standaloneSecondsEnabled = false;
bool infix {true};
// Add a source for constants.
Eval e;
e.addSource (namedDates);
e.addSource (get);
// Combine all the arguments into one expression string.
std::string expression;
for (int i = 1; i < argc; i++)
{
if (!strcmp (argv[i], "-h") || ! strcmp (argv[i], "--help"))
{
std::cout << "\n"
std::cout << '\n'
<< "Usage: " << argv[0] << " [options] '<expression>'\n"
<< "\n"
<< '\n'
<< "Options:\n"
<< " -h|--help Display this usage\n"
<< " -d|--debug Debug mode\n"
<< " -i|--infix Infix expression (default)\n"
<< " -p|--postfix Postfix expression\n"
<< "\n";
<< '\n';
exit (1);
}
else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "--version"))
{
std::cout << "\n"
<< format (STRING_CMD_VERSION_BUILT, "calc", VERSION)
#if defined (DARWIN)
<< "darwin"
#elif defined (SOLARIS)
<< "solaris"
#elif defined (CYGWIN)
<< "cygwin"
#elif defined (HAIKU)
<< "haiku"
#elif defined (OPENBSD)
<< "openbsd"
#elif defined (FREEBSD)
<< "freebsd"
#elif defined (NETBSD)
<< "netbsd"
#elif defined (LINUX)
<< "linux"
#elif defined (KFREEBSD)
<< "gnu-kfreebsd"
#elif defined (GNUHURD)
<< "gnu-hurd"
#else
<< STRING_CMD_VERSION_UNKNOWN
#endif
<< "\n"
<< STRING_CMD_VERSION_COPY
<< "\n"
<< "\n"
<< STRING_CMD_VERSION_MIT
<< "\n"
<< "\n";
std::cout << '\n'
<< format ("calc {1} built for ", VERSION)
<< osName ()
<< '\n'
<< "Copyright (C) 2006 - 2020 P. Beckingham, F. Hernandez."
<< '\n'
<< '\n'
<< "Taskwarrior may be copied only under the terms of the MIT license, which may be found in the Taskwarrior source kit."
<< '\n'
<< '\n';
exit (1);
}
@@ -123,7 +109,8 @@ int main (int argc, char** argv)
else if (!strcmp (argv[i], "-p") || !strcmp (argv[i], "--postfix"))
infix = false;
else
expression += std::string (argv[i]) + " ";
expression += std::string (argv[i]) + ' ';
}
Variant result;
if (infix)
@@ -132,17 +119,17 @@ int main (int argc, char** argv)
e.evaluatePostfixExpression (expression, result);
// Show any debug output.
for (auto& i : context.debugMessages)
std::cout << i << "\n";
for (const auto& i : Context::getContext ().debugMessages)
std::cout << i << '\n';
// Show the result in string form.
std::cout << (std::string) result
<< "\n";
<< '\n';
}
catch (const std::string& error)
{
std::cerr << error << "\n";
std::cerr << error << '\n';
status = -1;
}

View File

@@ -1,8 +1,9 @@
cmake_minimum_required (VERSION 2.8)
cmake_minimum_required (VERSION 3.0)
include_directories (${CMAKE_SOURCE_DIR}
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/commands
${CMAKE_SOURCE_DIR}/src/columns
${CMAKE_SOURCE_DIR}/src/libshared/src
${TASK_INCLUDE_DIRS})
set (columns_SRCS Column.cpp Column.h
@@ -21,7 +22,6 @@ set (columns_SRCS Column.cpp Column.h
ColScheduled.cpp ColScheduled.h
ColStart.cpp ColStart.h
ColStatus.cpp ColStatus.h
ColString.cpp ColString.h
ColTags.cpp ColTags.h
ColTypeDate.cpp ColTypeDate.h
ColTypeDuration.cpp ColTypeDuration.h

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,13 +28,13 @@
#include <ColDepends.h>
#include <algorithm>
#include <Context.h>
#include <text.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <i18n.h>
#include <main.h>
#include <stdlib.h>
extern Context context;
#define STRING_COLUMN_LABEL_DEP "Depends"
////////////////////////////////////////////////////////////////////////////////
ColumnDepends::ColumnDepends ()
@@ -47,7 +47,7 @@ ColumnDepends::ColumnDepends ()
"indicator"};
_examples = {"1 2 10",
"[3]",
context.config.get ("dependency.indicator")};
Context::getContext ().config.get ("dependency.indicator")};
_hyphenate = false;
}
@@ -57,42 +57,40 @@ ColumnDepends::ColumnDepends ()
// Note that you can not determine which gets called first.
void ColumnDepends::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP) _label = _label.substr (0, context.config.get ("dependency.indicator").length ());
else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP) _label = STRING_COLUMN_LABEL_DEP_S;
if (_style == "indicator" && _label == STRING_COLUMN_LABEL_DEP) _label = _label.substr (0, Context::getContext ().config.get ("dependency.indicator").length ());
else if (_style == "count" && _label == STRING_COLUMN_LABEL_DEP) _label = "Dep";
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
void ColumnDepends::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
std::vector <Task> blocking;
dependencyGetBlocking (task, blocking);
if (_style == "indicator")
minimum = maximum = 0;
if (task.has (_name))
{
if (task.has ("depends"))
minimum = maximum = utf8_width (context.config.get ("dependency.indicator"));
else
minimum = maximum = 0;
}
else if (_style == "count")
{
minimum = maximum = 2 + format ((int) blocking.size ()).length ();
}
else if (_style == "default" ||
_style == "list")
{
minimum = maximum = 0;
if (task.has ("depends"))
if (_style == "indicator")
{
minimum = maximum = utf8_width (Context::getContext ().config.get ("dependency.indicator"));
}
else if (_style == "count")
{
minimum = maximum = 2 + format ((int) dependencyGetBlocking (task).size ()).length ();
}
else if (_style == "default" ||
_style == "list")
{
minimum = maximum = 0;
auto blocking = dependencyGetBlocking (task);
std::vector <int> blocking_ids;
for (auto& i : blocking)
blocking_ids.push_back (i.id);
std::string all;
join (all, " ", blocking_ids);
auto all = join (" ", blocking_ids);
maximum = all.length ();
unsigned int length;
@@ -104,8 +102,6 @@ void ColumnDepends::measure (Task& task, unsigned int& minimum, unsigned int& ma
}
}
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -118,31 +114,31 @@ void ColumnDepends::render (
if (task.has (_name))
{
if (_style == "indicator")
renderStringRight (lines, width, color, context.config.get ("dependency.indicator"));
else
{
std::vector <Task> blocking;
dependencyGetBlocking (task, blocking);
renderStringRight (lines, width, color, Context::getContext ().config.get ("dependency.indicator"));
}
if (_style == "count")
renderStringRight (lines, width, color, "[" + format (static_cast <int>(blocking.size ())) + "]");
else if (_style == "count")
{
renderStringRight (lines, width, color, '[' + format (static_cast <int>(dependencyGetBlocking (task).size ())) + ']');
}
else if (_style == "default" ||
_style == "list")
{
std::vector <int> blocking_ids;
for (const auto& t : blocking)
blocking_ids.push_back (t.id);
else if (_style == "default" ||
_style == "list")
{
auto blocking = dependencyGetBlocking (task);
std::string combined;
join (combined, " ", blocking_ids);
std::vector <int> blocking_ids;
for (const auto& t : blocking)
blocking_ids.push_back (t.id);
std::vector <std::string> all;
wrapText (all, combined, width, _hyphenate);
auto combined = join (" ", blocking_ids);
for (const auto& i : all)
renderStringLeft (lines, width, color, i);
}
std::vector <std::string> all;
wrapText (all, combined, width, _hyphenate);
for (const auto& i : all)
renderStringLeft (lines, width, color, i);
}
}
}
@@ -150,26 +146,22 @@ void ColumnDepends::render (
////////////////////////////////////////////////////////////////////////////////
void ColumnDepends::modify (Task& task, const std::string& value)
{
// Parse IDs
std::vector <std::string> deps;
split (deps, value, ',');
// Apply or remove dendencies in turn.
for (auto& dep : deps)
for (auto& dep : split (value, ','))
{
if (dep[0] == '-')
{
if (dep.length () == 37)
task.removeDependency (dep.substr (1));
else
task.removeDependency (strtol (dep.substr (1).c_str (), NULL, 10));
task.removeDependency (strtol (dep.substr (1).c_str (), nullptr, 10));
}
else
{
if (dep.length () == 36)
task.addDependency (dep);
else
task.addDependency (strtol (dep.c_str (), NULL, 10));
task.addDependency (strtol (dep.c_str (), nullptr, 10));
}
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -28,55 +28,54 @@
#include <ColDescription.h>
#include <stdlib.h>
#include <Context.h>
#include <ISO8601.h>
#include <text.h>
#include <Datetime.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <util.h>
#include <i18n.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ColumnDescription::ColumnDescription ()
{
_name = "description";
_style = "combined";
_label = STRING_COLUMN_LABEL_DESC;
_name = "description";
_style = "combined";
_label = "Description";
_modifiable = true;
_styles = {"combined",
"desc",
"oneline",
"truncated",
"count",
"truncated_count"};
_styles = {"combined",
"desc",
"oneline",
"truncated",
"count",
"truncated_count"};
_dateformat = context.config.get ("dateformat.annotation");
_dateformat = Context::getContext ().config.get ("dateformat.annotation");
if (_dateformat == "")
_dateformat = context.config.get ("dateformat");
_dateformat = Context::getContext ().config.get ("dateformat");
std::string t = ISO8601d ().toString (_dateformat);
std::string d = STRING_COLUMN_EXAMPLES_DESC;
std::string a1 = STRING_COLUMN_EXAMPLES_ANNO1;
std::string a2 = STRING_COLUMN_EXAMPLES_ANNO2;
std::string a3 = STRING_COLUMN_EXAMPLES_ANNO3;
std::string a4 = STRING_COLUMN_EXAMPLES_ANNO4;
std::string t = Datetime ().toString (_dateformat);
std::string d = "Move your clothes down on to the lower peg";
std::string a1 = "Immediately before your lunch";
std::string a2 = "If you are playing in the match this afternoon";
std::string a3 = "Before you write your letter home";
std::string a4 = "If you're not getting your hair cut";
_examples = {d + "\n " + t + " " + a1
+ "\n " + t + " " + a2
+ "\n " + t + " " + a3
+ "\n " + t + " " + a4,
_examples = {d + "\n " + t + ' ' + a1
+ "\n " + t + ' ' + a2
+ "\n " + t + ' ' + a3
+ "\n " + t + ' ' + a4,
d,
d + " " + t + " " + a1
+ " " + t + " " + a2
+ " " + t + " " + a3
+ " " + t + " " + a4,
d + ' ' + t + ' ' + a1
+ ' ' + t + ' ' + a2
+ ' ' + t + ' ' + a3
+ ' ' + t + ' ' + a4,
d.substr (0, 20) + "...",
d + " [4]",
d.substr (0, 20) + "... [4]"};
_hyphenate = context.config.getBoolean ("hyphenate");
_hyphenate = Context::getContext ().config.getBoolean ("hyphenate");
_indent = context.config.getInteger ("indent.annotation");
_indent = Context::getContext ().config.getInteger ("indent.annotation");
}
////////////////////////////////////////////////////////////////////////////////
@@ -96,13 +95,11 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
if (task.annotation_count)
{
unsigned int min_anno = _indent + ISO8601d::length (_dateformat);
unsigned int min_anno = _indent + Datetime::length (_dateformat);
if (min_anno > minimum)
minimum = min_anno;
std::map <std::string, std::string> annos;
task.getAnnotations (annos);
for (auto& i : annos)
for (auto& i : task.getAnnotations ())
{
unsigned int len = min_anno + 1 + utf8_width (i.second);
if (len > maximum)
@@ -126,10 +123,8 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
if (task.annotation_count)
{
auto min_anno = ISO8601d::length (_dateformat);
std::map <std::string, std::string> annos;
task.getAnnotations (annos);
for (auto& i : annos)
auto min_anno = Datetime::length (_dateformat);
for (auto& i : task.getAnnotations ())
maximum += min_anno + 1 + utf8_width (i.second);
}
}
@@ -155,9 +150,6 @@ void ColumnDescription::measure (Task& task, unsigned int& minimum, unsigned int
minimum = 4;
maximum = utf8_width (description) + 1 + 1 + format (task.annotation_count).length () + 1;
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -177,12 +169,10 @@ void ColumnDescription::render (
{
if (task.annotation_count)
{
std::map <std::string, std::string> annos;
task.getAnnotations (annos);
for (const auto& i : annos)
for (const auto& i : task.getAnnotations ())
{
ISO8601d dt (strtol (i.first.substr (11).c_str (), NULL, 10));
description += "\n" + std::string (_indent, ' ') + dt.toString (_dateformat) + " " + i.second;
Datetime dt (strtol (i.first.substr (11).c_str (), nullptr, 10));
description += '\n' + std::string (_indent, ' ') + dt.toString (_dateformat) + ' ' + i.second;
}
}
@@ -208,12 +198,10 @@ void ColumnDescription::render (
{
if (task.annotation_count)
{
std::map <std::string, std::string> annos;
task.getAnnotations (annos);
for (const auto& i : annos)
for (const auto& i : task.getAnnotations ())
{
ISO8601d dt (strtol (i.first.substr (11).c_str (), NULL, 10));
description += " " + dt.toString (_dateformat) + " " + i.second;
Datetime dt (strtol (i.first.substr (11).c_str (), nullptr, 10));
description += ' ' + dt.toString (_dateformat) + ' ' + i.second;
}
}
@@ -238,7 +226,7 @@ void ColumnDescription::render (
else if (_style == "count")
{
if (task.annotation_count)
description += " [" + format (task.annotation_count) + "]";
description += " [" + format (task.annotation_count) + ']';
std::vector <std::string> raw;
wrapText (raw, description, width, _hyphenate);
@@ -256,7 +244,7 @@ void ColumnDescription::render (
int len_annos = 0;
if (task.annotation_count)
{
annos_count = " [" + format (task.annotation_count) + "]";
annos_count = " [" + format (task.annotation_count) + ']';
len_annos = utf8_width (annos_count);
len += len_annos;
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,21 +20,19 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColDue.h>
#include <stdlib.h>
#include <text.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnDue::ColumnDue ()
{
_name = "due";
_label = STRING_COLUMN_LABEL_DUE;
_name = "due";
_modifiable = true;
_label = "Due";
}
////////////////////////////////////////////////////////////////////////////////
@@ -44,8 +42,8 @@ void ColumnDue::setStyle (const std::string& value)
{
Column::setStyle (value);
if (_style == "countdown" && _label == STRING_COLUMN_LABEL_DUE)
_label = STRING_COLUMN_LABEL_COUNT;
if (_style == "countdown" && _label == "Due")
_label = "Count";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,19 +20,18 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColEnd.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnEnd::ColumnEnd ()
{
_name = "end";
_label = STRING_COLUMN_LABEL_COMPLETE;
_label = "Completed";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,19 +20,19 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColEntry.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnEntry::ColumnEntry ()
{
_name = "entry";
_label = STRING_COLUMN_LABEL_ADDED;
_name = "entry";
_modifiable = true;
_label = "Added";
}
////////////////////////////////////////////////////////////////////////////////
@@ -40,11 +40,11 @@ ColumnEntry::ColumnEntry ()
// Note that you can not determine which gets called first.
void ColumnEntry::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "age" &&
_label == STRING_COLUMN_LABEL_ADDED)
_label = STRING_COLUMN_LABEL_AGE;
_label == "Added")
_label = "Age";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,21 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColID.h>
#include <math.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnID::ColumnID ()
{
_name = "id";
_style = "number";
_label = STRING_COLUMN_LABEL_ID;
_label = "ID";
_modifiable = false;
_styles = {"number"};
_examples = {"123"};
@@ -55,10 +54,6 @@ void ColumnID::measure (Task& task, unsigned int& minimum, unsigned int& maximum
else length = 1 + (int) log10 ((double) task.id); // Slow
minimum = maximum = length;
if (_style != "default" &&
_style != "number")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -68,6 +63,7 @@ void ColumnID::render (
int width,
Color& color)
{
// Completed and deleted tasks have no ID.
if (task.id)
renderInteger (lines, width, color, task.id);
else

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,20 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColIMask.h>
#include <math.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnIMask::ColumnIMask ()
{
_name = "imask";
_style = "number";
_label = STRING_COLUMN_LABEL_MASK_IDX;
_label = "Mask Index";
_modifiable = false;
_styles = {"number"};
_examples = {"12"};
@@ -46,15 +44,8 @@ ColumnIMask::ColumnIMask ()
void ColumnIMask::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
minimum = maximum = task.get ("imask").length ();
if (_style != "default" &&
_style != "number")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
minimum = maximum = task.get (_name).length ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -65,7 +56,7 @@ void ColumnIMask::render (
Color& color)
{
if (task.has (_name))
renderStringRight (lines, width, color, task.get ("imask"));
renderStringRight (lines, width, color, task.get (_name));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,20 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColMask.h>
#include <math.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnMask::ColumnMask ()
{
_name = "mask";
_style = "default";
_label = STRING_COLUMN_LABEL_MASK;
_label = "Mask";
_modifiable = false;
_styles = {"default"};
_examples = {"++++---"};
@@ -47,12 +45,7 @@ void ColumnMask::measure (Task& task, unsigned int& minimum, unsigned int& maxim
{
minimum = maximum = 0;
if (task.has (_name))
{
minimum = maximum = task.get ("mask").length ();
if (_style != "default")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
minimum = maximum = task.get (_name).length ();
}
////////////////////////////////////////////////////////////////////////////////
@@ -63,7 +56,7 @@ void ColumnMask::render (
Color& color)
{
if (task.has (_name))
renderStringLeft (lines, width, color, task.get ("mask"));
renderStringLeft (lines, width, color, task.get (_name));
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,19 +20,18 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColModified.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnModified::ColumnModified ()
{
_name = "modified";
_label = STRING_COLUMN_LABEL_MOD;
_label = "Modified";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,20 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColParent.h>
#include <math.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnParent::ColumnParent ()
{
_name = "parent";
_style = "long";
_label = STRING_COLUMN_LABEL_PARENT;
_label = "Parent task";
_modifiable = false;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
@@ -46,13 +44,10 @@ ColumnParent::ColumnParent ()
void ColumnParent::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -31,13 +31,11 @@
#include <Variant.h>
#include <Lexer.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <util.h>
#include <i18n.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -45,12 +43,12 @@ ColumnProject::ColumnProject ()
{
_name = "project";
_style = "full";
_label = STRING_COLUMN_LABEL_PROJECT;
_label = "Project";
_styles = {"full", "parent", "indented"};
_examples = {STRING_COLUMN_EXAMPLES_PROJ,
STRING_COLUMN_EXAMPLES_PAR,
STRING_COLUMN_EXAMPLES_IND};
_hyphenate = context.config.getBoolean ("hyphenate");
_examples = {"home.garden",
"home",
" home.garden"};
_hyphenate = Context::getContext ().config.getBoolean ("hyphenate");
}
////////////////////////////////////////////////////////////////////////////////
@@ -58,7 +56,6 @@ ColumnProject::ColumnProject ()
void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
std::string project = task.get (_name);
@@ -73,10 +70,6 @@ void ColumnProject::measure (Task& task, unsigned int& minimum, unsigned int& ma
{
project = indentProject (project, " ", '.');
}
else if (_style != "default" &&
_style != "full" &&
_style != "indented")
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
minimum = longestWord (project);
maximum = utf8_width (project);
@@ -124,20 +117,34 @@ void ColumnProject::modify (Task& task, const std::string& value)
if (lexer.token (domRef, type) &&
type == Lexer::Type::dom)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
try
{
Eval e;
e.addSource (domSource);
contextTask = task;
Variant v;
e.evaluateInfixExpression (value, v);
task.set (_name, (std::string) v);
context.debug (label + _name + " <-- '" + (std::string) v + "' <-- '" + value + "'");
Variant v;
e.evaluateInfixExpression (value, v);
task.set (_name, (std::string) v);
Context::getContext ().debug (label + _name + " <-- '" + (std::string) v + "' <-- '" + value + '\'');
}
catch (const std::string& e)
{
// If the expression failed because it didn't look like an expression,
// simply store it as-is.
if (e == "The value is not an expression.")
{
task.set (_name, value);
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
}
else
throw;
}
}
else
{
task.set (_name, value);
context.debug (label + _name + " <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,34 +20,33 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColRecur.h>
#include <Context.h>
#include <ISO8601.h>
#include <Duration.h>
#include <Eval.h>
#include <Variant.h>
#include <Lexer.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <i18n.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
ColumnRecur::ColumnRecur ()
{
_name = "recur";
_style = "duration";
_label = STRING_COLUMN_LABEL_RECUR;
_styles = {"duration", "indicator"};
_examples = {"weekly", context.config.get ("recurrence.indicator")};
_name = "recur";
_style = "duration";
_label = "Recur";
_modifiable = true;
_styles = {"duration", "indicator"};
_examples = {"weekly", Context::getContext ().config.get ("recurrence.indicator")};
}
////////////////////////////////////////////////////////////////////////////////
@@ -55,10 +54,10 @@ ColumnRecur::ColumnRecur ()
// Note that you can not determine which gets called first.
void ColumnRecur::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "indicator" && _label == STRING_COLUMN_LABEL_RECUR)
_label = _label.substr (0, context.config.get ("recurrence.indicator").length ());
if (_style == "indicator" && _label == "Recur")
_label = _label.substr (0, Context::getContext ().config.get ("recurrence.indicator").length ());
}
////////////////////////////////////////////////////////////////////////////////
@@ -66,23 +65,17 @@ void ColumnRecur::setStyle (const std::string& value)
void ColumnRecur::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default" ||
_style == "duration")
{
minimum = maximum = ISO8601p (task.get ("recur")).format ().length ();
minimum = maximum = Duration (task.get (_name)).formatISO ().length ();
}
else if (_style == "indicator")
{
if (task.has ("recur"))
minimum = maximum = utf8_width (context.config.get ("recurrence.indicator"));
else
minimum = maximum = 0;
minimum = maximum = utf8_width (Context::getContext ().config.get ("recurrence.indicator"));
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -97,10 +90,10 @@ void ColumnRecur::render (
{
if (_style == "default" ||
_style == "duration")
renderStringRight (lines, width, color, ISO8601p (task.get ("recur")).format ());
renderStringRight (lines, width, color, Duration (task.get (_name)).formatISO ());
else if (_style == "indicator")
renderStringRight (lines, width, color, context.config.get ("recurrence.indicator"));
renderStringRight (lines, width, color, Context::getContext ().config.get ("recurrence.indicator"));
}
}
@@ -115,7 +108,6 @@ void ColumnRecur::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
e.evaluateInfixExpression (value, evaluatedValue);
}
@@ -129,11 +121,11 @@ void ColumnRecur::modify (Task& task, const std::string& value)
{
// Store the raw value, for 'recur'.
std::string label = " MODIFICATION ";
context.debug (label + _name + " <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
task.set (_name, value);
}
else
throw format (STRING_TASK_INVALID_DUR, value);
throw format ("The duration value '{1}' is not supported.", value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,21 +20,18 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColScheduled.h>
#include <stdlib.h>
#include <text.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnScheduled::ColumnScheduled ()
{
_name = "scheduled";
_label = STRING_COLUMN_LABEL_SCHED;
_name = "scheduled";
_label = "Scheduled";
}
////////////////////////////////////////////////////////////////////////////////
@@ -42,10 +39,10 @@ ColumnScheduled::ColumnScheduled ()
// Note that you can not determine which gets called first.
void ColumnScheduled::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "countdown" && _label == STRING_COLUMN_LABEL_DUE)
_label = STRING_COLUMN_LABEL_COUNT;
if (_style == "countdown" && _label == "Scheduled")
_label = "Count";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,27 +20,23 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColStart.h>
#include <Context.h>
#include <text.h>
#include <utf8.h>
#include <i18n.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ColumnStart::ColumnStart ()
{
_name = "start";
_label = STRING_COLUMN_LABEL_STARTED;
_label = "Started";
_styles.push_back ("active");
_examples.push_back (context.config.get ("active.indicator"));
_examples.push_back (Context::getContext ().config.get ("active.indicator"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -48,10 +44,10 @@ ColumnStart::ColumnStart ()
// Note that you can not determine which gets called first.
void ColumnStart::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "active" && _label == STRING_COLUMN_LABEL_STARTED)
_label = STRING_COLUMN_LABEL_ACTIVE;
if (_style == "active" && _label == "Started")
_label = "A";
}
////////////////////////////////////////////////////////////////////////////////
@@ -59,18 +55,14 @@ void ColumnStart::setStyle (const std::string& value)
void ColumnStart::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "active")
{
if (task.has ("start"))
minimum = maximum = utf8_width (context.config.get ("active.indicator"));
else
minimum = maximum = 0;
}
minimum = maximum = utf8_width (Context::getContext ().config.get ("active.indicator"));
else
ColumnTypeDate::measure (task, minimum, maximum);
// TODO Throw on bad format.
}
}
@@ -86,7 +78,7 @@ void ColumnStart::render (
if (_style == "active")
{
if (! task.has ("end"))
renderStringRight (lines, width, color, context.config.get ("active.indicator"));
renderStringRight (lines, width, color, Context::getContext ().config.get ("active.indicator"));
}
else
ColumnTypeDate::render (lines, task, width, color);

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,14 +20,13 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColStatus.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
#include <utf8.h>
////////////////////////////////////////////////////////////////////////////////
@@ -35,10 +34,10 @@ ColumnStatus::ColumnStatus ()
{
_name = "status";
_style = "long";
_label = STRING_COLUMN_LABEL_STATUS;
_label = "Status";
_styles = {"long", "short"};
_examples = {STRING_COLUMN_LABEL_STAT_PE,
STRING_COLUMN_LABEL_STAT_P};
_examples = {"Pending",
"P"};
}
////////////////////////////////////////////////////////////////////////////////
@@ -46,10 +45,10 @@ ColumnStatus::ColumnStatus ()
// Note that you can not determine which gets called first.
void ColumnStatus::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "short" && _label == STRING_COLUMN_LABEL_STATUS)
_label = STRING_COLUMN_LABEL_STAT;
if (_style == "short" && _label == "Status")
_label = "St";
}
////////////////////////////////////////////////////////////////////////////////
@@ -62,20 +61,18 @@ void ColumnStatus::measure (Task& task, unsigned int& minimum, unsigned int& max
_style == "long")
{
if (status == Task::pending)
minimum = maximum = utf8_width (STRING_COLUMN_LABEL_STAT_PE);
minimum = maximum = utf8_width ("Pending");
else if (status == Task::deleted)
minimum = maximum = utf8_width (STRING_COLUMN_LABEL_STAT_DE);
minimum = maximum = utf8_width ("Deleted");
else if (status == Task::waiting)
minimum = maximum = utf8_width (STRING_COLUMN_LABEL_STAT_WA);
minimum = maximum = utf8_width ("Waiting");
else if (status == Task::completed)
minimum = maximum = utf8_width (STRING_COLUMN_LABEL_STAT_CO);
minimum = maximum = utf8_width ("Completed");
else if (status == Task::recurring)
minimum = maximum = utf8_width (STRING_COLUMN_LABEL_STAT_RE);
minimum = maximum = utf8_width ("Recurring");
}
else if (_style == "short")
minimum = maximum = 1;
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
@@ -91,20 +88,20 @@ void ColumnStatus::render (
if (_style == "default" ||
_style == "long")
{
if (status == Task::pending) value = STRING_COLUMN_LABEL_STAT_PE;
else if (status == Task::completed) value = STRING_COLUMN_LABEL_STAT_CO;
else if (status == Task::deleted) value = STRING_COLUMN_LABEL_STAT_DE;
else if (status == Task::waiting) value = STRING_COLUMN_LABEL_STAT_WA;
else if (status == Task::recurring) value = STRING_COLUMN_LABEL_STAT_RE;
if (status == Task::pending) value = "Pending";
else if (status == Task::completed) value = "Completed";
else if (status == Task::deleted) value = "Deleted";
else if (status == Task::waiting) value = "Waiting";
else if (status == Task::recurring) value = "Recurring";
}
else if (_style == "short")
{
if (status == Task::pending) value = STRING_COLUMN_LABEL_STAT_P;
else if (status == Task::completed) value = STRING_COLUMN_LABEL_STAT_C;
else if (status == Task::deleted) value = STRING_COLUMN_LABEL_STAT_D;
else if (status == Task::waiting) value = STRING_COLUMN_LABEL_STAT_W;
else if (status == Task::recurring) value = STRING_COLUMN_LABEL_STAT_R;
if (status == Task::pending) value = "P";
else if (status == Task::completed) value = "C";
else if (status == Task::deleted) value = "D";
else if (status == Task::waiting) value = "W";
else if (status == Task::recurring) value = "R";
}
renderStringLeft (lines, width, color, value);

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,114 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColString.h>
#include <Context.h>
#include <text.h>
#include <i18n.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ColumnString::ColumnString ()
{
_name = "string";
_type = "string";
_style = "left";
_label = "";
_styles = {"left",
"right",
"left_fixed",
"right_fixed"};
_examples = {"Hello (wrapped) ",
" Hello (wrapped)",
"Hello (no-wrap) ",
" Hello (no-wrap)"};
_hyphenate = context.config.getBoolean ("hyphenate");
}
////////////////////////////////////////////////////////////////////////////////
// ColumnString is unique - it copies the report name into the label. This is
// a kludgy reuse of an otherwise unused member.
void ColumnString::setReport (const std::string& value)
{
_report = _label = value;
}
////////////////////////////////////////////////////////////////////////////////
// Set the minimum and maximum widths for the value.
//
void ColumnString::measure (const std::string& value, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (_style == "left" ||
_style == "right" ||
_style == "default")
{
std::string stripped = Color::strip (value);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
}
else if (_style == "left_fixed" ||
_style == "right_fixed")
minimum = maximum = strippedLength (value);
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////
void ColumnString::render (
std::vector <std::string>& lines,
const std::string& value,
int width,
Color& color)
{
if (_style == "default" || _style == "left")
{
std::vector <std::string> raw;
wrapText (raw, value, width, _hyphenate);
for (auto& i : raw)
renderStringLeft (lines, width, color, i);
}
else if (_style == "right")
{
std::vector <std::string> raw;
wrapText (raw, value, width, _hyphenate);
for (auto& i : raw)
renderStringRight (lines, width, color, i);
}
else if (_style == "left_fixed")
renderStringLeft (lines, width, color, value);
else if (_style == "right_fixed")
renderStringRight (lines, width, color, value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,49 +0,0 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, 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
//
////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDED_COLSTRING
#define INCLUDED_COLSTRING
#include <vector>
#include <string>
#include <Column.h>
#include <Color.h>
#include <Task.h>
class ColumnString : public Column
{
public:
ColumnString ();
void setReport (const std::string&);
void measure (const std::string&, unsigned int&, unsigned int&);
void render (std::vector <std::string>&, const std::string&, int, Color&);
private:
bool _hyphenate;
};
#endif
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -31,13 +31,11 @@
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <i18n.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <main.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -45,10 +43,10 @@ ColumnTags::ColumnTags ()
{
_name = "tags";
_style = "list";
_label = STRING_COLUMN_LABEL_TAGS;
_label = "Tags";
_styles = {"list", "indicator", "count"};
_examples = {STRING_COLUMN_EXAMPLES_TAGS,
context.config.get ("tag.indicator"),
_examples = {"home @chore next",
Context::getContext ().config.get ("tag.indicator"),
"[2]"};
_hyphenate = false;
}
@@ -58,15 +56,15 @@ ColumnTags::ColumnTags ()
// Note that you can not determine which gets called first.
void ColumnTags::setStyle (const std::string& value)
{
_style = value;
Column::setStyle (value);
if (_style == "indicator" &&
_label == STRING_COLUMN_LABEL_TAGS)
_label = _label.substr (0, context.config.get ("tag.indicator").length ());
_label == "Tags")
_label = _label.substr (0, Context::getContext ().config.get ("tag.indicator").length ());
else if (_style == "count" &&
_label == STRING_COLUMN_LABEL_TAGS)
_label = STRING_COLUMN_LABEL_TAG;
_label == "Tags")
_label = "Tag";
}
////////////////////////////////////////////////////////////////////////////////
@@ -74,15 +72,11 @@ void ColumnTags::setStyle (const std::string& value)
void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "indicator")
{
if (task.has ("tags"))
minimum = maximum = utf8_width (context.config.get ("tag.indicator"));
else
minimum = maximum = 0;
minimum = maximum = utf8_width (Context::getContext ().config.get ("tag.indicator"));
}
else if (_style == "count")
{
@@ -96,8 +90,7 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
// Find the widest tag.
if (tags.find (',') != std::string::npos)
{
std::vector <std::string> all;
split (all, tags, ',');
auto all = split (tags, ',');
for (const auto& tag : all)
{
auto length = utf8_width (tag);
@@ -112,8 +105,6 @@ void ColumnTags::measure (Task& task, unsigned int& minimum, unsigned int& maxim
else
minimum = maximum = utf8_width (tags);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -132,10 +123,9 @@ void ColumnTags::render (
{
if (tags.find (',') != std::string::npos)
{
std::vector <std::string> all;
split (all, tags, ',');
auto all = split (tags, ',');
std::sort (all.begin (), all.end ());
join (tags, " ", all);
tags = join (" ", all);
all.clear ();
wrapText (all, tags, width, _hyphenate);
@@ -148,13 +138,12 @@ void ColumnTags::render (
}
else if (_style == "indicator")
{
renderStringRight (lines, width, color, context.config.get ("tag.indicator"));
renderStringRight (lines, width, color, Context::getContext ().config.get ("tag.indicator"));
}
else if (_style == "count")
{
std::vector <std::string> all;
split (all, tags, ',');
renderStringRight (lines, width, color, "[" + format (static_cast <int> (all.size ())) + "]");
auto all = split (tags, ',');
renderStringRight (lines, width, color, '[' + format (static_cast <int> (all.size ())) + ']');
}
}
}
@@ -165,12 +154,9 @@ void ColumnTags::modify (Task& task, const std::string& value)
std::string label = " MODIFICATION ";
// TW-1701
task.set ("tags", "");
task.set (_name, "");
std::vector <std::string> tags;
split (tags, value, ',');
for (auto& tag : tags)
for (auto& tag : split (value, ','))
{
// If it's a DOM ref, eval it first.
Lexer lexer (tag);
@@ -181,18 +167,17 @@ void ColumnTags::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
Variant v;
e.evaluateInfixExpression (value, v);
task.addTag ((std::string) v);
context.debug (label + "tags <-- '" + (std::string) v + "' <-- '" + tag + "'");
Context::getContext ().debug (label + "tags <-- '" + (std::string) v + "' <-- '" + tag + '\'');
}
else
{
task.addTag (tag);
context.debug (label + "tags <-- '" + tag + "'");
Context::getContext ().debug (label + "tags <-- '" + tag + '\'');
}
feedback_special_tags (task, tag);

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,20 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColTypeDate.h>
#include <Context.h>
#include <ISO8601.h>
#include <Datetime.h>
#include <Duration.h>
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -54,16 +52,16 @@ ColumnTypeDate::ColumnTypeDate ()
"remaining",
"countdown"};
ISO8601d now;
Datetime now;
now -= 125; // So that "age" is non-zero.
_examples = {now.toString (context.config.get ("dateformat")),
_examples = {now.toString (Context::getContext ().config.get ("dateformat")),
format (now.toJulian (), 13, 12),
now.toEpochString (),
now.toISO (),
ISO8601p (ISO8601d () - now).formatVague (),
"-" + ISO8601p (ISO8601d () - now).formatVague (),
Duration (Datetime () - now).formatVague (true),
'-' + Duration (Datetime () - now).formatVague (true),
"",
ISO8601p (ISO8601d () - now).format ()};
Duration (Datetime () - now).format ()};
}
////////////////////////////////////////////////////////////////////////////////
@@ -71,10 +69,9 @@ ColumnTypeDate::ColumnTypeDate ()
void ColumnTypeDate::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
ISO8601d date (task.get_date (_name));
Datetime date (task.get_date (_name));
if (_style == "default" ||
_style == "formatted")
@@ -83,19 +80,18 @@ void ColumnTypeDate::measure (Task& task, unsigned int& minimum, unsigned int& m
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat.
std::string format = context.config.get ("report." + _report + ".dateformat");
std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
format = Context::getContext ().config.get ("dateformat");
minimum = maximum = ISO8601d::length (format);
minimum = maximum = Datetime::length (format);
}
else if (_style == "countdown")
{
ISO8601d now;
if (now > date)
minimum = maximum = ISO8601p (now - date).formatVague ().length ();
Datetime now;
minimum = maximum = Duration (now - date).formatVague (true).length ();
}
else if (_style == "julian")
{
@@ -111,28 +107,26 @@ void ColumnTypeDate::measure (Task& task, unsigned int& minimum, unsigned int& m
}
else if (_style == "age")
{
ISO8601d now;
Datetime now;
if (now > date)
minimum = maximum = ISO8601p (now - date).formatVague ().length ();
minimum = maximum = Duration (now - date).formatVague (true).length ();
else
minimum = maximum = ISO8601p (date - now).formatVague ().length () + 1;
minimum = maximum = Duration (date - now).formatVague (true).length () + 1;
}
else if (_style == "relative")
{
ISO8601d now;
Datetime now;
if (now < date)
minimum = maximum = ISO8601p (date - now).formatVague ().length ();
minimum = maximum = Duration (date - now).formatVague (true).length ();
else
minimum = maximum = ISO8601p (now - date).formatVague ().length () + 1;
minimum = maximum = Duration (now - date).formatVague (true).length () + 1;
}
else if (_style == "remaining")
{
ISO8601d now;
Datetime now;
if (date > now)
minimum = maximum = ISO8601p (date - now).formatVague ().length ();
minimum = maximum = Duration (date - now).formatVague (true).length ();
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -145,7 +139,7 @@ void ColumnTypeDate::render (
{
if (task.has (_name))
{
ISO8601d date (task.get_date (_name));
Datetime date (task.get_date (_name));
if (_style == "default" ||
_style == "formatted")
@@ -154,21 +148,20 @@ void ColumnTypeDate::render (
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
std::string format = context.config.get ("report." + _report + ".dateformat");
std::string format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
{
format = context.config.get ("dateformat.report");
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
format = Context::getContext ().config.get ("dateformat");
}
renderStringLeft (lines, width, color, date.toString (format));
}
else if (_style == "countdown")
{
ISO8601d now;
if (now > date)
renderStringRight (lines, width, color, ISO8601p (now - date).formatVague ());
Datetime now;
renderStringRight (lines, width, color, Duration (now - date).formatVague (true));
}
else if (_style == "julian")
renderStringRight (lines, width, color, format (date.toJulian (), 13, 12));
@@ -181,30 +174,36 @@ void ColumnTypeDate::render (
else if (_style == "age")
{
ISO8601d now;
Datetime now;
if (now > date)
renderStringLeft (lines, width, color, ISO8601p (now - date).formatVague ());
renderStringRight (lines, width, color, Duration (now - date).formatVague (true));
else
renderStringLeft (lines, width, color, "-" + ISO8601p (date - now).formatVague ());
renderStringRight (lines, width, color, '-' + Duration (date - now).formatVague (true));
}
else if (_style == "relative")
{
ISO8601d now;
Datetime now;
if (now < date)
renderStringLeft (lines, width, color, ISO8601p (date - now).formatVague ());
renderStringRight (lines, width, color, Duration (date - now).formatVague (true));
else
renderStringLeft (lines, width, color, "-" + ISO8601p (now - date).formatVague ());
renderStringRight (lines, width, color, '-' + Duration (now - date).formatVague (true));
}
else if (_style == "remaining")
{
ISO8601d now;
Datetime now;
if (date > now)
renderStringRight (lines, width, color, ISO8601p (date - now).formatVague ());
renderStringRight (lines, width, color, Duration (date - now).formatVague (true));
}
}
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDate::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeDate::modify (Task& task, const std::string& value)
{
@@ -214,7 +213,6 @@ void ColumnTypeDate::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
e.evaluateInfixExpression (value, evaluatedValue);
}
@@ -228,21 +226,21 @@ void ColumnTypeDate::modify (Task& task, const std::string& value)
std::string label = " MODIFICATION ";
if (evaluatedValue.type () == Variant::type_duration)
{
context.debug (label + _name + " <-- '" + format ("{1}", format (evaluatedValue.get_duration ())) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + "'");
Variant now;
if (namedDates ("now", now))
evaluatedValue += now;
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", format (evaluatedValue.get_duration ())) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
Datetime date_now;
Variant now (date_now.toEpoch (), Variant::type_date);
evaluatedValue += now;
}
else
{
evaluatedValue.cast (Variant::type_date);
context.debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + format ("{1}", evaluatedValue.get_date ()) + "' <-- '" + (std::string) evaluatedValue + "' <-- '" + value + '\'');
}
// If a date doesn't parse (2/29/2014) then it evaluates to zero.
if (value != "" &&
evaluatedValue.get_date () == 0)
throw format (STRING_DATE_INVALID_FORMAT, value, Variant::dateFormat);
throw format ("'{1}' is not a valid date in the '{2}' format.", value, Variant::dateFormat);
task.set (_name, evaluatedValue.get_date ());
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -39,6 +39,7 @@ public:
ColumnTypeDate ();
virtual void measure (Task&, unsigned int&, unsigned int&);
virtual void render (std::vector <std::string>&, Task&, int, Color&);
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
};

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -30,11 +30,8 @@
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -43,6 +40,12 @@ ColumnTypeDuration::ColumnTypeDuration ()
_type = "duration";
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeDuration::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeDuration::modify (Task& task, const std::string& value)
{
@@ -52,7 +55,6 @@ void ColumnTypeDuration::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
e.evaluateInfixExpression (value, evaluatedValue);
}
@@ -68,11 +70,11 @@ void ColumnTypeDuration::modify (Task& task, const std::string& value)
if (evaluatedValue.type () == Variant::type_duration)
{
// Store the raw value, for 'recur'.
context.debug (label + _name + " <-- " + (std::string) evaluatedValue + " <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- " + (std::string) evaluatedValue + " <-- '" + value + '\'');
task.set (_name, evaluatedValue);
}
else
throw format (STRING_TASK_INVALID_DUR, value);
throw format ("The duration value '{1}' is not supported.", value);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -35,6 +35,7 @@ class ColumnTypeDuration : public Column
{
public:
ColumnTypeDuration ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
};

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -30,11 +30,8 @@
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -43,6 +40,12 @@ ColumnTypeNumeric::ColumnTypeNumeric ()
_type = "numeric";
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeNumeric::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeNumeric::modify (Task& task, const std::string& value)
{
@@ -52,7 +55,6 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
e.evaluateInfixExpression (value, evaluatedValue);
}
@@ -63,12 +65,12 @@ void ColumnTypeNumeric::modify (Task& task, const std::string& value)
}
std::string label = " MODIFICATION ";
context.debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + evaluatedValue.get_string () + "' <-- '" + value + '\'');
// If the result is not readily convertible to a numeric value, then this is
// an error.
if (evaluatedValue.type () == Variant::type_string)
throw format (STRING_UDA_NUMERIC, evaluatedValue.get_string ());
throw format ("The value '{1}' is not a valid numeric value.", evaluatedValue.get_string ());
task.set (_name, evaluatedValue);
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -35,6 +35,7 @@ class ColumnTypeNumeric : public Column
{
public:
ColumnTypeNumeric ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
};

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -30,11 +30,10 @@
#include <Eval.h>
#include <Variant.h>
#include <Filter.h>
#include <Dates.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
#define STRING_INVALID_MOD "The '{1}' attribute does not allow a value of '{2}'."
extern Context context;
extern Task& contextTask;
////////////////////////////////////////////////////////////////////////////////
@@ -43,6 +42,12 @@ ColumnTypeString::ColumnTypeString ()
_type = "string";
}
////////////////////////////////////////////////////////////////////////////////
bool ColumnTypeString::validate (const std::string& input) const
{
return input.length () ? true : false;
}
////////////////////////////////////////////////////////////////////////////////
void ColumnTypeString::modify (Task& task, const std::string& value)
{
@@ -57,7 +62,6 @@ void ColumnTypeString::modify (Task& task, const std::string& value)
{
Eval e;
e.addSource (domSource);
e.addSource (namedDates);
contextTask = task;
Variant v;
@@ -66,7 +70,7 @@ void ColumnTypeString::modify (Task& task, const std::string& value)
if (validate (strValue))
{
task.set (_name, strValue);
context.debug (label + _name + " <-- '" + strValue + "' <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + strValue + "' <-- '" + value + '\'');
}
else
throw format (STRING_INVALID_MOD, _name, value);
@@ -76,7 +80,7 @@ void ColumnTypeString::modify (Task& task, const std::string& value)
if (validate (value))
{
task.set (_name, value);
context.debug (label + _name + " <-- '" + value + "'");
Context::getContext ().debug (label + _name + " <-- '" + value + '\'');
}
else
throw format (STRING_INVALID_MOD, _name, value);

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
@@ -35,6 +35,7 @@ class ColumnTypeString : public Column
{
public:
ColumnTypeString ();
virtual bool validate (const std::string&) const;
virtual void modify (Task&, const std::string&);
};

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,30 +20,30 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColUDA.h>
#include <Context.h>
#include <ISO8601.h>
#include <text.h>
#include <Datetime.h>
#include <Duration.h>
#include <shared.h>
#include <format.h>
#include <utf8.h>
#include <i18n.h>
#include <stdlib.h>
extern Context context;
////////////////////////////////////////////////////////////////////////////////
ColumnUDAString::ColumnUDAString ()
{
_name = "<uda>";
_style = "default";
_label = "";
_uda = true;
_hyphenate = true;
_styles = {_style, "indicator"};
_name = "<uda>"; // Gets overwritten at runtime.
_style = "default";
_label = "";
_modifiable = true;
_uda = true;
_hyphenate = true;
_styles = {_style, "indicator"};
}
////////////////////////////////////////////////////////////////////////////////
@@ -68,7 +68,6 @@ bool ColumnUDAString::validate (const std::string& value) const
void ColumnUDAString::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
@@ -76,26 +75,19 @@ void ColumnUDAString::measure (Task& task, unsigned int& minimum, unsigned int&
std::string value = task.get (_name);
if (value != "")
{
std::string stripped = Color::strip (value);
auto stripped = Color::strip (value);
maximum = longestLine (stripped);
minimum = longestWord (stripped);
}
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
minimum = maximum = utf8_width (indicator);
}
else
minimum = maximum = 0;
minimum = maximum = utf8_width (indicator);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -119,14 +111,11 @@ void ColumnUDAString::render (
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
renderStringRight (lines, width, color, indicator);
}
renderStringRight (lines, width, color, indicator);
}
}
}
@@ -164,30 +153,22 @@ bool ColumnUDANumeric::validate (const std::string& value) const
void ColumnUDANumeric::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
std::string value = task.get (_name);
auto value = task.get (_name);
if (value != "")
minimum = maximum = value.length ();
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
minimum = maximum = utf8_width (indicator);
}
else
minimum = maximum = 0;
minimum = maximum = utf8_width (indicator);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -202,19 +183,16 @@ void ColumnUDANumeric::render (
{
if (_style == "default")
{
std::string value = task.get (_name);
auto value = task.get (_name);
renderStringRight (lines, width, color, value);
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
renderStringRight (lines, width, color, indicator);
}
renderStringRight (lines, width, color, indicator);
}
}
}
@@ -252,43 +230,35 @@ bool ColumnUDADate::validate (const std::string& value) const
void ColumnUDADate::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
std::string value = task.get (_name);
auto value = task.get (_name);
if (value != "")
{
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat
ISO8601d date ((time_t) strtol (value.c_str (), NULL, 10));
std::string format = context.config.get ("report." + _report + ".dateformat");
Datetime date ((time_t) strtol (value.c_str (), nullptr, 10));
auto format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
format = context.config.get ("dateformat.report");
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
format = Context::getContext ().config.get ("dateformat");
minimum = maximum = ISO8601d::length (format);
minimum = maximum = Datetime::length (format);
}
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
minimum = maximum = utf8_width (indicator);
}
else
minimum = maximum = 0;
minimum = maximum = utf8_width (indicator);
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -303,32 +273,29 @@ void ColumnUDADate::render (
{
if (_style == "default")
{
std::string value = task.get (_name);
auto value = task.get (_name);
// Determine the output date format, which uses a hierarchy of definitions.
// rc.report.<report>.dateformat
// rc.dateformat.report
// rc.dateformat.
std::string format = context.config.get ("report." + _report + ".dateformat");
auto format = Context::getContext ().config.get ("report." + _report + ".dateformat");
if (format == "")
{
format = context.config.get ("dateformat.report");
format = Context::getContext ().config.get ("dateformat.report");
if (format == "")
format = context.config.get ("dateformat");
format = Context::getContext ().config.get ("dateformat");
}
renderStringLeft (lines, width, color, ISO8601d ((time_t) strtol (value.c_str (), NULL, 10)).toString (format));
renderStringLeft (lines, width, color, Datetime ((time_t) strtol (value.c_str (), nullptr, 10)).toString (format));
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
renderStringRight (lines, width, color, indicator);
}
renderStringRight (lines, width, color, indicator);
}
}
}
@@ -366,20 +333,19 @@ bool ColumnUDADuration::validate (const std::string& value) const
void ColumnUDADuration::measure (Task& task, unsigned int& minimum, unsigned int& maximum)
{
minimum = maximum = 0;
if (task.has (_name))
{
if (_style == "default")
{
std::string value = task.get (_name);
auto value = task.get (_name);
if (value != "")
minimum = maximum = ISO8601p (value).format ().length ();
minimum = maximum = Duration (value).formatISO ().length ();
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
@@ -388,8 +354,6 @@ void ColumnUDADuration::measure (Task& task, unsigned int& minimum, unsigned int
else
minimum = maximum = 0;
}
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
}
@@ -404,19 +368,16 @@ void ColumnUDADuration::render (
{
if (_style == "default")
{
std::string value = task.get (_name);
renderStringRight (lines, width, color, ISO8601p (value).format ());
auto value = task.get (_name);
renderStringRight (lines, width, color, Duration (value).formatISO ());
}
else if (_style == "indicator")
{
if (task.has (_name))
{
auto indicator = context.config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
auto indicator = Context::getContext ().config.get ("uda." + _name + ".indicator");
if (indicator == "")
indicator = "U";
renderStringRight (lines, width, color, indicator);
}
renderStringRight (lines, width, color, indicator);
}
}
}

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,22 +20,20 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColUUID.h>
#include <math.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUUID::ColumnUUID ()
{
_name = "uuid";
_style = "long";
_label = STRING_COLUMN_LABEL_UUID;
_label = "UUID";
_modifiable = false;
_styles = {"long", "short"};
_examples = {"f30cb9c3-3fc0-483f-bfb2-3bf134f00694", "f30cb9c3"};
@@ -45,10 +43,10 @@ ColumnUUID::ColumnUUID ()
// Set the minimum and maximum widths for the value.
void ColumnUUID::measure (Task&, unsigned int& minimum, unsigned int& maximum)
{
// Mandatory attribute, no need to check the value.
if (_style == "default" || _style == "long") minimum = maximum = 36;
else if (_style == "short") minimum = maximum = 8;
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,19 +20,18 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColUntil.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUntil::ColumnUntil ()
{
_name = "until";
_label = STRING_COLUMN_LABEL_UNTIL;
_name = "until";
_label = "Until";
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,23 +20,23 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColUrgency.h>
#include <text.h>
#include <i18n.h>
#include <format.h>
////////////////////////////////////////////////////////////////////////////////
ColumnUrgency::ColumnUrgency ()
{
_name = "urgency";
_style = "real";
_label = STRING_COLUMN_LABEL_URGENCY;
_styles = {"real", "integer"};
_examples = {"4.6", "4"};
_name = "urgency";
_style = "real";
_label = "Urgency";
_modifiable = false;
_styles = {"real", "integer"};
_examples = {"4.6", "4"};
}
////////////////////////////////////////////////////////////////////////////////
@@ -48,9 +48,6 @@ void ColumnUrgency::measure (Task& task, unsigned int& minimum, unsigned int& ma
else if (_style == "integer")
minimum = maximum = format ((int)task.urgency ()).length ();
else
throw format (STRING_COLUMN_BAD_FORMAT, _name, _style);
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,7 +20,7 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,6 +1,6 @@
////////////////////////////////////////////////////////////////////////////////
//
// Copyright 2006 - 2016, Paul Beckingham, Federico Hernandez.
// Copyright 2006 - 2020, 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
@@ -20,19 +20,18 @@
// 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
// https://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////
#include <cmake.h>
#include <ColWait.h>
#include <i18n.h>
////////////////////////////////////////////////////////////////////////////////
ColumnWait::ColumnWait ()
{
_name = "wait";
_label = STRING_COLUMN_LABEL_WAIT;
_name = "wait";
_label = "Wait";
}
////////////////////////////////////////////////////////////////////////////////

Some files were not shown because too many files have changed in this diff Show More