//////////////////////////////////////////////////////////////////////////////// // 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 // TODO Remove. #include #include #include #include #include #include #include #include #include #include #include #include extern Context context; #define NDEBUG #include #include #define DEBUG_OUTPUT 0 #if DEBUG_OUTPUT > 0 #define DEBUG_STR(str) std::cout << "DEBUG: " << str << "\n"; std::cout.flush() #define DEBUG_STR_PART(str) std::cout << "DEBUG: " << str; std::cout.flush() #define DEBUG_STR_END(str) std::cout << str << "\n"; std::cout.flush() #else #define DEBUG_STR(str) #define DEBUG_STR_PART(str) #define DEBUG_STR_END(str) #endif //////////////////////////////////////////////////////////////////////////////// TF2::TF2 () : _read_only (false) , _dirty (false) , _loaded_tasks (false) , _loaded_lines (false) , _loaded_contents (false) , _contents ("") { } //////////////////////////////////////////////////////////////////////////////// TF2::~TF2 () { } //////////////////////////////////////////////////////////////////////////////// void TF2::target (const std::string& f) { _file = File (f); _read_only = ! _file.writable (); // std::cout << "# TF2::target " << f << "\n"; } //////////////////////////////////////////////////////////////////////////////// const std::vector & TF2::get_tasks () { // std::cout << "# TF2::get_tasks " << _file._data << "\n"; if (! _loaded_tasks) { load_tasks (); // Apply previously added tasks. std::vector ::iterator i; for (i = _added_tasks.begin (); i != _added_tasks.end (); ++i) _tasks.push_back (*i); // std::cout << "# TF2::get_tasks added " << _added_tasks.size () << " tasks\n"; } return _tasks; } //////////////////////////////////////////////////////////////////////////////// const std::vector & TF2::get_lines () { // std::cout << "# TF2::get_lines " << _file._data << "\n"; if (! _loaded_lines) { load_lines (); // Apply previously added lines. std::vector ::iterator i; for (i = _added_lines.begin (); i != _added_lines.end (); ++i) _lines.push_back (*i); // std::cout << "# TF2::get_lines added " << _added_lines.size () << " lines\n"; } return _lines; } //////////////////////////////////////////////////////////////////////////////// const std::string& TF2::get_contents () { // std::cout << "# TF2::get_contents " << _file._data << "\n"; if (! _loaded_contents) load_contents (); return _contents; } //////////////////////////////////////////////////////////////////////////////// void TF2::add_task (const Task& task) { // std::cout << "# TF2::add_task " << _file._data << "\n"; _tasks.push_back (task); // For subsequent queries _added_tasks.push_back (task); // For commit/synch /* int id = context.tdb2.next_id (); _I2U[id] = task.get ("uuid"); _U2I[task.get ("uuid")] = id; */ _dirty = true; } //////////////////////////////////////////////////////////////////////////////// void TF2::modify_task (const Task& task) { // std::cout << "# TF2::modify_task " << _file._data << "\n"; // Modify in-place. std::string uuid = task.get ("uuid"); std::vector ::iterator i; for (i = _tasks.begin (); i != _tasks.end (); ++i) { if (i->get ("uuid") == uuid) { // std::cout << "# TF2::modify_task overwriting:\n" // << "# old " << i->composeF4 () // << "# new " << task.composeF4 (); *i = task; break; } } _modified_tasks.push_back (task); _dirty = true; } //////////////////////////////////////////////////////////////////////////////// void TF2::add_line (const std::string& line) { // std::cout << "# TF2::add_line " << _file._data << "\n"; _lines.push_back (line); _added_lines.push_back (line); _dirty = true; } //////////////////////////////////////////////////////////////////////////////// // This is so that synch.key can just overwrite and not grow. void TF2::clear_lines () { // std::cout << "# TF2::clear_lines " << _file._data << "\n"; _lines.clear (); _dirty = true; } //////////////////////////////////////////////////////////////////////////////// // Top-down recomposition. void TF2::commit () { // std::cout << "# TF2::commit " << _file._data << "\n"; // The _dirty flag indicates that the file needs to be written. if (_dirty) { // Special case: added but no modified means just append to the file. if (!_modified_tasks.size () && (_added_tasks.size () || _added_lines.size ())) { if (_file.open ()) { if (context.config.getBoolean ("locking")) _file.lock (); // Write out all the added tasks. std::vector ::iterator task; for (task = _added_tasks.begin (); task != _added_tasks.end (); ++task) { _file.append (task->composeF4 ()); } _added_tasks.clear (); // Write out all the added lines. std::vector ::iterator line; for (line = _added_lines.begin (); line != _added_lines.end (); ++line) { _file.append (*line); } _added_lines.clear (); _file.close (); _dirty = false; } } else { if (_file.open ()) { // Truncate the file and rewrite. _file.truncate (); // only write out _tasks, because any deltas have already been applied. std::vector ::iterator task; for (task = _tasks.begin (); task != _tasks.end (); ++task) { _file.append (task->composeF4 ()); } // Write out all the added lines. std::vector ::iterator line; for (line = _added_lines.begin (); line != _added_lines.end (); ++line) { _file.append (*line); } _added_lines.clear (); _file.close (); _dirty = false; } } } } //////////////////////////////////////////////////////////////////////////////// void TF2::load_tasks () { // std::cout << "# TF2::load_tasks " << _file._data << "\n"; context.timer_load.start (); if (! _loaded_lines) { load_lines (); // Apply previously added lines. std::vector ::iterator i; for (i = _added_lines.begin (); i != _added_lines.end (); ++i) _lines.push_back (*i); // std::cout << "# TF2::load_tasks added " << _added_lines.size () << " lines\n"; } int line_number = 0; try { std::vector ::iterator i; for (i = _lines.begin (); i != _lines.end (); ++i) { ++line_number; Task task (*i); // Every task gets an ID. task.id = context.tdb2.next_id (); _tasks.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. if (task.id) { _I2U[task.id] = task.get ("uuid"); _U2I[task.get ("uuid")] = task.id; } } _loaded_tasks = true; } catch (std::string& e) { throw e + format (" in {1} at line {2}", _file._data, line_number); } context.timer_load.stop (); } //////////////////////////////////////////////////////////////////////////////// void TF2::load_lines () { // std::cout << "# TF2::load_lines " << _file._data << "\n"; if (! _loaded_contents) load_contents (); split_minimal (_lines, _contents, '\n'); _loaded_lines = true; } //////////////////////////////////////////////////////////////////////////////// void TF2::load_contents () { // std::cout << "# TF2::load_contents " << _file._data << "\n"; _contents = ""; if (_file.open ()) { if (context.config.getBoolean ("locking")) _file.lock (); _file.read (_contents); _loaded_contents = true; } // TODO Error handling? } //////////////////////////////////////////////////////////////////////////////// std::string TF2::uuid (int id) { if (! _loaded_tasks) { load_tasks (); // Apply previously added tasks. std::vector ::iterator i; for (i = _added_tasks.begin (); i != _added_tasks.end (); ++i) _tasks.push_back (*i); // std::cout << "# TF2::uuid added " << _added_tasks.size () << " tasks\n"; } std::map ::const_iterator i; if ((i = _I2U.find (id)) != _I2U.end ()) return i->second; return ""; } //////////////////////////////////////////////////////////////////////////////// int TF2::id (const std::string& uuid) { if (! _loaded_tasks) { load_tasks (); // Apply previously added tasks. std::vector ::iterator i; for (i = _added_tasks.begin (); i != _added_tasks.end (); ++i) _tasks.push_back (*i); // std::cout << "# TF2::id added " << _added_tasks.size () << " tasks\n"; } std::map ::const_iterator i; if ((i = _U2I.find (uuid)) != _U2I.end ()) return i->second; return 0; } //////////////////////////////////////////////////////////////////////////////// void TF2::clear () { _read_only = false; _dirty = false; _loaded_tasks = false; _loaded_lines = false; _loaded_contents = false; _contents = ""; _file._data = ""; _tasks.clear (); _added_tasks.clear (); _modified_tasks.clear (); _lines.clear (); _added_lines.clear (); _I2U.clear (); _U2I.clear (); } //////////////////////////////////////////////////////////////////////////////// //