From 53d0d1cbac9e4d87318138f997445e7e5bfaaec0 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Sun, 14 Jun 2009 17:58:37 -0400 Subject: [PATCH] Integration - TDB* - Obsoleted TDB.h, TDB.cpp - Implemented missing functionality in TDB2.cpp --- src/TDB.cpp | 462 --------------------------------------------------- src/TDB.h | 68 -------- src/TDB2.cpp | 73 ++++++-- src/TDB2.h | 6 + 4 files changed, 69 insertions(+), 540 deletions(-) delete mode 100644 src/TDB.cpp delete mode 100644 src/TDB.h diff --git a/src/TDB.cpp b/src/TDB.cpp deleted file mode 100644 index 3621d995b..000000000 --- a/src/TDB.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include - -#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 & all) -{ - all.clear (); - - // Retrieve all the pending records. - std::vector allp; - if (allPendingT (allp)) - { - std::vector ::iterator i; - for (i = allp.begin (); i != allp.end (); ++i) - all.push_back (*i); - - // Retrieve all the completed records. - std::vector 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 & all) -{ - all.clear (); - - std::vector lines; - if (readLockedFile (mPendingFile, lines)) - { - mId = 1; - - int line = 1; - std::vector ::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 & all) -{ - all.clear (); - - std::vector lines; - if (readLockedFile (mPendingFile, lines)) - { - mId = 1; - - int line = 1; - std::vector ::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 & all) const -{ - all.clear (); - - std::vector lines; - if (readLockedFile (mCompletedFile, lines)) - { - int line = 1; - std::vector ::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 & all) const -{ - all.clear (); - - std::vector lines; - if (readLockedFile (mCompletedFile, lines)) - { - int line = 1; - std::vector ::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 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 all; - allPendingT (all); - - std::vector pending; - - std::vector ::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 & 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 ::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 & 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 all; - allPendingT (all); - - // A list of the truly pending tasks. - std::vector pending; - - std::vector::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; -} - -//////////////////////////////////////////////////////////////////////////////// - diff --git a/src/TDB.h b/src/TDB.h deleted file mode 100644 index e2c0257ec..000000000 --- a/src/TDB.h +++ /dev/null @@ -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 -#include -#include "T.h" - -class TDB -{ -public: - TDB (); - ~TDB (); - - void dataDirectory (const std::string&); - bool allT (std::vector &); - bool pendingT (std::vector &); - bool allPendingT (std::vector &); - bool completedT (std::vector &) const; - bool allCompletedT (std::vector &) const; - bool addT (const T&); - bool modifyT (const T&); - int gc (); - int nextId (); - - void noLock (); - -private: - bool lock (FILE*) const; - bool overwritePending (std::vector &); - bool writePending (const T&); - bool writeCompleted (const T&); - bool readLockedFile (const std::string&, std::vector &) const; - -private: - std::string mPendingFile; - std::string mCompletedFile; - int mId; - bool mNoLock; -}; - -#endif -//////////////////////////////////////////////////////////////////////////////// diff --git a/src/TDB2.cpp b/src/TDB2.cpp index 2c9357d89..43cc4688b 100644 --- a/src/TDB2.cpp +++ b/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 & 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 & 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 & 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"); } diff --git a/src/TDB2.h b/src/TDB2.h index bd099c442..04ae194da 100644 --- a/src/TDB2.h +++ b/src/TDB2.h @@ -68,6 +68,12 @@ private: bool mAllOpenAndLocked; int mId; + std::vector mPending; // Contents of pending.data +// std::vector mCompleted; // Contents of completed.data + + std::vector mNew; // Uncommitted new tasks + std::vector mModified; // Uncommitted modified tasks + // TODO Need cache of raw file contents to preserve comments. };