mirror of
https://github.com/GothenburgBitFactory/taskwarrior.git
synced 2025-06-26 10:54:26 +02:00
Integration - TDB*
- Obsoleted TDB.h, TDB.cpp - Implemented missing functionality in TDB2.cpp
This commit is contained in:
parent
1560463f9e
commit
53d0d1cbac
4 changed files with 69 additions and 540 deletions
462
src/TDB.cpp
462
src/TDB.cpp
|
@ -1,462 +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 <fstream>
|
||||
#include <sstream>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "T.h"
|
||||
#include "TDB.h"
|
||||
#include "util.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
TDB::TDB ()
|
||||
: mPendingFile ("")
|
||||
, mCompletedFile ("")
|
||||
, mId (1)
|
||||
, mNoLock (false)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
TDB::~TDB ()
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TDB::dataDirectory (const std::string& directory)
|
||||
{
|
||||
if (! access (expandPath (directory).c_str (), F_OK))
|
||||
{
|
||||
mPendingFile = directory + "/pending.data";
|
||||
mCompletedFile = directory + "/completed.data";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string error = "Directory '";
|
||||
error += directory;
|
||||
error += "' does not exist, or is not readable and writable.";
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Combine allPendingT with allCompletedT.
|
||||
// Note: this method is O(N1) + O(N2), where N2 is not bounded.
|
||||
bool TDB::allT (std::vector <T>& all)
|
||||
{
|
||||
all.clear ();
|
||||
|
||||
// Retrieve all the pending records.
|
||||
std::vector <T> allp;
|
||||
if (allPendingT (allp))
|
||||
{
|
||||
std::vector <T>::iterator i;
|
||||
for (i = allp.begin (); i != allp.end (); ++i)
|
||||
all.push_back (*i);
|
||||
|
||||
// Retrieve all the completed records.
|
||||
std::vector <T> allc;
|
||||
if (allCompletedT (allc))
|
||||
{
|
||||
for (i = allc.begin (); i != allc.end (); ++i)
|
||||
all.push_back (*i);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Only accesses to the pending file result in Tasks that have assigned ids.
|
||||
bool TDB::allPendingT (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++);
|
||||
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::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);
|
||||
if (length > 1)
|
||||
{
|
||||
line[length - 1] = '\0'; // Kill \n
|
||||
contents.push_back (line);
|
||||
}
|
||||
}
|
||||
|
||||
fclose (in);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 TDB::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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int TDB::nextId ()
|
||||
{
|
||||
return mId++;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void TDB::noLock ()
|
||||
{
|
||||
mNoLock = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
68
src/TDB.h
68
src/TDB.h
|
@ -1,68 +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_TDB
|
||||
#define INCLUDED_TDB
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "T.h"
|
||||
|
||||
class TDB
|
||||
{
|
||||
public:
|
||||
TDB ();
|
||||
~TDB ();
|
||||
|
||||
void dataDirectory (const std::string&);
|
||||
bool allT (std::vector <T>&);
|
||||
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 ();
|
||||
|
||||
private:
|
||||
bool lock (FILE*) const;
|
||||
bool overwritePending (std::vector <T>&);
|
||||
bool writePending (const T&);
|
||||
bool writeCompleted (const T&);
|
||||
bool readLockedFile (const std::string&, std::vector <std::string>&) const;
|
||||
|
||||
private:
|
||||
std::string mPendingFile;
|
||||
std::string mCompletedFile;
|
||||
int mId;
|
||||
bool mNoLock;
|
||||
};
|
||||
|
||||
#endif
|
||||
////////////////////////////////////////////////////////////////////////////////
|
73
src/TDB2.cpp
73
src/TDB2.cpp
|
@ -107,6 +107,11 @@ void TDB2::lock (bool lockFile /* = true */)
|
|||
{
|
||||
mLock = lockFile;
|
||||
|
||||
mPending.clear ();
|
||||
// mCompleted.clear ();
|
||||
mNew.clear ();
|
||||
mPending.clear ();
|
||||
|
||||
foreach (location, mLocations)
|
||||
{
|
||||
location->pending = openAndLock (location->path + "/pending.data");
|
||||
|
@ -121,6 +126,11 @@ void TDB2::unlock ()
|
|||
{
|
||||
if (mAllOpenAndLocked)
|
||||
{
|
||||
mPending.clear ();
|
||||
// mCompleted.clear ();
|
||||
mNew.clear ();
|
||||
mModified.clear ();
|
||||
|
||||
foreach (location, mLocations)
|
||||
{
|
||||
fclose (location->pending);
|
||||
|
@ -173,6 +183,7 @@ int TDB2::loadPending (std::vector <Task>& tasks, Filter& filter)
|
|||
Task task (line);
|
||||
task.id = mId++;
|
||||
|
||||
mPending.push_back (task);
|
||||
if (filter.pass (task))
|
||||
tasks.push_back (task);
|
||||
}
|
||||
|
@ -223,6 +234,7 @@ int TDB2::loadCompleted (std::vector <Task>& tasks, Filter& filter)
|
|||
Task task (line);
|
||||
task.id = mId++;
|
||||
|
||||
// mCompleted.push_back (task);
|
||||
if (filter.pass (task))
|
||||
tasks.push_back (task);
|
||||
}
|
||||
|
@ -245,36 +257,77 @@ int TDB2::loadCompleted (std::vector <Task>& tasks, Filter& filter)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO Write to transaction log.
|
||||
// Note: mLocations[0] is where all tasks are written.
|
||||
void TDB2::add (Task& after)
|
||||
void TDB2::add (Task& task)
|
||||
{
|
||||
// Seek to end of pending.
|
||||
fseek (mLocations[0].pending, 0, SEEK_END);
|
||||
|
||||
// Write after.composeF4 ().
|
||||
fputs (after.composeF4 ().c_str (), mLocations[0].pending);
|
||||
mNew.push_back (task);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO Write to transaction log.
|
||||
void TDB2::update (Task& before, Task& after)
|
||||
{
|
||||
throw std::string ("unimplemented TDB2::update");
|
||||
mModified.push_back (after);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TODO Writes all, including comments
|
||||
// Interestingly, only the pending file gets written to. The completed file is
|
||||
// only modified by TDB2::gc.
|
||||
int TDB2::commit ()
|
||||
{
|
||||
// TODO Two passes: first the pending file.
|
||||
// then the completed file.
|
||||
int quantity = mNew.size () + mModified.size ();
|
||||
|
||||
throw std::string ("unimplemented TDB2::commit");
|
||||
// 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 TDB2::upgrade ()
|
||||
{
|
||||
// TODO Read all pending
|
||||
// TODO Write out all pending
|
||||
|
||||
// TODO Read all completed
|
||||
// TODO Write out all completed
|
||||
|
||||
throw std::string ("unimplemented TDB2::upgrade");
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,12 @@ private:
|
|||
bool mAllOpenAndLocked;
|
||||
int mId;
|
||||
|
||||
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.
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue