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.
This commit is contained in:
John Wiegley 2005-02-09 05:05:24 +00:00
parent d8f35f3152
commit ed000a6967
3 changed files with 135 additions and 12 deletions

View file

@ -165,7 +165,7 @@ void entry_t::add_transaction(transaction_t * xact)
bool entry_t::valid() const bool entry_t::valid() const
{ {
if (! date) if (! date || ! journal)
return false; return false;
if (state != UNCLEARED && state != CLEARED && state != PENDING) if (state != UNCLEARED && state != CLEARED && state != PENDING)
@ -276,6 +276,7 @@ account_t * account_t::find_account(const std::string& name,
return NULL; return NULL;
account = new account_t(this, first); account = new account_t(this, first);
account->journal = journal;
std::pair<accounts_map::iterator, bool> result std::pair<accounts_map::iterator, bool> result
= accounts.insert(accounts_pair(first, account)); = 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 bool account_t::valid() const
{ {
if (depth > 256) if (depth > 256 || ! journal)
return false; return false;
for (accounts_map::const_iterator i = accounts.begin(); for (accounts_map::const_iterator i = accounts.begin();
@ -398,6 +399,7 @@ bool journal_t::add_entry(entry_t * entry)
return false; return false;
entries.push_back(entry); entries.push_back(entry);
entry->journal = this;
for (transactions_list::const_iterator i = entry->transactions.begin(); for (transactions_list::const_iterator i = entry->transactions.begin();
i != entry->transactions.end(); i != entry->transactions.end();
@ -422,6 +424,7 @@ bool journal_t::remove_entry(entry_t * entry)
return false; return false;
entries.erase(i); entries.erase(i);
entry->journal = NULL;
return true; return true;
} }
@ -686,6 +689,7 @@ void export_journal()
.def(self == self) .def(self == self)
.def(self != self) .def(self != self)
.def_readonly("journal", &account_t::journal)
.add_property("parent", .add_property("parent",
make_getter(&account_t::parent, make_getter(&account_t::parent,
return_internal_reference<1>())) return_internal_reference<1>()))
@ -739,6 +743,11 @@ void export_journal()
.def("__getitem__", transactions_getitem, .def("__getitem__", transactions_getitem,
return_internal_reference<1>()) 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("add_transaction", py_add_transaction)
.def("remove_transaction", &entry_base_t::remove_transaction) .def("remove_transaction", &entry_base_t::remove_transaction)

View file

@ -74,11 +74,17 @@ class transaction_t
bool valid() const; bool valid() const;
}; };
class journal_t;
typedef std::list<transaction_t *> transactions_list; typedef std::list<transaction_t *> transactions_list;
class entry_base_t class entry_base_t
{ {
public: public:
journal_t * journal;
unsigned long src_idx;
std::istream::pos_type beg_pos;
std::istream::pos_type end_pos;
transactions_list transactions; transactions_list transactions;
entry_base_t() { entry_base_t() {
@ -216,6 +222,7 @@ class account_t
public: public:
typedef unsigned long ident_t; typedef unsigned long ident_t;
journal_t * journal;
account_t * parent; account_t * parent;
std::string name; std::string name;
std::string note; std::string note;
@ -247,9 +254,11 @@ class account_t
void add_account(account_t * acct) { void add_account(account_t * acct) {
accounts.insert(accounts_pair(acct->name, acct)); accounts.insert(accounts_pair(acct->name, acct));
acct->journal = journal;
} }
bool remove_account(account_t * acct) { bool remove_account(account_t * acct) {
accounts_map::size_type n = accounts.erase(acct->name); accounts_map::size_type n = accounts.erase(acct->name);
acct->journal = NULL;
return n > 0; return n > 0;
} }
@ -325,6 +334,7 @@ class journal_t
journal_t() { journal_t() {
DEBUG_PRINT("ledger.memory.ctors", "ctor journal_t"); DEBUG_PRINT("ledger.memory.ctors", "ctor journal_t");
master = new account_t(NULL, ""); master = new account_t(NULL, "");
master->journal = this;
item_pool = item_pool_end = NULL; item_pool = item_pool_end = NULL;
} }
~journal_t(); ~journal_t();
@ -338,9 +348,11 @@ class journal_t
void add_account(account_t * acct) { void add_account(account_t * acct) {
master->add_account(acct); master->add_account(acct);
acct->journal = this;
} }
bool remove_account(account_t * acct) { bool remove_account(account_t * acct) {
return master->remove_account(acct); return master->remove_account(acct);
acct->journal = NULL;
} }
account_t * find_account(const std::string& name, bool auto_create = true) { 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); account_t * account = master->find_account(name, auto_create);
accounts_cache.insert(accounts_pair(name, account)); accounts_cache.insert(accounts_pair(name, account));
account->journal = this;
return account; return account;
} }
account_t * find_account_re(const std::string& regexp); account_t * find_account_re(const std::string& regexp);

View file

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "timing.h" #include "timing.h"
#include "util.h" #include "util.h"
#include "acconf.h"
#ifdef USE_BOOST_PYTHON #ifdef USE_BOOST_PYTHON
#include "py_eval.h" #include "py_eval.h"
#endif #endif
@ -308,6 +309,8 @@ unsigned int textual_parser_t::parse(std::istream& in,
while (in.good() && ! in.eof()) { while (in.good() && ! in.eof()) {
try { try {
std::istream::pos_type beg_pos = in.tellg();
in.getline(line, MAX_LINE); in.getline(line, MAX_LINE);
if (in.eof()) if (in.eof())
break; break;
@ -477,12 +480,16 @@ unsigned int textual_parser_t::parse(std::istream& in,
auto_entry_t * ae = new auto_entry_t(skip_ws(line + 1)); auto_entry_t * ae = new auto_entry_t(skip_ws(line + 1));
if (parse_transactions(in, account_stack.front(), *ae, "automated")) { if (parse_transactions(in, account_stack.front(), *ae, "automated")) {
if (ae->finalize()) if (ae->finalize()) {
journal->auto_entries.push_back(ae); 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, throw parse_error(path, linenum,
"Automated entry failed to balance"); "Automated entry failed to balance");
} }
}
break; break;
} }
@ -496,6 +503,9 @@ unsigned int textual_parser_t::parse(std::istream& in,
if (pe->finalize()) { if (pe->finalize()) {
extend_entry_base(journal, *pe); extend_entry_base(journal, *pe);
journal->period_entries.push_back(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 { } else {
throw parse_error(path, linenum, "Period entry failed to balance"); throw parse_error(path, linenum, "Period entry failed to balance");
} }
@ -543,9 +553,13 @@ unsigned int textual_parser_t::parse(std::istream& in,
default: { default: {
unsigned int first_line = linenum; unsigned int first_line = linenum;
if (entry_t * entry = parse_entry(in, line, account_stack.front(), if (entry_t * entry = parse_entry(in, line, account_stack.front(),
*this)) { *this)) {
if (journal->add_entry(entry)) { if (journal->add_entry(entry)) {
entry->src_idx = journal->sources.size() - 1;
entry->beg_pos = beg_pos;
entry->end_pos = in.tellg();
count++; count++;
} else { } else {
delete entry; delete entry;
@ -584,6 +598,93 @@ unsigned int textual_parser_t::parse(std::istream& in,
return count; return count;
} }
void write_textual_journal(journal_t& journal, std::string path,
item_handler<transaction_t>& 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 } // namespace ledger
#ifdef USE_BOOST_PYTHON #ifdef USE_BOOST_PYTHON