mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Code Cleanup
- Removed obsolete TDB, Att and Location code. - Removed associated unit tests, unfortunately all ones that pass.
This commit is contained in:
parent
6b3dfd0891
commit
f245fa808c
11 changed files with 1 additions and 2091 deletions
701
src/Att.cpp
701
src/Att.cpp
|
@ -1,701 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <text.h>
|
|
||||||
#include <RX.h>
|
|
||||||
#include <Color.h>
|
|
||||||
#include <util.h>
|
|
||||||
#include <Date.h>
|
|
||||||
#include <Duration.h>
|
|
||||||
#include <Context.h>
|
|
||||||
#include <Att.h>
|
|
||||||
#include <main.h>
|
|
||||||
|
|
||||||
extern Context context;
|
|
||||||
|
|
||||||
static const char* internalNames[] =
|
|
||||||
{
|
|
||||||
"entry",
|
|
||||||
"start",
|
|
||||||
"end",
|
|
||||||
"parent",
|
|
||||||
"uuid",
|
|
||||||
"mask",
|
|
||||||
"imask",
|
|
||||||
"limit",
|
|
||||||
"status",
|
|
||||||
"description",
|
|
||||||
"tags",
|
|
||||||
"urgency",
|
|
||||||
// Note that annotations are not listed.
|
|
||||||
};
|
|
||||||
|
|
||||||
static const char* modifiableNames[] =
|
|
||||||
{
|
|
||||||
"project",
|
|
||||||
"priority",
|
|
||||||
"fg",
|
|
||||||
"bg",
|
|
||||||
"due",
|
|
||||||
"recur",
|
|
||||||
"until",
|
|
||||||
"wait",
|
|
||||||
"depends",
|
|
||||||
};
|
|
||||||
|
|
||||||
// Synonyms on the same line.
|
|
||||||
static const char* modifierNames[] =
|
|
||||||
{
|
|
||||||
"before", "under", "below",
|
|
||||||
"after", "over", "above",
|
|
||||||
"none",
|
|
||||||
"any",
|
|
||||||
"is", "equals",
|
|
||||||
"isnt", "not",
|
|
||||||
"has", "contains",
|
|
||||||
"hasnt",
|
|
||||||
"startswith", "left",
|
|
||||||
"endswith", "right",
|
|
||||||
"word",
|
|
||||||
"noword"
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NUM_INTERNAL_NAMES (sizeof (internalNames) / sizeof (internalNames[0]))
|
|
||||||
#define NUM_MODIFIABLE_NAMES (sizeof (modifiableNames) / sizeof (modifiableNames[0]))
|
|
||||||
#define NUM_MODIFIER_NAMES (sizeof (modifierNames) / sizeof (modifierNames[0]))
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att ()
|
|
||||||
: _name ("")
|
|
||||||
, _value ("")
|
|
||||||
, _mod ("")
|
|
||||||
, _sense ("positive")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const std::string& name, const std::string& mod, const std::string& value)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_value = value;
|
|
||||||
_mod = mod;
|
|
||||||
_sense = "positive";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const std::string& name, const std::string& mod, const std::string& value,
|
|
||||||
const std::string& sense)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_value = value;
|
|
||||||
_mod = mod;
|
|
||||||
_sense = sense;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const std::string& name, const std::string& mod, int value)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
|
|
||||||
std::stringstream s;
|
|
||||||
s << value;
|
|
||||||
_value = s.str ();
|
|
||||||
|
|
||||||
_mod = mod;
|
|
||||||
_sense = "positive";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const std::string& name, const std::string& value)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
_value = value;
|
|
||||||
_mod = "";
|
|
||||||
_sense = "positive";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const std::string& name, int value)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
|
|
||||||
std::stringstream s;
|
|
||||||
s << value;
|
|
||||||
_value = s.str ();
|
|
||||||
|
|
||||||
_mod = "";
|
|
||||||
_sense = "positive";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::Att (const Att& other)
|
|
||||||
{
|
|
||||||
_name = other._name;
|
|
||||||
_value = other._value;
|
|
||||||
_mod = other._mod;
|
|
||||||
_sense = other._sense;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att& Att::operator= (const Att& other)
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
_name = other._name;
|
|
||||||
_value = other._value;
|
|
||||||
_mod = other._mod;
|
|
||||||
_sense = other._sense;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::operator== (const Att& other) const
|
|
||||||
{
|
|
||||||
return _name == other._name &&
|
|
||||||
_mod == other._mod &&
|
|
||||||
_value == other._value &&
|
|
||||||
_sense == other._sense;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Att::~Att ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::logicSense (bool match) const
|
|
||||||
{
|
|
||||||
if (_sense == "positive")
|
|
||||||
return match;
|
|
||||||
else if (_sense == "negative")
|
|
||||||
return ! match;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.debug ("_sense: " + _sense);
|
|
||||||
throw ("unknown _sense " + _sense);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// For parsing.
|
|
||||||
bool Att::valid (const std::string& input) const
|
|
||||||
{
|
|
||||||
Nibbler n (input);
|
|
||||||
std::string ignored;
|
|
||||||
if (n.getUntilOneOf (".:", ignored))
|
|
||||||
{
|
|
||||||
if (ignored.length () == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
while (n.skip ('.'))
|
|
||||||
if (!n.getUntilOneOf (".:", ignored))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (n.skip (':'))
|
|
||||||
{
|
|
||||||
if (input.find ('@') <= n.cursor () ||
|
|
||||||
input.find ('/') <= n.cursor ())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (n.getQuoted ('"', ignored) ||
|
|
||||||
n.getUntil (' ', ignored) ||
|
|
||||||
n.getUntilEOS (ignored) ||
|
|
||||||
n.depleted ())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::validInternalName (const std::string& name)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < NUM_INTERNAL_NAMES; ++i)
|
|
||||||
if (name == internalNames[i])
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::validModifiableName (const std::string& name)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
|
|
||||||
if (name == modifiableNames[i])
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::validNameValue (
|
|
||||||
const std::string& name,
|
|
||||||
const std::string& mod,
|
|
||||||
const std::string& value)
|
|
||||||
{
|
|
||||||
std::string writableName = name;
|
|
||||||
std::string writableMod = mod;
|
|
||||||
std::string writableValue = value;
|
|
||||||
return Att::validNameValue (writableName, writableMod, writableValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool Att::validNameValue (
|
|
||||||
std::string& name,
|
|
||||||
std::string& mod,
|
|
||||||
std::string& value)
|
|
||||||
{
|
|
||||||
// First, guess at the full attribute name.
|
|
||||||
std::vector <std::string> candidates;
|
|
||||||
for (unsigned i = 0; i < NUM_INTERNAL_NAMES; ++i)
|
|
||||||
candidates.push_back (internalNames[i]);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
|
|
||||||
candidates.push_back (modifiableNames[i]);
|
|
||||||
|
|
||||||
std::vector <std::string> matches;
|
|
||||||
autoComplete (name,
|
|
||||||
candidates,
|
|
||||||
matches,
|
|
||||||
context.config.getInteger ("abbreviation.minimum"));
|
|
||||||
|
|
||||||
if (matches.size () == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
else if (matches.size () != 1)
|
|
||||||
{
|
|
||||||
std::string error = "Ambiguous attribute '" + name + "' - could be either of "; // TODO i18n
|
|
||||||
|
|
||||||
std::sort (matches.begin (), matches.end ());
|
|
||||||
std::string combined;
|
|
||||||
join (combined, ", ", matches);
|
|
||||||
|
|
||||||
throw error + combined + ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
name = matches[0];
|
|
||||||
|
|
||||||
// Second, guess at the modifier name.
|
|
||||||
if (mod != "")
|
|
||||||
{
|
|
||||||
candidates.clear ();
|
|
||||||
for (unsigned i = 0; i < NUM_MODIFIER_NAMES; ++i)
|
|
||||||
candidates.push_back (modifierNames[i]);
|
|
||||||
|
|
||||||
matches.clear ();
|
|
||||||
autoComplete (mod,
|
|
||||||
candidates,
|
|
||||||
matches,
|
|
||||||
context.config.getInteger ("abbreviation.minimum"));
|
|
||||||
|
|
||||||
if (matches.size () == 0)
|
|
||||||
throw std::string ("Unrecognized modifier '") + mod + "'.";
|
|
||||||
|
|
||||||
else if (matches.size () != 1)
|
|
||||||
{
|
|
||||||
std::string error = "Ambiguous modifier '" + mod + "' - could be either of "; // TODO i18n
|
|
||||||
|
|
||||||
std::sort (matches.begin (), matches.end ());
|
|
||||||
std::string combined;
|
|
||||||
join (combined, ", ", matches);
|
|
||||||
error += combined;
|
|
||||||
|
|
||||||
throw error + combined + ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
mod = matches[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some attributes are intended to be private, unless the command is read-
|
|
||||||
// only, in which cased these are perfectly valid elements of a filter.
|
|
||||||
/*
|
|
||||||
if (context.cmd.isWriteCommand () &&
|
|
||||||
!validModifiableName (name))
|
|
||||||
throw std::string ("\"") +
|
|
||||||
name +
|
|
||||||
"\" is not an attribute you may modify directly.";
|
|
||||||
*/
|
|
||||||
else if (name == "priority")
|
|
||||||
{
|
|
||||||
if (value != "")
|
|
||||||
{
|
|
||||||
value = upperCase (value);
|
|
||||||
if (value != "H" &&
|
|
||||||
value != "M" &&
|
|
||||||
value != "L")
|
|
||||||
throw std::string ("\"") +
|
|
||||||
value +
|
|
||||||
"\" is not a valid priority. Use H, M, L or leave blank.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (name == "description")
|
|
||||||
{
|
|
||||||
// if (context.cmd.isWriteCommand ())
|
|
||||||
{
|
|
||||||
if (value == "")
|
|
||||||
throw std::string ("The '") + name + "' attribute must not be blank.";
|
|
||||||
|
|
||||||
if (!noVerticalSpace (value))
|
|
||||||
throw std::string ("The '") + name + "' attribute must not contain vertical white space.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (name == "fg" || name == "bg")
|
|
||||||
{
|
|
||||||
// TODO Determine whether color abbreviations are supported, and if so,
|
|
||||||
// modify 'value' here accordingly.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dates can now be either a date, or a duration that is added as an offset
|
|
||||||
// to the current date.
|
|
||||||
else if (name == "due" ||
|
|
||||||
name == "until" ||
|
|
||||||
name == "wait")
|
|
||||||
{
|
|
||||||
// Validate and convert to epoch.
|
|
||||||
if (value != "")
|
|
||||||
{
|
|
||||||
// Try parsing as a duration. If unsuccessful, try again, as a date.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Date now;
|
|
||||||
Duration dur (value);
|
|
||||||
|
|
||||||
if (dur.negative ())
|
|
||||||
value = (now - (time_t)dur).toEpochString ();
|
|
||||||
else
|
|
||||||
value = (now + (time_t)dur).toEpochString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the date parsing failed, try parsing as a duration. If successful,
|
|
||||||
// add the duration to the current date. If unsuccessful, propagate the
|
|
||||||
// original date parse error.
|
|
||||||
|
|
||||||
// Try parsing as a date. If unsuccessfull, throw.
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
value = Date (value, context.config.get ("dateformat")).toEpochString ();
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (name == "recur")
|
|
||||||
{
|
|
||||||
// Just validate, don't convert to days.
|
|
||||||
Duration d;
|
|
||||||
if (value != "")
|
|
||||||
d.parse (value);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (name == "limit")
|
|
||||||
{
|
|
||||||
if (value == "" || (value != "page" && !digitsOnly (value)))
|
|
||||||
throw std::string ("The '") + name + "' attribute must be an integer, or the value 'page'.";
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (name == "status")
|
|
||||||
{
|
|
||||||
value = lowerCase (value);
|
|
||||||
|
|
||||||
std::vector <std::string> matches;
|
|
||||||
std::vector <std::string> candidates;
|
|
||||||
candidates.push_back ("pending");
|
|
||||||
candidates.push_back ("completed");
|
|
||||||
candidates.push_back ("deleted");
|
|
||||||
candidates.push_back ("recurring");
|
|
||||||
candidates.push_back ("waiting");
|
|
||||||
autoComplete (value,
|
|
||||||
candidates,
|
|
||||||
matches,
|
|
||||||
context.config.getInteger ("abbreviation.minimum"));
|
|
||||||
|
|
||||||
if (matches.size () == 1)
|
|
||||||
value = matches[0];
|
|
||||||
else
|
|
||||||
throw std::string ("\"") +
|
|
||||||
value +
|
|
||||||
"\" is not a valid status. Use 'pending', 'completed', 'deleted', 'recurring' or 'waiting'.";
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (! validInternalName (name) &&
|
|
||||||
! validModifiableName (name))
|
|
||||||
throw std::string ("'") + name + "' is not a recognized attribute.";
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO Deprecated - remove.
|
|
||||||
bool Att::validMod (const std::string& mod)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < NUM_MODIFIER_NAMES; ++i)
|
|
||||||
if (modifierNames[i] == mod)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The type of an attribute is useful for modifier evaluation.
|
|
||||||
std::string Att::type (const std::string& name) const
|
|
||||||
{
|
|
||||||
if (name == "due" ||
|
|
||||||
name == "wait" ||
|
|
||||||
name == "until" ||
|
|
||||||
name == "start" ||
|
|
||||||
name == "entry" ||
|
|
||||||
name == "end")
|
|
||||||
return "date";
|
|
||||||
|
|
||||||
else if (name == "recur")
|
|
||||||
return "duration";
|
|
||||||
|
|
||||||
else if (name == "limit" ||
|
|
||||||
name == "urgency")
|
|
||||||
return "number";
|
|
||||||
|
|
||||||
else if (name == "priority")
|
|
||||||
return "priority";
|
|
||||||
|
|
||||||
else
|
|
||||||
return "text";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// ______________
|
|
||||||
// | |
|
|
||||||
// | v
|
|
||||||
// start --> name --> . --> mod --> : --> " --> value --> " --> end
|
|
||||||
// | ^ | ^
|
|
||||||
// |_____________________| |_____________|
|
|
||||||
//
|
|
||||||
void Att::parse (const std::string& input)
|
|
||||||
{
|
|
||||||
Nibbler n (input);
|
|
||||||
parse (n);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Att::parse (Nibbler& n)
|
|
||||||
{
|
|
||||||
// Ensure a clean object first.
|
|
||||||
_name = "";
|
|
||||||
_value = "";
|
|
||||||
_mod = "";
|
|
||||||
_sense = "positive";
|
|
||||||
|
|
||||||
if (n.getUntilOneOf (".:", _name))
|
|
||||||
{
|
|
||||||
if (_name.length () == 0)
|
|
||||||
throw std::string ("Missing attribute name"); // TODO i18n
|
|
||||||
|
|
||||||
if (n.skip ('.'))
|
|
||||||
{
|
|
||||||
std::string mod;
|
|
||||||
|
|
||||||
if (n.skip ('~'))
|
|
||||||
{
|
|
||||||
context.debug ("using negative logic");
|
|
||||||
_sense = "negative";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.getUntil (":", mod))
|
|
||||||
{
|
|
||||||
if (validMod (mod))
|
|
||||||
_mod = mod;
|
|
||||||
else
|
|
||||||
throw std::string ("The name '") + mod + "' is not a valid modifier."; // TODO i18n
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::string ("Missing . or : after modifier."); // TODO i18n
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n.skip (':'))
|
|
||||||
{
|
|
||||||
// Both quoted and unquoted Att's are accepted.
|
|
||||||
// Consider removing this for a stricter parse.
|
|
||||||
if (n.getQuoted ('"', _value) ||
|
|
||||||
n.getUntilEOS (_value))
|
|
||||||
{
|
|
||||||
decode (_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::string ("Missing : after attribute name."); // TODO i18n
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw std::string ("Missing : after attribute name."); // TODO i18n
|
|
||||||
|
|
||||||
/* TODO This might be too slow to include. Test this assumption.
|
|
||||||
validNameValue (_name, _mod, _value);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// name : " value "
|
|
||||||
std::string Att::composeF4 () const
|
|
||||||
{
|
|
||||||
std::string output = "";
|
|
||||||
|
|
||||||
if (_name != "" && _value != "")
|
|
||||||
{
|
|
||||||
std::string value = _value;
|
|
||||||
encode (value);
|
|
||||||
enquote (value);
|
|
||||||
|
|
||||||
output += _name + ":" + value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void Att::mod (const std::string& input)
|
|
||||||
{
|
|
||||||
if (input != "" && !validMod (input))
|
|
||||||
throw std::string ("The name '") + input + "' is not a valid modifier."; // TODO i18n
|
|
||||||
|
|
||||||
_mod = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
std::string Att::mod () const
|
|
||||||
{
|
|
||||||
return _mod;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
std::string Att::name () const
|
|
||||||
{
|
|
||||||
return _name;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void Att::name (const std::string& name)
|
|
||||||
{
|
|
||||||
_name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
std::string Att::value () const
|
|
||||||
{
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void Att::value (const std::string& value)
|
|
||||||
{
|
|
||||||
_value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void Att::allNames (std::vector <std::string>& all)
|
|
||||||
{
|
|
||||||
all.clear ();
|
|
||||||
|
|
||||||
unsigned int i;
|
|
||||||
for (i = 0; i < NUM_INTERNAL_NAMES; ++i)
|
|
||||||
all.push_back (internalNames[i]);
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_MODIFIABLE_NAMES; ++i)
|
|
||||||
all.push_back (modifiableNames[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Add quotes.
|
|
||||||
void Att::enquote (std::string& value) const
|
|
||||||
{
|
|
||||||
value = '"' + value + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Remove quotes. Instead of being picky, just remove them all. There should
|
|
||||||
// be none within the value, and this will correct for one possible corruption
|
|
||||||
// that hand-editing the pending.data file could cause.
|
|
||||||
void Att::dequote (std::string& value) const
|
|
||||||
{
|
|
||||||
str_replace (value, "\"", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Encode values prior to serialization.
|
|
||||||
// \t -> &tab;
|
|
||||||
// " -> &dquot;
|
|
||||||
// [ -> &open;
|
|
||||||
// ] -> &close;
|
|
||||||
// \ -> \\ (extra chars to disambiguate multi-line comment)
|
|
||||||
void Att::encode (std::string& value) const
|
|
||||||
{
|
|
||||||
str_replace (value, "\t", "&tab;");
|
|
||||||
str_replace (value, "\"", "&dquot;");
|
|
||||||
str_replace (value, "[", "&open;");
|
|
||||||
str_replace (value, "]", "&close;");
|
|
||||||
str_replace (value, "\\", "\\\\");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Decode values after parse.
|
|
||||||
// \t <- &tab;
|
|
||||||
// " <- " or &dquot;
|
|
||||||
// ' <- &squot;
|
|
||||||
// , <- ,
|
|
||||||
// [ <- &open;
|
|
||||||
// ] <- &close;
|
|
||||||
// : <- :
|
|
||||||
void Att::decode (std::string& value) const
|
|
||||||
{
|
|
||||||
// Supported encodings.
|
|
||||||
str_replace (value, "&tab;", "\t");
|
|
||||||
str_replace (value, "&dquot;", "\"");
|
|
||||||
str_replace (value, """, "'");
|
|
||||||
str_replace (value, "&open;", "[");
|
|
||||||
str_replace (value, "&close;", "]");
|
|
||||||
|
|
||||||
// Support for deprecated encodings. These cannot be removed or old files
|
|
||||||
// will not be parsable. Not just old files - completed.data can contain
|
|
||||||
// tasks formatted/encoded using these.
|
|
||||||
str_replace (value, "&squot;", "'");
|
|
||||||
str_replace (value, ",", ",");
|
|
||||||
str_replace (value, ":", ":");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
87
src/Att.h
87
src/Att.h
|
@ -1,87 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifndef INCLUDED_ATT
|
|
||||||
#define INCLUDED_ATT
|
|
||||||
#define L10N // Localization complete.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <Nibbler.h>
|
|
||||||
|
|
||||||
class Att
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Att ();
|
|
||||||
Att (const std::string&, const std::string&, const std::string&);
|
|
||||||
Att (const std::string&, const std::string&, const std::string&, const std::string&);
|
|
||||||
Att (const std::string&, const std::string&, int);
|
|
||||||
Att (const std::string&, const std::string&);
|
|
||||||
Att (const std::string&, int);
|
|
||||||
Att (const Att&);
|
|
||||||
Att& operator= (const Att&);
|
|
||||||
bool operator== (const Att&) const;
|
|
||||||
~Att ();
|
|
||||||
|
|
||||||
bool logicSense (bool match) const;
|
|
||||||
bool valid (const std::string&) const;
|
|
||||||
static bool validInternalName (const std::string&);
|
|
||||||
static bool validModifiableName (const std::string&);
|
|
||||||
static bool validNameValue (const std::string&, const std::string&, const std::string&);
|
|
||||||
static bool validNameValue (std::string&, std::string&, std::string&);
|
|
||||||
static bool validMod (const std::string&);
|
|
||||||
std::string type (const std::string&) const;
|
|
||||||
void parse (const std::string&);
|
|
||||||
void parse (Nibbler&);
|
|
||||||
|
|
||||||
std::string composeF4 () const;
|
|
||||||
|
|
||||||
void mod (const std::string&);
|
|
||||||
std::string mod () const;
|
|
||||||
|
|
||||||
std::string name () const;
|
|
||||||
void name (const std::string&);
|
|
||||||
|
|
||||||
std::string value () const;
|
|
||||||
void value (const std::string&);
|
|
||||||
|
|
||||||
static void allNames (std::vector <std::string>&);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void enquote (std::string&) const;
|
|
||||||
void dequote (std::string&) const;
|
|
||||||
void encode (std::string&) const;
|
|
||||||
void decode (std::string&) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string _name;
|
|
||||||
std::string _value;
|
|
||||||
std::string _mod;
|
|
||||||
std::string _sense;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
|
@ -8,7 +8,6 @@ include_directories (${CMAKE_SOURCE_DIR}
|
||||||
set (task_SRCS A3.cpp A3.h
|
set (task_SRCS A3.cpp A3.h
|
||||||
API.cpp API.h
|
API.cpp API.h
|
||||||
Arg.cpp Arg.h
|
Arg.cpp Arg.h
|
||||||
Att.cpp Att.h
|
|
||||||
Color.cpp Color.h
|
Color.cpp Color.h
|
||||||
Config.cpp Config.h
|
Config.cpp Config.h
|
||||||
Context.cpp Context.h
|
Context.cpp Context.h
|
||||||
|
@ -20,12 +19,10 @@ set (task_SRCS A3.cpp A3.h
|
||||||
File.cpp File.h
|
File.cpp File.h
|
||||||
Hooks.cpp Hooks.h
|
Hooks.cpp Hooks.h
|
||||||
JSON.cpp JSON.h
|
JSON.cpp JSON.h
|
||||||
Location.cpp Location.h
|
|
||||||
Nibbler.cpp Nibbler.h
|
Nibbler.cpp Nibbler.h
|
||||||
Path.cpp Path.h
|
Path.cpp Path.h
|
||||||
Permission.cpp Permission.h
|
Permission.cpp Permission.h
|
||||||
RX.cpp RX.h
|
RX.cpp RX.h
|
||||||
TDB.cpp TDB.h
|
|
||||||
TDB2.cpp TDB2.h
|
TDB2.cpp TDB2.h
|
||||||
Task.cpp Task.h
|
Task.cpp Task.h
|
||||||
Taskmod.cpp Taskmod.h
|
Taskmod.cpp Taskmod.h
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <Location.h>
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Location::Location ()
|
|
||||||
: _path ("")
|
|
||||||
, _pending (NULL)
|
|
||||||
, _completed (NULL)
|
|
||||||
, _undo (NULL)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Location::Location (const std::string& p)
|
|
||||||
: _path (p)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Location::Location (const Location& other)
|
|
||||||
{
|
|
||||||
_path = other._path;
|
|
||||||
_pending = other._pending;
|
|
||||||
_completed = other._completed;
|
|
||||||
_undo = other._undo;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Location& Location::operator= (const Location& other)
|
|
||||||
{
|
|
||||||
if (this != &other)
|
|
||||||
{
|
|
||||||
_path = other._path;
|
|
||||||
_pending = other._pending;
|
|
||||||
_completed = other._completed;
|
|
||||||
_undo = other._undo;
|
|
||||||
}
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
Location::~Location ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
|
@ -1,51 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifndef INCLUDED_LOCATION
|
|
||||||
#define INCLUDED_LOCATION
|
|
||||||
#define L10N // Localization complete.
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
class Location
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Location (); // Default constructor
|
|
||||||
Location (const std::string&); // Default constructor
|
|
||||||
Location (const Location&); // Copy constructor
|
|
||||||
Location& operator= (const Location&); // Assignment operator
|
|
||||||
~Location (); // Destructor
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string _path;
|
|
||||||
FILE* _pending;
|
|
||||||
FILE* _completed;
|
|
||||||
FILE* _undo;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
693
src/TDB.cpp
693
src/TDB.cpp
|
@ -1,693 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <set>
|
|
||||||
#include <list>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <text.h>
|
|
||||||
#include <util.h>
|
|
||||||
#include <TDB.h>
|
|
||||||
#include <Directory.h>
|
|
||||||
#include <File.h>
|
|
||||||
#include <ViewText.h>
|
|
||||||
#include <Color.h>
|
|
||||||
#include <main.h>
|
|
||||||
|
|
||||||
extern Context context;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// The ctor/dtor do nothing.
|
|
||||||
// The lock/unlock methods hold the file open.
|
|
||||||
// There should be only one commit.
|
|
||||||
//
|
|
||||||
// +- TDB::TDB
|
|
||||||
// |
|
|
||||||
// | +- TDB::lock
|
|
||||||
// | | open
|
|
||||||
// | | [lock]
|
|
||||||
// | |
|
|
||||||
// | | +- TDB::load
|
|
||||||
// | | | read all
|
|
||||||
// | | | return subset
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::add (T)
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::update (T)
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::commit
|
|
||||||
// | | | write all
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::undo
|
|
||||||
// | |
|
|
||||||
// | +- TDB::unlock
|
|
||||||
// | [unlock]
|
|
||||||
// | close
|
|
||||||
// |
|
|
||||||
// +- TDB::~TDB
|
|
||||||
// [TDB::unlock]
|
|
||||||
//
|
|
||||||
TDB::TDB ()
|
|
||||||
: _lock (true)
|
|
||||||
, _all_opened_and_locked (false)
|
|
||||||
, _id (1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
TDB::~TDB ()
|
|
||||||
{
|
|
||||||
if (_all_opened_and_locked)
|
|
||||||
unlock ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::clear ()
|
|
||||||
{
|
|
||||||
_locations.clear ();
|
|
||||||
_lock = true;
|
|
||||||
|
|
||||||
if (_all_opened_and_locked)
|
|
||||||
unlock ();
|
|
||||||
|
|
||||||
_all_opened_and_locked = false;
|
|
||||||
_id = 1;
|
|
||||||
_pending.clear ();
|
|
||||||
_new.clear ();
|
|
||||||
_completed.clear ();
|
|
||||||
_modified.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::location (const std::string& path)
|
|
||||||
{
|
|
||||||
Directory d (path);
|
|
||||||
if (!d.exists ())
|
|
||||||
throw std::string ("Data location '") +
|
|
||||||
path +
|
|
||||||
"' does not exist, or is not readable and writable.";
|
|
||||||
|
|
||||||
_locations.push_back (Location (d));
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::lock (bool lockFile /* = true */)
|
|
||||||
{
|
|
||||||
_lock = lockFile;
|
|
||||||
|
|
||||||
_pending.clear ();
|
|
||||||
_new.clear ();
|
|
||||||
_completed.clear ();
|
|
||||||
_modified.clear ();
|
|
||||||
|
|
||||||
foreach (location, _locations)
|
|
||||||
{
|
|
||||||
location->_pending = openAndLock (location->_path + "/pending.data");
|
|
||||||
location->_completed = openAndLock (location->_path + "/completed.data");
|
|
||||||
location->_undo = openAndLock (location->_path + "/undo.data");
|
|
||||||
}
|
|
||||||
|
|
||||||
_all_opened_and_locked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::unlock ()
|
|
||||||
{
|
|
||||||
// Do not clear out these items, as they may be used in a read-only fashion.
|
|
||||||
// _pending.clear ();
|
|
||||||
// _new.clear ();
|
|
||||||
// _modified.clear ();
|
|
||||||
|
|
||||||
foreach (location, _locations)
|
|
||||||
{
|
|
||||||
fflush (location->_pending);
|
|
||||||
fclose (location->_pending);
|
|
||||||
location->_pending = NULL;
|
|
||||||
|
|
||||||
fflush (location->_completed);
|
|
||||||
fclose (location->_completed);
|
|
||||||
location->_completed = NULL;
|
|
||||||
|
|
||||||
fflush (location->_undo);
|
|
||||||
fclose (location->_undo);
|
|
||||||
location->_completed = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
_all_opened_and_locked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Returns number of filtered tasks.
|
|
||||||
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
|
||||||
// multiple files.
|
|
||||||
int TDB::load (std::vector <Task>& tasks)
|
|
||||||
{
|
|
||||||
// Special optimization: if the filter contains Att ('status', '', 'pending'),
|
|
||||||
// and no other 'status' filters, then loadCompleted can be skipped.
|
|
||||||
int numberStatusClauses = 0;
|
|
||||||
int numberSimpleStatusClauses = 0;
|
|
||||||
|
|
||||||
loadPending (tasks);
|
|
||||||
|
|
||||||
if (numberStatusClauses == 0 ||
|
|
||||||
numberStatusClauses != numberSimpleStatusClauses)
|
|
||||||
loadCompleted (tasks);
|
|
||||||
else
|
|
||||||
context.debug ("load optimization short circuit");
|
|
||||||
|
|
||||||
return tasks.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Returns number of filtered tasks.
|
|
||||||
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
|
||||||
// multiple files.
|
|
||||||
int TDB::loadPending (std::vector <Task>& tasks)
|
|
||||||
{
|
|
||||||
std::string file;
|
|
||||||
int line_number = 1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Only load if not already loaded.
|
|
||||||
if (_pending.size () == 0)
|
|
||||||
{
|
|
||||||
_id = 1;
|
|
||||||
char line[T_LINE_MAX];
|
|
||||||
foreach (location, _locations)
|
|
||||||
{
|
|
||||||
line_number = 1;
|
|
||||||
file = location->_path + "/pending.data";
|
|
||||||
|
|
||||||
fseek (location->_pending, 0, SEEK_SET);
|
|
||||||
while (fgets (line, T_LINE_MAX, location->_pending))
|
|
||||||
{
|
|
||||||
int length = strlen (line);
|
|
||||||
if (length > 3) // []\n
|
|
||||||
{
|
|
||||||
// TODO Add hidden attribute indicating source?
|
|
||||||
Task task (line);
|
|
||||||
task.id = _id++;
|
|
||||||
|
|
||||||
_pending.push_back (task);
|
|
||||||
|
|
||||||
// Maintain mapping for ease of link/dependency resolution.
|
|
||||||
// Note that this mapping is not restricted by the filter, and is
|
|
||||||
// therefore a complete set.
|
|
||||||
_I2U[task.id] = task.get ("uuid");
|
|
||||||
_U2I[task.get ("uuid")] = task.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
++line_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now filter and return.
|
|
||||||
foreach (task, _pending)
|
|
||||||
tasks.push_back (*task);
|
|
||||||
|
|
||||||
// Hand back any accumulated additions, if TDB::loadPending is being called
|
|
||||||
// repeatedly.
|
|
||||||
int fakeId = _id;
|
|
||||||
foreach (task, _new)
|
|
||||||
{
|
|
||||||
task->id = fakeId++;
|
|
||||||
tasks.push_back (*task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << " in " << file << " at line " << line_number;
|
|
||||||
throw e + s.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Returns number of filtered tasks.
|
|
||||||
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
|
||||||
// multiple files.
|
|
||||||
int TDB::loadCompleted (std::vector <Task>& tasks)
|
|
||||||
{
|
|
||||||
std::string file;
|
|
||||||
int line_number = 1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_completed.size () == 0)
|
|
||||||
{
|
|
||||||
char line[T_LINE_MAX];
|
|
||||||
foreach (location, _locations)
|
|
||||||
{
|
|
||||||
line_number = 1;
|
|
||||||
file = location->_path + "/completed.data";
|
|
||||||
|
|
||||||
fseek (location->_completed, 0, SEEK_SET);
|
|
||||||
while (fgets (line, T_LINE_MAX, location->_completed))
|
|
||||||
{
|
|
||||||
int length = strlen (line);
|
|
||||||
if (length > 3) // []\n
|
|
||||||
{
|
|
||||||
// TODO Add hidden attribute indicating source?
|
|
||||||
|
|
||||||
Task task (line);
|
|
||||||
task.id = 0; // Need a value, just not a valid value.
|
|
||||||
|
|
||||||
_completed.push_back (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
++line_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now filter and return.
|
|
||||||
foreach (task, _completed)
|
|
||||||
tasks.push_back (*task);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << " in " << file << " at line " << line_number;
|
|
||||||
throw e + s.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
const std::vector <Task>& TDB::getAllPending ()
|
|
||||||
{
|
|
||||||
return _pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
const std::vector <Task>& TDB::getAllNew ()
|
|
||||||
{
|
|
||||||
return _new;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
const std::vector <Task>& TDB::getAllCompleted ()
|
|
||||||
{
|
|
||||||
return _completed;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
const std::vector <Task>& TDB::getAllModified ()
|
|
||||||
{
|
|
||||||
return _modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Note: _locations[0] is where all tasks are written.
|
|
||||||
void TDB::add (const Task& task)
|
|
||||||
{
|
|
||||||
std::string unique;
|
|
||||||
Task t (task);
|
|
||||||
if (task.get ("uuid") == "")
|
|
||||||
unique = ::uuid ();
|
|
||||||
else
|
|
||||||
unique = task.get ("uuid");
|
|
||||||
|
|
||||||
t.set ("uuid", unique);
|
|
||||||
|
|
||||||
// If the tasks are loaded, then verify that this uuid is not already in
|
|
||||||
// the file.
|
|
||||||
if (uuidAlreadyUsed (unique, _new) ||
|
|
||||||
uuidAlreadyUsed (unique, _modified) ||
|
|
||||||
uuidAlreadyUsed (unique, _pending) ||
|
|
||||||
uuidAlreadyUsed (unique, _completed))
|
|
||||||
throw std::string ("Cannot add task because the uuid '") + unique + "' is not unique.";
|
|
||||||
|
|
||||||
_new.push_back (t);
|
|
||||||
_I2U[task.id] = unique;
|
|
||||||
_U2I[task.get ("uuid")] = t.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::update (const Task& task)
|
|
||||||
{
|
|
||||||
_modified.push_back (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Interestingly, only the pending file gets written to. The completed file is
|
|
||||||
// only modified by TDB::gc.
|
|
||||||
int TDB::commit ()
|
|
||||||
{
|
|
||||||
context.timer_gc.start ();
|
|
||||||
|
|
||||||
int quantity = _new.size () + _modified.size ();
|
|
||||||
|
|
||||||
// This is an optimization. If there are only new tasks, and none were
|
|
||||||
// modified, simply seek to the end of pending and write.
|
|
||||||
if (_new.size () && ! _modified.size ())
|
|
||||||
{
|
|
||||||
fseek (_locations[0]._pending, 0, SEEK_END);
|
|
||||||
foreach (task, _new)
|
|
||||||
{
|
|
||||||
_pending.push_back (*task);
|
|
||||||
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
fseek (_locations[0]._undo, 0, SEEK_END);
|
|
||||||
foreach (task, _new)
|
|
||||||
writeUndo (*task, _locations[0]._undo);
|
|
||||||
|
|
||||||
_new.clear ();
|
|
||||||
return quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The alternative is to potentially rewrite both files.
|
|
||||||
else if (_new.size () || _modified.size ())
|
|
||||||
{
|
|
||||||
// allPending is a copy of _pending, with all modifications included, and
|
|
||||||
// new tasks appended.
|
|
||||||
std::vector <Task> allPending;
|
|
||||||
allPending = _pending;
|
|
||||||
foreach (mtask, _modified)
|
|
||||||
{
|
|
||||||
foreach (task, allPending)
|
|
||||||
{
|
|
||||||
if (task->id == mtask->id)
|
|
||||||
{
|
|
||||||
*task = *mtask;
|
|
||||||
goto next_mod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_mod:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (task, _new)
|
|
||||||
allPending.push_back (*task);
|
|
||||||
|
|
||||||
// Write out all pending.
|
|
||||||
if (fseek (_locations[0]._pending, 0, SEEK_SET) == 0)
|
|
||||||
{
|
|
||||||
if (ftruncate (fileno (_locations[0]._pending), 0))
|
|
||||||
throw std::string ("Failed to truncate pending.data file ");
|
|
||||||
|
|
||||||
foreach (task, allPending)
|
|
||||||
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the undo log.
|
|
||||||
if (fseek (_locations[0]._undo, 0, SEEK_END) == 0)
|
|
||||||
{
|
|
||||||
foreach (mtask, _modified)
|
|
||||||
{
|
|
||||||
foreach (task, _pending)
|
|
||||||
{
|
|
||||||
if (task->id == mtask->id)
|
|
||||||
{
|
|
||||||
writeUndo (*task, *mtask, _locations[0]._undo);
|
|
||||||
goto next_mod2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
next_mod2:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (task, _new)
|
|
||||||
writeUndo (*task, _locations[0]._undo);
|
|
||||||
}
|
|
||||||
|
|
||||||
_pending = allPending;
|
|
||||||
|
|
||||||
_modified.clear ();
|
|
||||||
_new.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
context.timer_gc.stop ();
|
|
||||||
return quantity;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Scans the pending tasks for any that are completed or deleted, and if so,
|
|
||||||
// moves them to the completed.data file. Returns a count of tasks moved.
|
|
||||||
// Now reverts expired waiting tasks to pending.
|
|
||||||
// Now cleans up dangling dependencies.
|
|
||||||
int TDB::gc ()
|
|
||||||
{
|
|
||||||
context.timer_gc.start ();
|
|
||||||
|
|
||||||
// Allowed as a temporary override.
|
|
||||||
if (!context.config.getBoolean ("gc"))
|
|
||||||
{
|
|
||||||
context.timer_gc.stop ();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count_pending_changes = 0;
|
|
||||||
int count_completed_changes = 0;
|
|
||||||
Date now;
|
|
||||||
|
|
||||||
if (_new.size ())
|
|
||||||
throw std::string ("Unexpected new tasks found during gc.");
|
|
||||||
|
|
||||||
if (_modified.size ())
|
|
||||||
throw std::string ("Unexpected modified tasks found during gc.");
|
|
||||||
|
|
||||||
lock ();
|
|
||||||
|
|
||||||
std::vector <Task> ignore;
|
|
||||||
loadPending (ignore);
|
|
||||||
|
|
||||||
// Search for dangling dependencies. These are dependencies whose uuid cannot
|
|
||||||
// be converted to an id by TDB.
|
|
||||||
std::vector <std::string> deps;
|
|
||||||
foreach (task, _pending)
|
|
||||||
{
|
|
||||||
if (task->has ("depends"))
|
|
||||||
{
|
|
||||||
deps.clear ();
|
|
||||||
task->getDependencies (deps);
|
|
||||||
foreach (dep, deps)
|
|
||||||
{
|
|
||||||
if (id (*dep) == 0)
|
|
||||||
{
|
|
||||||
task->removeDependency (*dep);
|
|
||||||
++count_pending_changes;
|
|
||||||
context.debug ("GC: Removed dangling dependency "
|
|
||||||
+ task->get ("uuid")
|
|
||||||
+ " -> "
|
|
||||||
+ *dep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now move completed and deleted tasks from the pending list to the
|
|
||||||
// completed list. Isn't garbage collection easy?
|
|
||||||
std::vector <Task> still_pending;
|
|
||||||
std::vector <Task> newly_completed;
|
|
||||||
foreach (task, _pending)
|
|
||||||
{
|
|
||||||
Task::status s = task->getStatus ();
|
|
||||||
if (s == Task::completed ||
|
|
||||||
s == Task::deleted)
|
|
||||||
{
|
|
||||||
newly_completed.push_back (*task);
|
|
||||||
++count_pending_changes; // removal
|
|
||||||
++count_completed_changes; // addition
|
|
||||||
}
|
|
||||||
else if (s == Task::waiting)
|
|
||||||
{
|
|
||||||
// Wake up tasks that need to be woken.
|
|
||||||
Date wait_date (task->get_date ("wait"));
|
|
||||||
if (now > wait_date)
|
|
||||||
{
|
|
||||||
task->setStatus (Task::pending);
|
|
||||||
task->remove ("wait");
|
|
||||||
++count_pending_changes; // modification
|
|
||||||
|
|
||||||
context.debug (std::string ("TDB::gc waiting -> pending for ") +
|
|
||||||
task->get ("uuid"));
|
|
||||||
}
|
|
||||||
|
|
||||||
still_pending.push_back (*task);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
still_pending.push_back (*task);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No commit - all updates performed manually.
|
|
||||||
if (count_pending_changes > 0)
|
|
||||||
{
|
|
||||||
if (fseek (_locations[0]._pending, 0, SEEK_SET) == 0)
|
|
||||||
{
|
|
||||||
if (ftruncate (fileno (_locations[0]._pending), 0))
|
|
||||||
throw std::string ("Failed to truncate pending.data file ");
|
|
||||||
|
|
||||||
foreach (task, still_pending)
|
|
||||||
fputs (task->composeF4 ().c_str (), _locations[0]._pending);
|
|
||||||
|
|
||||||
// Update cached copy.
|
|
||||||
_pending = still_pending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append the new_completed tasks to completed.data. No need to write out the
|
|
||||||
// whole list.
|
|
||||||
if (count_completed_changes > 0)
|
|
||||||
{
|
|
||||||
fseek (_locations[0]._completed, 0, SEEK_END);
|
|
||||||
foreach (task, newly_completed)
|
|
||||||
fputs (task->composeF4 ().c_str (), _locations[0]._completed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close files.
|
|
||||||
unlock ();
|
|
||||||
|
|
||||||
std::stringstream s;
|
|
||||||
s << "gc " << (count_pending_changes + count_completed_changes) << " tasks";
|
|
||||||
context.debug (s.str ());
|
|
||||||
|
|
||||||
context.timer_gc.stop ();
|
|
||||||
return count_pending_changes + count_completed_changes;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
int TDB::nextId ()
|
|
||||||
{
|
|
||||||
return _id++;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::undo ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::merge (const std::string& mergeFile)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
std::string TDB::uuid (int id) const
|
|
||||||
{
|
|
||||||
std::map <int, std::string>::const_iterator i;
|
|
||||||
if ((i = _I2U.find (id)) != _I2U.end ())
|
|
||||||
return i->second;
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
int TDB::id (const std::string& uuid) const
|
|
||||||
{
|
|
||||||
std::map <std::string, int>::const_iterator i;
|
|
||||||
if ((i = _U2I.find (uuid)) != _U2I.end ())
|
|
||||||
return i->second;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
FILE* TDB::openAndLock (const std::string& file)
|
|
||||||
{
|
|
||||||
// TODO Need provision here for read-only locations.
|
|
||||||
|
|
||||||
// Check for access.
|
|
||||||
File f (file);
|
|
||||||
bool exists = f.exists ();
|
|
||||||
if (exists)
|
|
||||||
if (!f.readable () || !f.writable ())
|
|
||||||
throw std::string ("Task does not have the correct permissions for '") +
|
|
||||||
file + "'.";
|
|
||||||
|
|
||||||
// Open the file.
|
|
||||||
FILE* in = fopen (file.c_str (), (exists ? "r+" : "w+"));
|
|
||||||
if (!in)
|
|
||||||
throw std::string ("Could not open '") + file + "'.";
|
|
||||||
|
|
||||||
// Lock if desired. Try three times before failing.
|
|
||||||
if (_lock)
|
|
||||||
{
|
|
||||||
int retry = 0;
|
|
||||||
while (flock (fileno (in), LOCK_NB | LOCK_EX) && ++retry <= 3)
|
|
||||||
{
|
|
||||||
std::cout << "Waiting for file lock...\n";
|
|
||||||
while (flock (fileno (in), LOCK_NB | LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (retry > 3)
|
|
||||||
throw std::string ("Could not lock '") + file + "'.";
|
|
||||||
}
|
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::writeUndo (const Task& after, FILE* file)
|
|
||||||
{
|
|
||||||
fprintf (file,
|
|
||||||
"time %u\nnew %s---\n",
|
|
||||||
(unsigned int) time (NULL),
|
|
||||||
after.composeF4 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB::writeUndo (const Task& before, const Task& after, FILE* file)
|
|
||||||
{
|
|
||||||
fprintf (file,
|
|
||||||
"time %u\nold %snew %s---\n",
|
|
||||||
(unsigned int) time (NULL),
|
|
||||||
before.composeF4 ().c_str (),
|
|
||||||
after.composeF4 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::uuidAlreadyUsed (
|
|
||||||
const std::string& uuid,
|
|
||||||
const std::vector <Task>& all)
|
|
||||||
{
|
|
||||||
std::vector <Task>::const_iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
if (it->get ("uuid") == uuid)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
98
src/TDB.h
98
src/TDB.h
|
@ -1,98 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
#ifndef INCLUDED_TDB
|
|
||||||
#define INCLUDED_TDB
|
|
||||||
#define L10N // Localization complete.
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <Location.h>
|
|
||||||
#include <Task.h>
|
|
||||||
|
|
||||||
// Length of longest line.
|
|
||||||
#define T_LINE_MAX 32768
|
|
||||||
|
|
||||||
class TDB
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TDB (); // Default constructor
|
|
||||||
~TDB (); // Destructor
|
|
||||||
|
|
||||||
TDB (const TDB&);
|
|
||||||
TDB& operator= (const TDB&);
|
|
||||||
|
|
||||||
void clear ();
|
|
||||||
void location (const std::string&);
|
|
||||||
|
|
||||||
void lock (bool lockFile = true);
|
|
||||||
void unlock ();
|
|
||||||
|
|
||||||
int load (std::vector <Task>&);
|
|
||||||
int loadPending (std::vector <Task>&);
|
|
||||||
int loadCompleted (std::vector <Task>&);
|
|
||||||
|
|
||||||
const std::vector <Task>& getAllPending ();
|
|
||||||
const std::vector <Task>& getAllNew ();
|
|
||||||
const std::vector <Task>& getAllCompleted ();
|
|
||||||
const std::vector <Task>& getAllModified ();
|
|
||||||
|
|
||||||
void add (const Task&); // Single task add to pending
|
|
||||||
void update (const Task&); // Single task update to pending
|
|
||||||
int commit (); // Write out all tasks
|
|
||||||
int gc (); // Clean up pending
|
|
||||||
int nextId ();
|
|
||||||
void undo ();
|
|
||||||
void merge (const std::string&);
|
|
||||||
|
|
||||||
std::string uuid (int) const;
|
|
||||||
int id (const std::string&) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* openAndLock (const std::string&);
|
|
||||||
void writeUndo (const Task&, FILE*);
|
|
||||||
void writeUndo (const Task&, const Task&, FILE*);
|
|
||||||
bool uuidAlreadyUsed (const std::string&);
|
|
||||||
bool uuidAlreadyUsed (const std::string&, const std::vector <Task>&);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector <Location> _locations;
|
|
||||||
bool _lock;
|
|
||||||
bool _all_opened_and_locked;
|
|
||||||
int _id;
|
|
||||||
|
|
||||||
std::vector <Task> _pending; // Contents of pending.data
|
|
||||||
std::vector <Task> _completed; // Contents of pending.data
|
|
||||||
std::vector <Task> _new; // Uncommitted new tasks
|
|
||||||
std::vector <Task> _modified; // Uncommitted modified tasks
|
|
||||||
|
|
||||||
std::map <int, std::string> _I2U; // ID -> UUID map
|
|
||||||
std::map <std::string, int> _U2I; // UUID -> ID map
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
2
test/.gitignore
vendored
2
test/.gitignore
vendored
|
@ -2,7 +2,6 @@
|
||||||
*.data
|
*.data
|
||||||
*.log
|
*.log
|
||||||
arguments.t
|
arguments.t
|
||||||
att.t
|
|
||||||
autocomplete.t
|
autocomplete.t
|
||||||
cmd.t
|
cmd.t
|
||||||
color.t
|
color.t
|
||||||
|
@ -27,7 +26,6 @@ subst.t
|
||||||
t.t
|
t.t
|
||||||
t2.t
|
t2.t
|
||||||
taskmod.t
|
taskmod.t
|
||||||
tdb.t
|
|
||||||
tdb2.t
|
tdb2.t
|
||||||
text.t
|
text.t
|
||||||
transport.t
|
transport.t
|
||||||
|
|
|
@ -6,7 +6,7 @@ include_directories (${CMAKE_SOURCE_DIR}
|
||||||
${CMAKE_SOURCE_DIR}/test
|
${CMAKE_SOURCE_DIR}/test
|
||||||
${TASK_INCLUDE_DIRS})
|
${TASK_INCLUDE_DIRS})
|
||||||
|
|
||||||
set (test_SRCS att.t autocomplete.t color.t config.t date.t directory.t dom.t
|
set (test_SRCS autocomplete.t color.t config.t date.t directory.t dom.t
|
||||||
duration.t file.t i18n.t json.t list.t nibbler.t path.t rx.t
|
duration.t file.t i18n.t json.t list.t nibbler.t path.t rx.t
|
||||||
t.t t2.t taskmod.t tdb2.t text.t uri.t util.t view.t json_test)
|
t.t t2.t taskmod.t tdb2.t text.t uri.t util.t view.t json_test)
|
||||||
|
|
||||||
|
|
381
test/att.t.cpp
381
test/att.t.cpp
|
@ -1,381 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// taskwarrior - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2011, Paul Beckingham, Federico Hernandez.
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under
|
|
||||||
// the terms of the GNU General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 2 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
||||||
// details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License along with
|
|
||||||
// this program; if not, write to the
|
|
||||||
//
|
|
||||||
// Free Software Foundation, Inc.,
|
|
||||||
// 51 Franklin Street, Fifth Floor,
|
|
||||||
// Boston, MA
|
|
||||||
// 02110-1301
|
|
||||||
// USA
|
|
||||||
//
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
#include <Context.h>
|
|
||||||
#include <Att.h>
|
|
||||||
#include <test.h>
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
Context context;
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
int main (int argc, char** argv)
|
|
||||||
{
|
|
||||||
UnitTest t (118);
|
|
||||||
|
|
||||||
Att a;
|
|
||||||
t.notok (a.valid ("name"), "Att::valid name -> fail");
|
|
||||||
t.notok (a.valid (":"), "Att::valid : -> fail");
|
|
||||||
t.notok (a.valid (":value"), "Att::valid :value -> fail");
|
|
||||||
t.notok (a.valid ("n@me.mod:value"), "Att::valie n@me.mod:value -> fail");
|
|
||||||
t.notok (a.valid ("n/me.mod:value"), "Att::valie n/me.mod:value -> fail");
|
|
||||||
t.notok (a.valid ("name.m@d:value"), "Att::valie name.m@d:value -> fail");
|
|
||||||
t.notok (a.valid ("name.m/d:value"), "Att::valie name.m/d:value -> fail");
|
|
||||||
|
|
||||||
t.ok (a.valid ("name:value"), "Att::valid name:value");
|
|
||||||
t.ok (a.valid ("name:value "), "Att::valid name:value\\s");
|
|
||||||
t.ok (a.valid ("name:'value'"), "Att::valid name:'value'");
|
|
||||||
t.ok (a.valid ("name:'one two'"), "Att::valid name:'one two'");
|
|
||||||
t.ok (a.valid ("name:\"value\""), "Att::valid name:\"value\"");
|
|
||||||
t.ok (a.valid ("name:\"one two\""), "Att::valid name:\"one two\"");
|
|
||||||
t.ok (a.valid ("name:"), "Att::valid name:");
|
|
||||||
t.ok (a.valid ("name:""), "Att::valid "");
|
|
||||||
t.ok (a.valid ("name.one:value"), "Att::valid name.one.value");
|
|
||||||
t.ok (a.valid ("name.one.two:value"), "Att::valid name.one.two:value");
|
|
||||||
t.ok (a.valid ("name.:value"), "Att::valid name.:value");
|
|
||||||
t.ok (a.valid ("name..:value"), "Att::valid name..:value");
|
|
||||||
|
|
||||||
Att a1 ("name", "value");
|
|
||||||
t.is (a1.name (), "name", "Att::Att (name, value), Att.name");
|
|
||||||
t.is (a1.value (), "value", "Att::Att (name, value), Att.value");
|
|
||||||
|
|
||||||
Att a2;
|
|
||||||
a2.name ("name");
|
|
||||||
a2.value ("value");
|
|
||||||
t.is (a2.name (), "name", "Att::Att (), Att.name");
|
|
||||||
t.is (a2.value (), "value", "Att::Att (), Att.value");
|
|
||||||
|
|
||||||
Att a3 (a2);
|
|
||||||
t.is (a3.name (), "name", "Att::Att (Att), Att.name");
|
|
||||||
t.is (a3.value (), "value", "Att::Att (Att), Att.value");
|
|
||||||
|
|
||||||
Att a4;
|
|
||||||
a4 = a2;
|
|
||||||
t.is (a4.name (), "name", "Att::Att (), Att.operator=, Att.name");
|
|
||||||
t.is (a4.value (), "value", "Att::Att (), Att.operator=, Att.value");
|
|
||||||
|
|
||||||
Att a5 ("name", "value");
|
|
||||||
t.is (a5.composeF4 (), "name:\"value\"", "Att::composeF4 simple");
|
|
||||||
a5.value ("\"");
|
|
||||||
t.is (a5.composeF4 (), "name:\"&dquot;\"", "Att::composeF4 encoded \"");
|
|
||||||
a5.value ("\t\",[]:");
|
|
||||||
t.is (a5.composeF4 (), "name:\"&tab;&dquot;,&open;&close;:\"", "Att::composeF4 fully encoded \\t\",[]:");
|
|
||||||
|
|
||||||
Att a6 ("name", 7);
|
|
||||||
|
|
||||||
// Att::mod - straight comparisons.
|
|
||||||
bool good = true;
|
|
||||||
try {a6.mod ("is");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (is)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("before");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (before)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("after");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (after)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("none");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (none)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("any");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (any)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("over");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (over)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("under");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (under)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("above");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (above)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("below");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (below)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("isnt");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (isnt)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("has");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (has)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("contains");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (contains)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("hasnt");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (hasnt)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("startswith");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (startswith)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("endswith");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (endswith)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("word");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (word)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("noword");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (noword)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("unrecognized");} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::mod (unrecognized)");
|
|
||||||
|
|
||||||
// Att::mod - regex comparisons.
|
|
||||||
context.config.set ("regex", "on");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("is");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (is)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("before");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (before)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("after");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (after)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("none");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (none)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("any");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (any)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("over");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (over)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("under");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (under)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("above");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (above)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("below");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (below)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("isnt");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (isnt)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("has");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (has)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("contains");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (contains)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("hasnt");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (hasnt)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("startswith");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (startswith)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("endswith");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (endswith)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("word");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (word)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("noword");} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::mod (noword)");
|
|
||||||
|
|
||||||
good = true;
|
|
||||||
try {a6.mod ("unrecognized");} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::mod (unrecognized)");
|
|
||||||
|
|
||||||
// Att::parse
|
|
||||||
Nibbler n ("");
|
|
||||||
Att a7;
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse () -> throw");
|
|
||||||
|
|
||||||
n = Nibbler ("name:value");
|
|
||||||
a7.parse (n);
|
|
||||||
t.is (a7.composeF4 (), "name:\"value\"",
|
|
||||||
"Att::parse (name:value)");
|
|
||||||
|
|
||||||
n = Nibbler ("name:\"value\"");
|
|
||||||
a7.parse (n);
|
|
||||||
t.is (a7.composeF4 (), "name:\"value\"",
|
|
||||||
"Att::parse (name:\"value\")");
|
|
||||||
|
|
||||||
n = Nibbler ("name:\"one two\"");
|
|
||||||
a7.parse (n);
|
|
||||||
t.is (a7.composeF4 (), "name:\"one two\"",
|
|
||||||
"Att::parse (name:\"one two\")");
|
|
||||||
|
|
||||||
n = Nibbler ("name:\""\"");
|
|
||||||
a7.parse (n);
|
|
||||||
t.is (a7.composeF4 (), "name:\"'\"",
|
|
||||||
"Att::parse (name:\"'\")");
|
|
||||||
|
|
||||||
n = Nibbler ("name:\"&tab;",&open;&close;:\"");
|
|
||||||
a7.parse (n);
|
|
||||||
t.is (a7.composeF4 (), "name:\"&tab;',&open;&close;:\"",
|
|
||||||
"Att::parse (name:\"&tab;',&open;&close;:\")");
|
|
||||||
|
|
||||||
n = Nibbler ("total gibberish");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (total gibberish)");
|
|
||||||
|
|
||||||
n = Nibbler ("malformed");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (malformed)");
|
|
||||||
|
|
||||||
n = Nibbler (":malformed");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (:malformed)");
|
|
||||||
|
|
||||||
n = Nibbler (":\"\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (:\"\")");
|
|
||||||
|
|
||||||
n = Nibbler (":\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (:\")");
|
|
||||||
|
|
||||||
n = Nibbler ("name:");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::parse (name:)");
|
|
||||||
|
|
||||||
n = Nibbler ("name:\"value");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::parse (name:\"value)");
|
|
||||||
t.is (a7.composeF4 (), "name:\"&dquot;value\"", "Att::composeF4 -> name:\"&dquot;value\"");
|
|
||||||
|
|
||||||
n = Nibbler ("name:value\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::parse (name:value\")");
|
|
||||||
t.is (a7.composeF4 (), "name:\"value&dquot;\"", "Att::composeF4 -> name:\"value&dquot;\"");
|
|
||||||
|
|
||||||
n = Nibbler ("name:val\"ue");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::parse (name:val\"ue)");
|
|
||||||
t.is (a7.composeF4 (), "name:\"val&dquot;ue\"", "Att::composeF4 -> name:\"val&dquot;ue\"");
|
|
||||||
|
|
||||||
n = Nibbler ("name\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (name\")");
|
|
||||||
|
|
||||||
// Mods
|
|
||||||
n = Nibbler ("name.any:\"value\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.ok (good, "Att::parse (name.any:\"value\")");
|
|
||||||
t.is (a7.composeF4 (), "name:\"value\"", "Att::composeF4 -> name:\"value\"");
|
|
||||||
|
|
||||||
n = Nibbler ("name.bogus:\"value\"");
|
|
||||||
good = true;
|
|
||||||
try {a7.parse (n);} catch (...) {good = false;}
|
|
||||||
t.notok (good, "Att::parse (name.bogus:\"value\")");
|
|
||||||
|
|
||||||
// Att::type
|
|
||||||
t.is (a.type ("entry"), "date", "Att::type entry -> date");
|
|
||||||
t.is (a.type ("due"), "date", "Att::type due -> date");
|
|
||||||
t.is (a.type ("until"), "date", "Att::type until -> date");
|
|
||||||
t.is (a.type ("start"), "date", "Att::type start -> date");
|
|
||||||
t.is (a.type ("end"), "date", "Att::type end -> date");
|
|
||||||
t.is (a.type ("wait"), "date", "Att::type wait -> date");
|
|
||||||
t.is (a.type ("recur"), "duration", "Att::type recur -> duration");
|
|
||||||
t.is (a.type ("limit"), "number", "Att::type limit -> number");
|
|
||||||
t.is (a.type ("description"), "text", "Att::type description -> text");
|
|
||||||
t.is (a.type ("foo"), "text", "Att::type foo -> text");
|
|
||||||
|
|
||||||
// Att::validInternalName
|
|
||||||
t.ok (Att::validInternalName ("entry"), "internal entry");
|
|
||||||
t.ok (Att::validInternalName ("start"), "internal start");
|
|
||||||
t.ok (Att::validInternalName ("end"), "internal end");
|
|
||||||
t.ok (Att::validInternalName ("parent"), "internal parent");
|
|
||||||
t.ok (Att::validInternalName ("uuid"), "internal uuid");
|
|
||||||
t.ok (Att::validInternalName ("mask"), "internal mask");
|
|
||||||
t.ok (Att::validInternalName ("imask"), "internal imask");
|
|
||||||
t.ok (Att::validInternalName ("limit"), "internal limit");
|
|
||||||
t.ok (Att::validInternalName ("status"), "internal status");
|
|
||||||
t.ok (Att::validInternalName ("description"), "internal description");
|
|
||||||
|
|
||||||
// Att::validModifiableName
|
|
||||||
t.ok (Att::validModifiableName ("project"), "modifiable project");
|
|
||||||
t.ok (Att::validModifiableName ("priority"), "modifiable priority");
|
|
||||||
t.ok (Att::validModifiableName ("fg"), "modifiable fg");
|
|
||||||
t.ok (Att::validModifiableName ("bg"), "modifiable bg");
|
|
||||||
t.ok (Att::validModifiableName ("due"), "modifiable due");
|
|
||||||
t.ok (Att::validModifiableName ("recur"), "modifiable recur");
|
|
||||||
t.ok (Att::validModifiableName ("until"), "modifiable until");
|
|
||||||
t.ok (Att::validModifiableName ("wait"), "modifiable wait");
|
|
||||||
|
|
||||||
// Att::allNames
|
|
||||||
std::vector <std::string> all;
|
|
||||||
Att::allNames (all);
|
|
||||||
|
|
||||||
std::vector <std::string>::iterator it;
|
|
||||||
it = std::find (all.begin (), all.end (), "uuid");
|
|
||||||
t.ok (it != all.end (), "internal name 'uuid' found in Att::allNames");
|
|
||||||
it = std::find (all.begin (), all.end (), "project");
|
|
||||||
t.ok (it != all.end (), "modifiable name 'project' found in Att::allNames");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
|
@ -26,7 +26,6 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <Context.h>
|
#include <Context.h>
|
||||||
#include <Att.h>
|
|
||||||
#include <Task.h>
|
#include <Task.h>
|
||||||
#include <test.h>
|
#include <test.h>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue