summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2005-02-09 05:05:24 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 02:40:54 -0400
commited000a6967be06bf9e767e7375d2a5ce85c22d94 (patch)
tree69d573b99f2bc1422011cc39abc5342bed2a8b40
parentd8f35f3152aae177bf0848f6e1e1429600730bb1 (diff)
downloadfork-ledger-ed000a6967be06bf9e767e7375d2a5ce85c22d94.tar.gz
fork-ledger-ed000a6967be06bf9e767e7375d2a5ce85c22d94.tar.bz2
fork-ledger-ed000a6967be06bf9e767e7375d2a5ce85c22d94.zip
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.
-rw-r--r--journal.cc13
-rw-r--r--journal.h29
-rw-r--r--textual.cc105
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<accounts_map::iterator, bool> 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<transaction_t *> 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<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
#ifdef USE_BOOST_PYTHON