From ed000a6967be06bf9e767e7375d2a5ce85c22d94 Mon Sep 17 00:00:00 2001 From: John Wiegley Date: Wed, 9 Feb 2005 05:05:24 +0000 Subject: Added support for each entry to remember: the file it came from, and the beginning and ending character positions of that entry within the file. This makes it possible to reproduce the input exactly, with only the changed entries updated. --- journal.cc | 13 ++++++-- journal.h | 29 ++++++++++++----- textual.cc | 105 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 12 deletions(-) diff --git a/journal.cc b/journal.cc index d57734b5..1af0342f 100644 --- a/journal.cc +++ b/journal.cc @@ -165,7 +165,7 @@ void entry_t::add_transaction(transaction_t * xact) bool entry_t::valid() const { - if (! date) + if (! date || ! journal) return false; if (state != UNCLEARED && state != CLEARED && state != PENDING) @@ -276,6 +276,7 @@ account_t * account_t::find_account(const std::string& name, return NULL; account = new account_t(this, first); + account->journal = journal; std::pair result = accounts.insert(accounts_pair(first, account)); @@ -338,7 +339,7 @@ std::ostream& operator<<(std::ostream& out, const account_t& account) bool account_t::valid() const { - if (depth > 256) + if (depth > 256 || ! journal) return false; for (accounts_map::const_iterator i = accounts.begin(); @@ -398,6 +399,7 @@ bool journal_t::add_entry(entry_t * entry) return false; entries.push_back(entry); + entry->journal = this; for (transactions_list::const_iterator i = entry->transactions.begin(); i != entry->transactions.end(); @@ -422,6 +424,7 @@ bool journal_t::remove_entry(entry_t * entry) return false; entries.erase(i); + entry->journal = NULL; return true; } @@ -686,6 +689,7 @@ void export_journal() .def(self == self) .def(self != self) + .def_readonly("journal", &account_t::journal) .add_property("parent", make_getter(&account_t::parent, return_internal_reference<1>())) @@ -739,6 +743,11 @@ void export_journal() .def("__getitem__", transactions_getitem, return_internal_reference<1>()) + .def_readonly("journal", &entry_base_t::journal) + .def_readonly("src_idx", &entry_base_t::src_idx) + .def_readonly("beg_pos", &entry_base_t::beg_pos) + .def_readonly("end_pos", &entry_base_t::end_pos) + .def("add_transaction", py_add_transaction) .def("remove_transaction", &entry_base_t::remove_transaction) diff --git a/journal.h b/journal.h index 75ae35fa..733540c1 100644 --- a/journal.h +++ b/journal.h @@ -74,12 +74,18 @@ class transaction_t bool valid() const; }; +class journal_t; + typedef std::list transactions_list; class entry_base_t { public: - transactions_list transactions; + journal_t * journal; + unsigned long src_idx; + std::istream::pos_type beg_pos; + std::istream::pos_type end_pos; + transactions_list transactions; entry_base_t() { DEBUG_PRINT("ledger.memory.ctors", "ctor entry_base_t"); @@ -216,14 +222,15 @@ class account_t public: typedef unsigned long ident_t; - account_t * parent; - std::string name; - std::string note; - unsigned short depth; - accounts_map accounts; + journal_t * journal; + account_t * parent; + std::string name; + std::string note; + unsigned short depth; + accounts_map accounts; - mutable void * data; - mutable ident_t ident; + mutable void * data; + mutable ident_t ident; mutable std::string _fullname; account_t(account_t * _parent = NULL, @@ -247,9 +254,11 @@ class account_t void add_account(account_t * acct) { accounts.insert(accounts_pair(acct->name, acct)); + acct->journal = journal; } bool remove_account(account_t * acct) { accounts_map::size_type n = accounts.erase(acct->name); + acct->journal = NULL; return n > 0; } @@ -325,6 +334,7 @@ class journal_t journal_t() { DEBUG_PRINT("ledger.memory.ctors", "ctor journal_t"); master = new account_t(NULL, ""); + master->journal = this; item_pool = item_pool_end = NULL; } ~journal_t(); @@ -338,9 +348,11 @@ class journal_t void add_account(account_t * acct) { master->add_account(acct); + acct->journal = this; } bool remove_account(account_t * acct) { return master->remove_account(acct); + acct->journal = NULL; } account_t * find_account(const std::string& name, bool auto_create = true) { @@ -350,6 +362,7 @@ class journal_t account_t * account = master->find_account(name, auto_create); accounts_cache.insert(accounts_pair(name, account)); + account->journal = this; return account; } account_t * find_account_re(const std::string& regexp); diff --git a/textual.cc b/textual.cc index c15b7701..339d7a59 100644 --- a/textual.cc +++ b/textual.cc @@ -7,6 +7,7 @@ #include "config.h" #include "timing.h" #include "util.h" +#include "acconf.h" #ifdef USE_BOOST_PYTHON #include "py_eval.h" #endif @@ -308,6 +309,8 @@ unsigned int textual_parser_t::parse(std::istream& in, while (in.good() && ! in.eof()) { try { + std::istream::pos_type beg_pos = in.tellg(); + in.getline(line, MAX_LINE); if (in.eof()) break; @@ -477,11 +480,15 @@ unsigned int textual_parser_t::parse(std::istream& in, auto_entry_t * ae = new auto_entry_t(skip_ws(line + 1)); if (parse_transactions(in, account_stack.front(), *ae, "automated")) { - if (ae->finalize()) + if (ae->finalize()) { journal->auto_entries.push_back(ae); - else + ae->src_idx = journal->sources.size() - 1; + ae->beg_pos = beg_pos; + ae->end_pos = in.tellg(); + } else { throw parse_error(path, linenum, "Automated entry failed to balance"); + } } break; } @@ -496,6 +503,9 @@ unsigned int textual_parser_t::parse(std::istream& in, if (pe->finalize()) { extend_entry_base(journal, *pe); journal->period_entries.push_back(pe); + pe->src_idx = journal->sources.size() - 1; + pe->beg_pos = beg_pos; + pe->end_pos = in.tellg(); } else { throw parse_error(path, linenum, "Period entry failed to balance"); } @@ -543,9 +553,13 @@ unsigned int textual_parser_t::parse(std::istream& in, default: { unsigned int first_line = linenum; + if (entry_t * entry = parse_entry(in, line, account_stack.front(), *this)) { if (journal->add_entry(entry)) { + entry->src_idx = journal->sources.size() - 1; + entry->beg_pos = beg_pos; + entry->end_pos = in.tellg(); count++; } else { delete entry; @@ -584,6 +598,93 @@ unsigned int textual_parser_t::parse(std::istream& in, return count; } +void write_textual_journal(journal_t& journal, std::string path, + item_handler& formatter, + std::ostream& out) +{ + unsigned long index = 0; + std::string found; + char buf1[PATH_MAX]; + char buf2[PATH_MAX]; + +#ifdef HAVE_REALPATH + realpath(path.c_str(), buf1); + for (strings_list::iterator i = journal.sources.begin(); + i != journal.sources.end(); + i++) { + realpath((*i).c_str(), buf2); + if (std::strcmp(buf1, buf2) == 0) { + found = *i; + break; + } + index++; + } +#else + for (strings_list::iterator i = journal.sources.begin(); + i != journal.sources.end(); + i++) { + if (path == *i) { + found = *i; + break; + } + index++; + } +#endif + + if (found.empty()) + throw error(std::string("Journal does not refer to file '") + + found + "'"); + + entries_list::iterator el = journal.entries.begin(); + auto_entries_list::iterator al = journal.auto_entries.begin(); + period_entries_list::iterator pl = journal.period_entries.begin(); + + std::istream::pos_type pos = 0; + std::istream::pos_type jump_to; + + format_t hdr_fmt(config.write_hdr_format); + + std::ifstream in(found.c_str()); + while (! in.eof()) { + entry_base_t * base = NULL; + if (el != journal.entries.end() && + pos == (*el)->beg_pos) { + hdr_fmt.format(out, details_t(**el)); + base = *el++; + } + else if (al != journal.auto_entries.end() && + pos == (*al)->beg_pos) { + out << "= " << (*al)->predicate_string << '\n'; + base = *al++; + } + else if (pl != journal.period_entries.end() && + pos == (*pl)->beg_pos) { + out << "~ " << (*pl)->period_string << '\n'; + base = *pl++; + } + + char c; + if (base) { + for (transactions_list::iterator x = base->transactions.begin(); + x != base->transactions.end(); + x++) + transaction_xdata(**x).dflags |= TRANSACTION_TO_DISPLAY; + + walk_transactions(base->transactions, formatter); + formatter.flush(); + + while (pos < base->end_pos) { + in.get(c); + pos = in.tellg(); // pos++; + } + } else { + in.get(c); + pos = in.tellg(); // pos++; + out.put(c); + } + } +} + } // namespace ledger #ifdef USE_BOOST_PYTHON -- cgit v1.2.3