Merge branch '1.8.0' of git@github.com:pbeckingham/task into 1.8.0
This commit is contained in:
@@ -34,7 +34,7 @@
|
|||||||
#include "Subst.h"
|
#include "Subst.h"
|
||||||
#include "Cmd.h"
|
#include "Cmd.h"
|
||||||
#include "Task.h"
|
#include "Task.h"
|
||||||
#include "TDB2.h"
|
#include "TDB.h"
|
||||||
#include "StringTable.h"
|
#include "StringTable.h"
|
||||||
|
|
||||||
class Context
|
class Context
|
||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
Sequence sequence;
|
Sequence sequence;
|
||||||
Subst subst;
|
Subst subst;
|
||||||
Task task;
|
Task task;
|
||||||
TDB2 tdb;
|
TDB tdb;
|
||||||
StringTable stringtable;
|
StringTable stringtable;
|
||||||
std::string program;
|
std::string program;
|
||||||
std::vector <std::string> args;
|
std::vector <std::string> args;
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ bin_PROGRAMS = task
|
|||||||
task_SOURCES = Att.cpp Cmd.cpp Config.cpp Context.cpp Date.cpp Duration.cpp \
|
task_SOURCES = Att.cpp Cmd.cpp Config.cpp Context.cpp Date.cpp Duration.cpp \
|
||||||
Filter.cpp Grid.cpp Keymap.cpp Location.cpp Nibbler.cpp \
|
Filter.cpp Grid.cpp Keymap.cpp Location.cpp Nibbler.cpp \
|
||||||
Record.cpp Sequence.cpp StringTable.cpp Subst.cpp Task.cpp \
|
Record.cpp Sequence.cpp StringTable.cpp Subst.cpp Task.cpp \
|
||||||
TDB2.cpp Table.cpp Timer.cpp color.cpp command.cpp edit.cpp \
|
TDB.cpp Table.cpp Timer.cpp color.cpp command.cpp edit.cpp \
|
||||||
import.cpp interactive.cpp valid.cpp recur.cpp report.cpp \
|
import.cpp interactive.cpp valid.cpp recur.cpp report.cpp \
|
||||||
custom.cpp rules.cpp main.cpp text.cpp util.cpp \
|
custom.cpp rules.cpp main.cpp text.cpp util.cpp \
|
||||||
Att.h Cmd.h Config.h Context.h Date.h Duration.h Filter.h \
|
Att.h Cmd.h Config.h Context.h Date.h Duration.h Filter.h \
|
||||||
Grid.h Keymap.h Location.h Nibbler.h Record.h Sequence.h \
|
Grid.h Keymap.h Location.h Nibbler.h Record.h Sequence.h \
|
||||||
StringTable.h Subst.h Task.h TDB2.h Table.h Timer.h color.h \
|
StringTable.h Subst.h Task.h TDB.h Table.h Timer.h color.h \
|
||||||
i18n.h main.h text.h util.h
|
i18n.h main.h text.h util.h
|
||||||
|
|||||||
631
src/TDB.cpp
631
src/TDB.cpp
@@ -24,386 +24,311 @@
|
|||||||
// USA
|
// USA
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <sys/file.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "T.h"
|
#include <iostream>
|
||||||
#include "TDB.h"
|
#include <sstream>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/file.h>
|
||||||
|
#include "text.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "TDB.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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 (Filter)
|
||||||
|
// | | | read all
|
||||||
|
// | | | apply filter
|
||||||
|
// | | | return subset
|
||||||
|
// | | |
|
||||||
|
// | | +- TDB::add (T)
|
||||||
|
// | | |
|
||||||
|
// | | +- TDB::update (T, T')
|
||||||
|
// | | |
|
||||||
|
// | | +- TDB::commit
|
||||||
|
// | | write all
|
||||||
|
// | |
|
||||||
|
// | +- TDB::unlock
|
||||||
|
// | [unlock]
|
||||||
|
// | close
|
||||||
|
// |
|
||||||
|
// +- TDB::~TDB
|
||||||
|
// [TDB::unlock]
|
||||||
|
//
|
||||||
TDB::TDB ()
|
TDB::TDB ()
|
||||||
: mPendingFile ("")
|
: mLock (true)
|
||||||
, mCompletedFile ("")
|
, mAllOpenAndLocked (false)
|
||||||
, mId (1)
|
, mId (1)
|
||||||
, mNoLock (false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
TDB::~TDB ()
|
TDB::~TDB ()
|
||||||
{
|
{
|
||||||
|
if (mAllOpenAndLocked)
|
||||||
|
unlock ();
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB::dataDirectory (const std::string& directory)
|
void TDB::clear ()
|
||||||
{
|
{
|
||||||
if (! access (expandPath (directory).c_str (), F_OK))
|
mLocations.clear ();
|
||||||
{
|
mLock = true;
|
||||||
mPendingFile = directory + "/pending.data";
|
|
||||||
mCompletedFile = directory + "/completed.data";
|
if (mAllOpenAndLocked)
|
||||||
}
|
unlock ();
|
||||||
else
|
|
||||||
{
|
mAllOpenAndLocked = false;
|
||||||
std::string error = "Directory '";
|
|
||||||
error += directory;
|
|
||||||
error += "' does not exist, or is not readable and writable.";
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Combine allPendingT with allCompletedT.
|
void TDB::location (const std::string& path)
|
||||||
// Note: this method is O(N1) + O(N2), where N2 is not bounded.
|
|
||||||
bool TDB::allT (std::vector <T>& all)
|
|
||||||
{
|
{
|
||||||
all.clear ();
|
if (access (expandPath (path).c_str (), F_OK))
|
||||||
|
throw std::string ("Data location '") +
|
||||||
|
path +
|
||||||
|
"' does not exist, or is not readable and writable.";
|
||||||
|
|
||||||
// Retrieve all the pending records.
|
mLocations.push_back (Location (path));
|
||||||
std::vector <T> allp;
|
}
|
||||||
if (allPendingT (allp))
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void TDB::lock (bool lockFile /* = true */)
|
||||||
|
{
|
||||||
|
mLock = lockFile;
|
||||||
|
|
||||||
|
mPending.clear ();
|
||||||
|
// mCompleted.clear ();
|
||||||
|
mNew.clear ();
|
||||||
|
mPending.clear ();
|
||||||
|
|
||||||
|
foreach (location, mLocations)
|
||||||
{
|
{
|
||||||
std::vector <T>::iterator i;
|
location->pending = openAndLock (location->path + "/pending.data");
|
||||||
for (i = allp.begin (); i != allp.end (); ++i)
|
location->completed = openAndLock (location->path + "/completed.data");
|
||||||
all.push_back (*i);
|
}
|
||||||
|
|
||||||
// Retrieve all the completed records.
|
mAllOpenAndLocked = true;
|
||||||
std::vector <T> allc;
|
}
|
||||||
if (allCompletedT (allc))
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
void TDB::unlock ()
|
||||||
|
{
|
||||||
|
if (mAllOpenAndLocked)
|
||||||
|
{
|
||||||
|
mPending.clear ();
|
||||||
|
// mCompleted.clear ();
|
||||||
|
mNew.clear ();
|
||||||
|
mModified.clear ();
|
||||||
|
|
||||||
|
foreach (location, mLocations)
|
||||||
{
|
{
|
||||||
for (i = allc.begin (); i != allc.end (); ++i)
|
fclose (location->pending);
|
||||||
all.push_back (*i);
|
location->pending = NULL;
|
||||||
|
fclose (location->completed);
|
||||||
return true;
|
location->completed = NULL;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Only accesses to the pending file result in Tasks that have assigned ids.
|
|
||||||
bool TDB::pendingT (std::vector <T>& all)
|
|
||||||
{
|
|
||||||
all.clear ();
|
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
|
||||||
if (readLockedFile (mPendingFile, lines))
|
|
||||||
{
|
|
||||||
mId = 1;
|
|
||||||
|
|
||||||
int line = 1;
|
|
||||||
std::vector <std::string>::iterator it;
|
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
T t (*it);
|
|
||||||
t.setId (mId++);
|
|
||||||
if (t.getStatus () == T::pending)
|
|
||||||
all.push_back (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream more;
|
|
||||||
more << " Line " << line << ", in " << "pending.data";
|
|
||||||
|
|
||||||
throw e + more.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
++line;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
mAllOpenAndLocked = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Only accesses to the pending file result in Tasks that have assigned ids.
|
// Returns number of filtered tasks.
|
||||||
bool TDB::allPendingT (std::vector <T>& all)
|
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
||||||
|
// multiple files.
|
||||||
|
int TDB::load (std::vector <Task>& tasks, Filter& filter)
|
||||||
{
|
{
|
||||||
all.clear ();
|
loadPending (tasks, filter);
|
||||||
|
loadCompleted (tasks, filter);
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
return tasks.size ();
|
||||||
if (readLockedFile (mPendingFile, lines))
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// 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, Filter& filter)
|
||||||
|
{
|
||||||
|
std::string file;
|
||||||
|
int line_number;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
mId = 1;
|
char line[T_LINE_MAX];
|
||||||
|
foreach (location, mLocations)
|
||||||
int line = 1;
|
|
||||||
std::vector <std::string>::iterator it;
|
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
|
||||||
{
|
{
|
||||||
try
|
line_number = 1;
|
||||||
{
|
file = location->path + "/pending.data";
|
||||||
T t (*it);
|
|
||||||
t.setId (mId++);
|
|
||||||
all.push_back (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
fseek (location->pending, 0, SEEK_SET);
|
||||||
{
|
while (fgets (line, T_LINE_MAX, location->pending))
|
||||||
std::stringstream more;
|
|
||||||
more << " Line " << line << ", in " << "pending.data";
|
|
||||||
|
|
||||||
throw e + more.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
++line;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::completedT (std::vector <T>& all) const
|
|
||||||
{
|
|
||||||
all.clear ();
|
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
|
||||||
if (readLockedFile (mCompletedFile, lines))
|
|
||||||
{
|
|
||||||
int line = 1;
|
|
||||||
std::vector <std::string>::iterator it;
|
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
T t (*it);
|
|
||||||
if (t.getStatus () != T::deleted)
|
|
||||||
all.push_back (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream more;
|
|
||||||
more << " Line " << line << ", in " << "pending.data";
|
|
||||||
|
|
||||||
throw e + more.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
++line;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::allCompletedT (std::vector <T>& all) const
|
|
||||||
{
|
|
||||||
all.clear ();
|
|
||||||
|
|
||||||
std::vector <std::string> lines;
|
|
||||||
if (readLockedFile (mCompletedFile, lines))
|
|
||||||
{
|
|
||||||
int line = 1;
|
|
||||||
std::vector <std::string>::iterator it;
|
|
||||||
for (it = lines.begin (); it != lines.end (); ++it)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
T t (*it);
|
|
||||||
all.push_back (t);
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream more;
|
|
||||||
more << " Line " << line << ", in " << "pending.data";
|
|
||||||
|
|
||||||
throw e + more.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
++line;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::addT (const T& t)
|
|
||||||
{
|
|
||||||
T task (t);
|
|
||||||
std::vector <std::string> tags;
|
|
||||||
task.getTags (tags);
|
|
||||||
|
|
||||||
// +tag or -tag are both considered valid tags to add to a new pending task.
|
|
||||||
// Generating an error here would not be friendly.
|
|
||||||
for (unsigned int i = 0; i < tags.size (); ++i)
|
|
||||||
{
|
|
||||||
if (tags[i][0] == '-' || tags[i][0] == '+')
|
|
||||||
{
|
|
||||||
task.removeTag (tags[i]);
|
|
||||||
task.addTag (tags[i].substr (1, std::string::npos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (task.getStatus () == T::pending ||
|
|
||||||
task.getStatus () == T::recurring)
|
|
||||||
{
|
|
||||||
return writePending (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
return writeCompleted (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::modifyT (const T& t)
|
|
||||||
{
|
|
||||||
T modified (t);
|
|
||||||
|
|
||||||
std::vector <T> all;
|
|
||||||
allPendingT (all);
|
|
||||||
|
|
||||||
std::vector <T> pending;
|
|
||||||
|
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
|
||||||
if (it->getId () == t.getId ())
|
|
||||||
{
|
|
||||||
modified.setUUID (it->getUUID ());
|
|
||||||
pending.push_back (modified);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pending.push_back (*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
return overwritePending (pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::lock (FILE* file) const
|
|
||||||
{
|
|
||||||
if (mNoLock)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return flock (fileno (file), LOCK_EX) ? false : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::overwritePending (std::vector <T>& all)
|
|
||||||
{
|
|
||||||
// Write a single task to the pending file
|
|
||||||
FILE* out;
|
|
||||||
if ((out = fopen (mPendingFile.c_str (), "w")))
|
|
||||||
{
|
|
||||||
int retry = 0;
|
|
||||||
if (!mNoLock)
|
|
||||||
while (flock (fileno (out), LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.1);
|
|
||||||
|
|
||||||
std::vector <T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
fputs (it->compose ().c_str (), out);
|
|
||||||
|
|
||||||
fclose (out);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::writePending (const T& t)
|
|
||||||
{
|
|
||||||
// Write a single task to the pending file
|
|
||||||
FILE* out;
|
|
||||||
if ((out = fopen (mPendingFile.c_str (), "a")))
|
|
||||||
{
|
|
||||||
int retry = 0;
|
|
||||||
if (!mNoLock)
|
|
||||||
while (flock (fileno (out), LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.1);
|
|
||||||
|
|
||||||
fputs (t.compose ().c_str (), out);
|
|
||||||
fclose (out);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::writeCompleted (const T& t)
|
|
||||||
{
|
|
||||||
// Write a single task to the pending file
|
|
||||||
FILE* out;
|
|
||||||
if ((out = fopen (mCompletedFile.c_str (), "a")))
|
|
||||||
{
|
|
||||||
int retry = 0;
|
|
||||||
if (!mNoLock)
|
|
||||||
while (flock (fileno (out), LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.1);
|
|
||||||
|
|
||||||
fputs (t.compose ().c_str (), out);
|
|
||||||
|
|
||||||
fclose (out);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
bool TDB::readLockedFile (
|
|
||||||
const std::string& file,
|
|
||||||
std::vector <std::string>& contents) const
|
|
||||||
{
|
|
||||||
contents.clear ();
|
|
||||||
|
|
||||||
if (! access (file.c_str (), F_OK | R_OK))
|
|
||||||
{
|
|
||||||
FILE* in;
|
|
||||||
if ((in = fopen (file.c_str (), "r")))
|
|
||||||
{
|
|
||||||
int retry = 0;
|
|
||||||
if (!mNoLock)
|
|
||||||
while (flock (fileno (in), LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.1);
|
|
||||||
|
|
||||||
char line[T_LINE_MAX];
|
|
||||||
while (fgets (line, T_LINE_MAX, in))
|
|
||||||
{
|
{
|
||||||
int length = ::strlen (line);
|
int length = ::strlen (line);
|
||||||
if (length > 1)
|
if (length > 1)
|
||||||
{
|
{
|
||||||
|
// TODO Add hidden attribute indicating source?
|
||||||
line[length - 1] = '\0'; // Kill \n
|
line[length - 1] = '\0'; // Kill \n
|
||||||
contents.push_back (line);
|
Task task (line);
|
||||||
}
|
task.id = mId++;
|
||||||
}
|
|
||||||
|
|
||||||
fclose (in);
|
mPending.push_back (task);
|
||||||
return true;
|
if (filter.pass (task))
|
||||||
|
tasks.push_back (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
++line_number;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
catch (std::string& e)
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
s << " int " << 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, Filter& filter)
|
||||||
|
{
|
||||||
|
std::string file;
|
||||||
|
int line_number;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
char line[T_LINE_MAX];
|
||||||
|
foreach (location, mLocations)
|
||||||
|
{
|
||||||
|
// TODO If the filter contains Status:x where x is not deleted or
|
||||||
|
// completed, then this can be skipped.
|
||||||
|
|
||||||
|
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 > 1)
|
||||||
|
{
|
||||||
|
// TODO Add hidden attribute indicating source?
|
||||||
|
line[length - 1] = '\0'; // Kill \n
|
||||||
|
Task task (line);
|
||||||
|
task.id = mId++;
|
||||||
|
|
||||||
|
// mCompleted.push_back (task);
|
||||||
|
if (filter.pass (task))
|
||||||
|
tasks.push_back (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
++line_number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
catch (std::string& e)
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
s << " int " << file << " at line " << line_number;
|
||||||
|
throw e + s.str ();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks.size ();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO Write to transaction log.
|
||||||
|
// Note: mLocations[0] is where all tasks are written.
|
||||||
|
void TDB::add (Task& task)
|
||||||
|
{
|
||||||
|
mNew.push_back (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO Write to transaction log.
|
||||||
|
void TDB::update (Task& before, Task& after)
|
||||||
|
{
|
||||||
|
mModified.push_back (after);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO Writes all, including comments
|
||||||
|
// Interestingly, only the pending file gets written to. The completed file is
|
||||||
|
// only modified by TDB::gc.
|
||||||
|
int TDB::commit ()
|
||||||
|
{
|
||||||
|
int quantity = mNew.size () + mModified.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 (mNew.size () && ! mModified.size ())
|
||||||
|
{
|
||||||
|
fseek (mLocations[0].pending, 0, SEEK_END);
|
||||||
|
foreach (task, mNew)
|
||||||
|
{
|
||||||
|
mPending.push_back (*task);
|
||||||
|
fputs (task->composeF4 ().c_str (), mLocations[0].pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
mNew.clear ();
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The alternative is to potentially rewrite both files.
|
||||||
|
else if (mNew.size () || mModified.size ())
|
||||||
|
{
|
||||||
|
foreach (task, mPending)
|
||||||
|
foreach (mtask, mModified)
|
||||||
|
if (task->id == mtask->id)
|
||||||
|
*task = *mtask;
|
||||||
|
|
||||||
|
mModified.clear ();
|
||||||
|
|
||||||
|
foreach (task, mNew)
|
||||||
|
mPending.push_back (*task);
|
||||||
|
|
||||||
|
mNew.clear ();
|
||||||
|
|
||||||
|
// Write out all pending.
|
||||||
|
fseek (mLocations[0].pending, 0, SEEK_SET);
|
||||||
|
// TODO Do I need to truncate the file? Does file I/O even work that way
|
||||||
|
// any more? I forget.
|
||||||
|
foreach (task, mPending)
|
||||||
|
fputs (task->composeF4 ().c_str (), mLocations[0].pending);
|
||||||
|
}
|
||||||
|
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TODO -> FF4
|
||||||
|
void TDB::upgrade ()
|
||||||
|
{
|
||||||
|
// TODO Read all pending
|
||||||
|
// TODO Write out all pending
|
||||||
|
|
||||||
|
// TODO Read all completed
|
||||||
|
// TODO Write out all completed
|
||||||
|
|
||||||
|
throw std::string ("unimplemented TDB::upgrade");
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -412,7 +337,7 @@ bool TDB::readLockedFile (
|
|||||||
int TDB::gc ()
|
int TDB::gc ()
|
||||||
{
|
{
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
/*
|
||||||
// Read everything from the pending file.
|
// Read everything from the pending file.
|
||||||
std::vector <T> all;
|
std::vector <T> all;
|
||||||
allPendingT (all);
|
allPendingT (all);
|
||||||
@@ -442,21 +367,33 @@ int TDB::gc ()
|
|||||||
// task was transferred.
|
// task was transferred.
|
||||||
if (count)
|
if (count)
|
||||||
overwritePending (pending);
|
overwritePending (pending);
|
||||||
|
*/
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
int TDB::nextId ()
|
FILE* TDB::openAndLock (const std::string& file)
|
||||||
{
|
{
|
||||||
return mId++;
|
// Check for access.
|
||||||
|
if (access (file.c_str (), F_OK | R_OK | W_OK))
|
||||||
|
throw std::string ("Task does not have the correct permissions for '") +
|
||||||
|
file + "'.";
|
||||||
|
|
||||||
|
// Open the file.
|
||||||
|
FILE* in = fopen (file.c_str (), "r+");
|
||||||
|
if (!in)
|
||||||
|
throw std::string ("Could not open '") + file + "'.";
|
||||||
|
|
||||||
|
// Lock if desired. Try three times before failing.
|
||||||
|
int retry = 0;
|
||||||
|
if (mLock)
|
||||||
|
while (flock (fileno (in), LOCK_EX) && ++retry <= 3)
|
||||||
|
delay (0.1);
|
||||||
|
|
||||||
|
if (retry > 3)
|
||||||
|
throw std::string ("Could not lock '") + file + "'.";
|
||||||
|
|
||||||
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
void TDB::noLock ()
|
|
||||||
{
|
|
||||||
mNoLock = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
|||||||
57
src/TDB.h
57
src/TDB.h
@@ -27,41 +27,54 @@
|
|||||||
#ifndef INCLUDED_TDB
|
#ifndef INCLUDED_TDB
|
||||||
#define INCLUDED_TDB
|
#define INCLUDED_TDB
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "T.h"
|
#include <Location.h>
|
||||||
|
#include <Filter.h>
|
||||||
|
#include <Task.h>
|
||||||
|
|
||||||
|
// Length of longest line.
|
||||||
|
#define T_LINE_MAX 32768
|
||||||
|
|
||||||
class TDB
|
class TDB
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TDB ();
|
TDB (); // Default constructor
|
||||||
~TDB ();
|
~TDB (); // Destructor
|
||||||
|
|
||||||
void dataDirectory (const std::string&);
|
void clear ();
|
||||||
bool allT (std::vector <T>&);
|
void location (const std::string&);
|
||||||
bool pendingT (std::vector <T>&);
|
|
||||||
bool allPendingT (std::vector <T>&);
|
|
||||||
bool completedT (std::vector <T>&) const;
|
|
||||||
bool allCompletedT (std::vector <T>&) const;
|
|
||||||
bool addT (const T&);
|
|
||||||
bool modifyT (const T&);
|
|
||||||
int gc ();
|
|
||||||
int nextId ();
|
|
||||||
|
|
||||||
void noLock ();
|
void lock (bool lockFile = true);
|
||||||
|
void unlock ();
|
||||||
|
|
||||||
|
int load (std::vector <Task>&, Filter&);
|
||||||
|
int loadPending (std::vector <Task>&, Filter&);
|
||||||
|
int loadCompleted (std::vector <Task>&, Filter&);
|
||||||
|
|
||||||
|
void add (Task&); // Single task add to pending
|
||||||
|
void update (Task&, Task&); // Single task update to pending
|
||||||
|
int commit (); // Write out all tasks
|
||||||
|
void upgrade (); // Convert both files to FF4
|
||||||
|
int gc (); // Clean up pending
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool lock (FILE*) const;
|
FILE* openAndLock (const std::string&);
|
||||||
bool overwritePending (std::vector <T>&);
|
|
||||||
bool writePending (const T&);
|
|
||||||
bool writeCompleted (const T&);
|
|
||||||
bool readLockedFile (const std::string&, std::vector <std::string>&) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string mPendingFile;
|
std::vector <Location> mLocations;
|
||||||
std::string mCompletedFile;
|
bool mLock;
|
||||||
|
bool mAllOpenAndLocked;
|
||||||
int mId;
|
int mId;
|
||||||
bool mNoLock;
|
|
||||||
|
std::vector <Task> mPending; // Contents of pending.data
|
||||||
|
// std::vector <Task> mCompleted; // Contents of completed.data
|
||||||
|
|
||||||
|
std::vector <Task> mNew; // Uncommitted new tasks
|
||||||
|
std::vector <Task> mModified; // Uncommitted modified tasks
|
||||||
|
|
||||||
|
// TODO Need cache of raw file contents to preserve comments.
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
346
src/TDB2.cpp
346
src/TDB2.cpp
@@ -1,346 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// task - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2009, Paul Beckingham.
|
|
||||||
// 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 <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/file.h>
|
|
||||||
#include "text.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "TDB2.h"
|
|
||||||
#include "main.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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 (Filter)
|
|
||||||
// | | | read all
|
|
||||||
// | | | apply filter
|
|
||||||
// | | | return subset
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::add (T)
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::update (T, T')
|
|
||||||
// | | |
|
|
||||||
// | | +- TDB::commit
|
|
||||||
// | | write all
|
|
||||||
// | |
|
|
||||||
// | +- TDB::unlock
|
|
||||||
// | [unlock]
|
|
||||||
// | close
|
|
||||||
// |
|
|
||||||
// +- TDB::~TDB
|
|
||||||
// [TDB::unlock]
|
|
||||||
//
|
|
||||||
TDB2::TDB2 ()
|
|
||||||
: mLock (true)
|
|
||||||
, mAllOpenAndLocked (false)
|
|
||||||
, mId (1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
TDB2::~TDB2 ()
|
|
||||||
{
|
|
||||||
if (mAllOpenAndLocked)
|
|
||||||
unlock ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB2::clear ()
|
|
||||||
{
|
|
||||||
mLocations.clear ();
|
|
||||||
mLock = true;
|
|
||||||
|
|
||||||
if (mAllOpenAndLocked)
|
|
||||||
unlock ();
|
|
||||||
|
|
||||||
mAllOpenAndLocked = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB2::location (const std::string& path)
|
|
||||||
{
|
|
||||||
if (access (expandPath (path).c_str (), F_OK))
|
|
||||||
throw std::string ("Data location '") +
|
|
||||||
path +
|
|
||||||
"' does not exist, or is not readable and writable.";
|
|
||||||
|
|
||||||
mLocations.push_back (Location (path));
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB2::lock (bool lockFile /* = true */)
|
|
||||||
{
|
|
||||||
mLock = lockFile;
|
|
||||||
|
|
||||||
foreach (location, mLocations)
|
|
||||||
{
|
|
||||||
location->pending = openAndLock (location->path + "/pending.data");
|
|
||||||
location->completed = openAndLock (location->path + "/completed.data");
|
|
||||||
}
|
|
||||||
|
|
||||||
mAllOpenAndLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
void TDB2::unlock ()
|
|
||||||
{
|
|
||||||
if (mAllOpenAndLocked)
|
|
||||||
{
|
|
||||||
foreach (location, mLocations)
|
|
||||||
{
|
|
||||||
fclose (location->pending);
|
|
||||||
location->pending = NULL;
|
|
||||||
fclose (location->completed);
|
|
||||||
location->completed = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAllOpenAndLocked = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Returns number of filtered tasks.
|
|
||||||
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
|
||||||
// multiple files.
|
|
||||||
int TDB2::load (std::vector <Task>& tasks, Filter& filter)
|
|
||||||
{
|
|
||||||
loadPending (tasks, filter);
|
|
||||||
loadCompleted (tasks, filter);
|
|
||||||
|
|
||||||
return tasks.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Returns number of filtered tasks.
|
|
||||||
// Note: tasks.clear () is deliberately not called, to allow the combination of
|
|
||||||
// multiple files.
|
|
||||||
int TDB2::loadPending (std::vector <Task>& tasks, Filter& filter)
|
|
||||||
{
|
|
||||||
std::string file;
|
|
||||||
int line_number;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
char line[T_LINE_MAX];
|
|
||||||
foreach (location, mLocations)
|
|
||||||
{
|
|
||||||
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 > 1)
|
|
||||||
{
|
|
||||||
// TODO Add hidden attribute indicating source?
|
|
||||||
line[length - 1] = '\0'; // Kill \n
|
|
||||||
Task task (line);
|
|
||||||
task.id = mId++;
|
|
||||||
|
|
||||||
if (filter.pass (task))
|
|
||||||
tasks.push_back (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
++line_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << " int " << 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 TDB2::loadCompleted (std::vector <Task>& tasks, Filter& filter)
|
|
||||||
{
|
|
||||||
std::string file;
|
|
||||||
int line_number;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
char line[T_LINE_MAX];
|
|
||||||
foreach (location, mLocations)
|
|
||||||
{
|
|
||||||
// TODO If the filter contains Status:x where x is not deleted or
|
|
||||||
// completed, then this can be skipped.
|
|
||||||
|
|
||||||
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 > 1)
|
|
||||||
{
|
|
||||||
// TODO Add hidden attribute indicating source?
|
|
||||||
line[length - 1] = '\0'; // Kill \n
|
|
||||||
Task task (line);
|
|
||||||
task.id = mId++;
|
|
||||||
|
|
||||||
if (filter.pass (task))
|
|
||||||
tasks.push_back (task);
|
|
||||||
}
|
|
||||||
|
|
||||||
++line_number;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
catch (std::string& e)
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
s << " int " << file << " at line " << line_number;
|
|
||||||
throw e + s.str ();
|
|
||||||
}
|
|
||||||
|
|
||||||
return tasks.size ();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO Write to transaction log.
|
|
||||||
// Note: mLocations[0] is where all tasks are written.
|
|
||||||
void TDB2::add (Task& after)
|
|
||||||
{
|
|
||||||
// Seek to end of pending.
|
|
||||||
fseek (mLocations[0].pending, 0, SEEK_END);
|
|
||||||
|
|
||||||
// Write after.composeF4 ().
|
|
||||||
fputs (after.composeF4 ().c_str (), mLocations[0].pending);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO Write to transaction log.
|
|
||||||
void TDB2::update (Task& before, Task& after)
|
|
||||||
{
|
|
||||||
throw std::string ("unimplemented TDB2::update");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO Writes all, including comments
|
|
||||||
int TDB2::commit ()
|
|
||||||
{
|
|
||||||
// TODO Two passes: first the pending file.
|
|
||||||
// then the completed file.
|
|
||||||
|
|
||||||
throw std::string ("unimplemented TDB2::commit");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// TODO -> FF4
|
|
||||||
void TDB2::upgrade ()
|
|
||||||
{
|
|
||||||
throw std::string ("unimplemented TDB2::upgrade");
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// 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.
|
|
||||||
int TDB2::gc ()
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
/*
|
|
||||||
// Read everything from the pending file.
|
|
||||||
std::vector <T> all;
|
|
||||||
allPendingT (all);
|
|
||||||
|
|
||||||
// A list of the truly pending tasks.
|
|
||||||
std::vector <T> pending;
|
|
||||||
|
|
||||||
std::vector<T>::iterator it;
|
|
||||||
for (it = all.begin (); it != all.end (); ++it)
|
|
||||||
{
|
|
||||||
// Some tasks stay in the pending file.
|
|
||||||
if (it->getStatus () == T::pending ||
|
|
||||||
it->getStatus () == T::recurring)
|
|
||||||
{
|
|
||||||
pending.push_back (*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Others are transferred to the completed file.
|
|
||||||
else
|
|
||||||
{
|
|
||||||
writeCompleted (*it);
|
|
||||||
++count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump all clean tasks into pending. But don't bother unless at least one
|
|
||||||
// task was transferred.
|
|
||||||
if (count)
|
|
||||||
overwritePending (pending);
|
|
||||||
*/
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
FILE* TDB2::openAndLock (const std::string& file)
|
|
||||||
{
|
|
||||||
// Check for access.
|
|
||||||
if (access (file.c_str (), F_OK | R_OK | W_OK))
|
|
||||||
throw std::string ("Task does not have the correct permissions for '") +
|
|
||||||
file + "'.";
|
|
||||||
|
|
||||||
// Open the file.
|
|
||||||
FILE* in = fopen (file.c_str (), "r+");
|
|
||||||
if (!in)
|
|
||||||
throw std::string ("Could not open '") + file + "'.";
|
|
||||||
|
|
||||||
// Lock if desired. Try three times before failing.
|
|
||||||
int retry = 0;
|
|
||||||
if (mLock)
|
|
||||||
while (flock (fileno (in), LOCK_EX) && ++retry <= 3)
|
|
||||||
delay (0.1);
|
|
||||||
|
|
||||||
if (retry > 3)
|
|
||||||
throw std::string ("Could not lock '") + file + "'.";
|
|
||||||
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
75
src/TDB2.h
75
src/TDB2.h
@@ -1,75 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// task - a command line task list manager.
|
|
||||||
//
|
|
||||||
// Copyright 2006 - 2009, Paul Beckingham.
|
|
||||||
// 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_TDB2
|
|
||||||
#define INCLUDED_TDB2
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <Location.h>
|
|
||||||
#include <Filter.h>
|
|
||||||
#include <Task.h>
|
|
||||||
|
|
||||||
// Length of longest line.
|
|
||||||
#define T_LINE_MAX 32768
|
|
||||||
|
|
||||||
class TDB2
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TDB2 (); // Default constructor
|
|
||||||
~TDB2 (); // Destructor
|
|
||||||
|
|
||||||
void clear ();
|
|
||||||
void location (const std::string&);
|
|
||||||
|
|
||||||
void lock (bool lockFile = true);
|
|
||||||
void unlock ();
|
|
||||||
|
|
||||||
int load (std::vector <Task>&, Filter&);
|
|
||||||
int loadPending (std::vector <Task>&, Filter&);
|
|
||||||
int loadCompleted (std::vector <Task>&, Filter&);
|
|
||||||
|
|
||||||
void add (Task&); // Single task add to pending
|
|
||||||
void update (Task&, Task&); // Single task update to pending
|
|
||||||
int commit (); // Write out all tasks
|
|
||||||
void upgrade (); // Convert both files to FF4
|
|
||||||
int gc (); // Clean up pending
|
|
||||||
|
|
||||||
private:
|
|
||||||
FILE* openAndLock (const std::string&);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector <Location> mLocations;
|
|
||||||
bool mLock;
|
|
||||||
bool mAllOpenAndLocked;
|
|
||||||
int mId;
|
|
||||||
|
|
||||||
// TODO Need cache of raw file contents to preserve comments.
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
1
src/tests/.gitignore
vendored
1
src/tests/.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
t.t
|
t.t
|
||||||
t2.t
|
|
||||||
t.benchmark.t
|
t.benchmark.t
|
||||||
tdb.t
|
tdb.t
|
||||||
date.t
|
date.t
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
PROJECT = t2.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
|
||||||
config.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
|
config.t seq.t att.t stringtable.t record.t nibbler.t subst.t filt.t \
|
||||||
cmd.t
|
cmd.t
|
||||||
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
|
||||||
LFLAGS = -L/usr/local/lib -lncurses
|
LFLAGS = -L/usr/local/lib -lncurses
|
||||||
OBJECTS = ../TDB2.o ../Task.o ../valid.o ../text.o ../Date.o ../Table.o \
|
OBJECTS = ../TDB.o ../Task.o ../valid.o ../text.o ../Date.o ../Table.o \
|
||||||
../Duration.o ../util.o ../Config.o ../Sequence.o ../Att.o ../Cmd.o \
|
../Duration.o ../util.o ../Config.o ../Sequence.o ../Att.o ../Cmd.o \
|
||||||
../Record.o ../StringTable.o ../Subst.o ../Nibbler.o ../Location.o \
|
../Record.o ../StringTable.o ../Subst.o ../Nibbler.o ../Location.o \
|
||||||
../Filter.o ../Context.o ../Keymap.o ../command.o ../interactive.o \
|
../Filter.o ../Context.o ../Keymap.o ../command.o ../interactive.o \
|
||||||
@@ -23,8 +23,8 @@ clean:
|
|||||||
.cpp.o:
|
.cpp.o:
|
||||||
g++ -c $(CFLAGS) $<
|
g++ -c $(CFLAGS) $<
|
||||||
|
|
||||||
t2.t: t2.t.o $(OBJECTS) test.o
|
t.t: t.t.o $(OBJECTS) test.o
|
||||||
g++ t2.t.o $(OBJECTS) test.o $(LFLAGS) -o t2.t
|
g++ t.t.o $(OBJECTS) test.o $(LFLAGS) -o t.t
|
||||||
|
|
||||||
tdb.t: tdb.t.o $(OBJECTS) test.o
|
tdb.t: tdb.t.o $(OBJECTS) test.o
|
||||||
g++ tdb.t.o $(OBJECTS) test.o $(LFLAGS) -o tdb.t
|
g++ tdb.t.o $(OBJECTS) test.o $(LFLAGS) -o tdb.t
|
||||||
|
|||||||
Reference in New Issue
Block a user