Merge branch '2.5.2' into master
This commit is contained in:
1
src/.gitignore
vendored
1
src/.gitignore
vendored
@@ -3,3 +3,4 @@ Makefile.in
|
||||
debug
|
||||
calc
|
||||
lex
|
||||
liblibshared.a
|
||||
|
||||
224
src/CLI2.cpp
224
src/CLI2.cpp
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
603
src/Color.cpp
603
src/Color.cpp
@@ -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 "";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
75
src/Color.h
75
src/Color.h
@@ -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
|
||||
637
src/Config.cpp
637
src/Config.cpp
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
70
src/Config.h
70
src/Config.h
@@ -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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
666
src/Context.cpp
666
src/Context.cpp
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
456
src/DOM.cpp
456
src/DOM.cpp
@@ -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 ();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
43
src/DOM.h
43
src/DOM.h
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
519
src/Dates.cpp
519
src/Dates.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
63
src/Eval.cpp
63
src/Eval.cpp
@@ -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 ("[1;37;42mFILTER[0m Infix " + dump (tokens));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m Infix " + dump (tokens));
|
||||
infixParse (tokens);
|
||||
if (_debug)
|
||||
context.debug ("[1;37;42mFILTER[0m Infix parsed " + dump (tokens));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m Infix parsed " + dump (tokens));
|
||||
|
||||
// Convert infix --> postfix.
|
||||
infixToPostfix (tokens);
|
||||
if (_debug)
|
||||
context.debug ("[1;37;42mFILTER[0m Postfix " + dump (tokens));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m 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 ("[1;37;42mFILTER[0m Postfix " + dump (tokens));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m 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 ("[1;37;42mFILTER[0m Infix " + dump (_compiled));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m Infix " + dump (_compiled));
|
||||
infixParse (_compiled);
|
||||
if (_debug)
|
||||
context.debug ("[1;37;42mFILTER[0m Infix parsed " + dump (_compiled));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m Infix parsed " + dump (_compiled));
|
||||
|
||||
// Convert infix --> postfix.
|
||||
infixToPostfix (_compiled);
|
||||
if (_debug)
|
||||
context.debug ("[1;37;42mFILTER[0m Postfix " + dump (_compiled));
|
||||
Context::getContext ().debug ("[1;37;42mFILTER[0m 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 ();
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
972
src/FS.cpp
972
src/FS.cpp
@@ -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
145
src/FS.h
@@ -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
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
180
src/Hooks.cpp
180
src/Hooks.cpp
@@ -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;
|
||||
|
||||
12
src/Hooks.h
12
src/Hooks.h
@@ -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;
|
||||
|
||||
|
||||
2069
src/ISO8601.cpp
2069
src/ISO8601.cpp
File diff suppressed because it is too large
Load Diff
178
src/ISO8601.h
178
src/ISO8601.h
@@ -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
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
494
src/JSON.cpp
494
src/JSON.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
135
src/JSON.h
135
src/JSON.h
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
262
src/Lexer.cpp
262
src/Lexer.cpp
@@ -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 (
|
||||
|
||||
@@ -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);
|
||||
|
||||
109
src/Msg.cpp
109
src/Msg.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
53
src/Msg.h
53
src/Msg.h
@@ -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
|
||||
|
||||
626
src/Nibbler.cpp
626
src/Nibbler.cpp
@@ -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)
|
||||
+ "›";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
159
src/RX.cpp
159
src/RX.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
58
src/RX.h
58
src/RX.h
@@ -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
|
||||
|
||||
261
src/TDB2.cpp
261
src/TDB2.cpp
@@ -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 (" ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
501
src/Task.cpp
501
src/Task.cpp
File diff suppressed because it is too large
Load Diff
38
src/Task.h
38
src/Task.h
@@ -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
|
||||
|
||||
113
src/Timer.cpp
113
src/Timer.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
55
src/Timer.h
55
src/Timer.h
@@ -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
|
||||
130
src/Variant.cpp
130
src/Variant.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
315
src/ViewText.cpp
315
src/ViewText.cpp
@@ -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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
85
src/calc.cpp
85
src/calc.cpp
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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 + '\'');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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 = " [1;37;43mMODIFICATION[0m ";
|
||||
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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -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 = " [1;37;43mMODIFICATION[0m ";
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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 = " [1;37;43mMODIFICATION[0m ";
|
||||
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 ());
|
||||
}
|
||||
|
||||
@@ -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&);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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&);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = " [1;37;43mMODIFICATION[0m ";
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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&);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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&);
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -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
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user