summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--autoxact.cc24
-rw-r--r--autoxact.h6
-rw-r--r--journal.cc121
-rw-r--r--ledger.el20
-rw-r--r--ledger.h31
-rw-r--r--main.cc3
-rw-r--r--python.cc2
-rw-r--r--textual.cc102
-rw-r--r--textual.h29
9 files changed, 191 insertions, 147 deletions
diff --git a/autoxact.cc b/autoxact.cc
index cc07ad2f..4de8122d 100644
--- a/autoxact.cc
+++ b/autoxact.cc
@@ -2,10 +2,10 @@
namespace ledger {
-void automated_transaction_t::extend_entry(entry_t * entry)
+void automated_transaction_t::extend_entry(entry_t& entry)
{
- transactions_deque initial_xacts(entry->transactions.begin(),
- entry->transactions.end());
+ transactions_deque initial_xacts(entry.transactions.begin(),
+ entry.transactions.end());
for (transactions_deque::iterator i = initial_xacts.begin();
i != initial_xacts.end();
@@ -23,13 +23,13 @@ void automated_transaction_t::extend_entry(entry_t * entry)
transaction_t * xact
= new transaction_t((*t)->account, amt,
(*t)->flags | TRANSACTION_AUTO);
- entry->add_transaction(xact);
+ entry.add_transaction(xact);
}
}
automated_transactions_t * current_auto_xacts = NULL;
-bool handle_auto_xacts(entry_t * entry)
+bool handle_auto_xacts(entry_t& entry)
{
if (current_auto_xacts &&
! current_auto_xacts->automated_transactions.empty())
@@ -39,3 +39,17 @@ bool handle_auto_xacts(entry_t * entry)
}
} // namespace ledger
+
+#ifdef USE_BOOST_PYTHON
+
+#include <boost/python.hpp>
+#include <Python.h>
+
+using namespace boost::python;
+using namespace ledger;
+
+void export_autoxact() {
+ def("handle_auto_xacts", handle_auto_xacts);
+}
+
+#endif // USE_BOOST_PYTHON
diff --git a/autoxact.h b/autoxact.h
index 301a2b26..255138a8 100644
--- a/autoxact.h
+++ b/autoxact.h
@@ -34,7 +34,7 @@ public:
delete *i;
}
- void extend_entry(entry_t * entry);
+ void extend_entry(entry_t& entry);
};
@@ -63,7 +63,7 @@ public:
delete *i;
}
- void extend_entry(entry_t * entry) {
+ void extend_entry(entry_t& entry) {
for (automated_transactions_deque::iterator i
= automated_transactions.begin();
i != automated_transactions.end();
@@ -90,7 +90,7 @@ public:
extern automated_transactions_t * current_auto_xacts;
-bool handle_auto_xacts(entry_t * entry);
+bool handle_auto_xacts(entry_t& entry);
} // namespace ledger
diff --git a/journal.cc b/journal.cc
index fee080e6..d7b852eb 100644
--- a/journal.cc
+++ b/journal.cc
@@ -120,6 +120,93 @@ bool entry_t::remove_transaction(transaction_t * xact)
return true;
}
+bool finalize_entry(entry_t& entry)
+{
+ // Scan through and compute the total balance for the entry. This
+ // is used for auto-calculating the value of entries with no cost,
+ // and the per-unit price of unpriced commodities.
+
+ value_t balance;
+
+ bool no_amounts = true;
+ for (transactions_list::const_iterator x = entry.transactions.begin();
+ x != entry.transactions.end();
+ x++)
+ if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
+ ((*x)->flags & TRANSACTION_BALANCE)) {
+ amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
+ if (*p) {
+ if (no_amounts) {
+ balance = *p;
+ no_amounts = false;
+ } else {
+ balance += *p;
+ }
+ }
+ }
+
+ // If it's a null entry, then let the user have their fun
+ if (no_amounts)
+ return true;
+
+ // If one transaction of a two-line transaction is of a different
+ // commodity than the others, and it has no per-unit price,
+ // determine its price by dividing the unit count into the value of
+ // the balance. This is done for the last eligible commodity.
+
+ if (balance.type == value_t::BALANCE &&
+ ((balance_t *) balance.data)->amounts.size() == 2)
+ for (transactions_list::const_iterator x = entry.transactions.begin();
+ x != entry.transactions.end();
+ x++) {
+ if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL))
+ continue;
+
+ for (amounts_map::const_iterator i
+ = ((balance_t *) balance.data)->amounts.begin();
+ i != ((balance_t *) balance.data)->amounts.end();
+ i++)
+ if ((*i).second.commodity() != (*x)->amount.commodity()) {
+ assert((*x)->amount);
+ balance -= (*x)->amount;
+ assert(! (*x)->cost);
+ (*x)->cost = new amount_t(- (*i).second);
+ balance += *(*x)->cost;
+ break;
+ }
+
+ break;
+ }
+
+ // Walk through each of the transactions, fixing up any that we
+ // can, and performing any on-the-fly calculations.
+
+ bool empty_allowed = true;
+
+ for (transactions_list::const_iterator x = entry.transactions.begin();
+ x != entry.transactions.end();
+ x++) {
+ if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL))
+ continue;
+
+ if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT)
+ return false;
+
+ empty_allowed = false;
+
+ // If one transaction gives no value at all -- and all the
+ // rest are of the same commodity -- then its value is the
+ // inverse of the computed value of the others.
+
+ (*x)->amount = *((amount_t *) balance.data);
+ (*x)->amount.negate();
+
+ balance = 0U;
+ }
+
+ return ! balance;
+}
+
bool entry_t::valid() const
{
if (! date || date == -1)
@@ -296,6 +383,11 @@ journal_t::~journal_t()
bool journal_t::add_entry(entry_t * entry)
{
+ if (! run_hooks(entry_finalize_hooks, *entry)) {
+ delete entry;
+ return false;
+ }
+
entries.push_back(entry);
for (transactions_list::const_iterator i = entry->transactions.begin();
@@ -439,6 +531,9 @@ entry_t * journal_t::derive_entry(strings_list::iterator i,
}
done:
+ if (! run_hooks(entry_finalize_hooks, *added))
+ return NULL;
+
return added.release();
}
@@ -590,6 +685,27 @@ account_t * py_find_account_2(journal_t& journal, const std::string& name,
return journal.find_account(name, auto_create);
}
+#if 0
+
+void py_add_entry_finalize_hook(journal_t& journal, object x)
+{
+ add_hook(journal.entry_finalize_hooks,
+ extract<journal_t::entry_finalize_hook_t>(x));
+}
+
+void py_remove_entry_finalize_hook(journal_t& journal, object x)
+{
+ remove_hook(journal.entry_finalize_hooks,
+ extract<journal_t::entry_finalize_hook_t>(x));
+}
+
+void py_run_entry_finalize_hooks(journal_t& journal, entry_t& entry)
+{
+ run_hooks(journal.entry_finalize_hooks, entry);
+}
+
+#endif
+
#define EXC_TRANSLATOR(type) \
void exc_translate_ ## type(const type& err) { \
PyErr_SetString(PyExc_RuntimeError, err.what()); \
@@ -677,6 +793,11 @@ void export_journal()
.def("add_entry", &journal_t::add_entry)
.def("remove_entry", &journal_t::remove_entry)
+#if 0
+ .def("add_entry_finalize_hook", py_add_entry_finalize_hook)
+ .def("remove_entry_finalize_hook", py_remove_entry_finalize_hook)
+ .def("run_entry_finalize_hooks", py_run_entry_finalize_hooks)
+#endif
.def("derive_entry", &journal_t::derive_entry,
return_value_policy<manage_new_object>())
diff --git a/ledger.el b/ledger.el
index 416026b0..f50fc3b8 100644
--- a/ledger.el
+++ b/ledger.el
@@ -100,11 +100,10 @@
(if (time-less-p moment date)
(throw 'found t)))))))
-(defun ledger-add-entry (entry)
+(defun ledger-add-entry (entry-text)
(interactive
- (list (read-string "Entry: " (format-time-string "%Y/%m/%d "))))
- (let* ((args (mapcar 'shell-quote-argument (split-string entry)))
- (date (car args))
+ (list (read-string "Entry: " (format-time-string "%Y/%m/"))))
+ (let* ((date (car (split-string entry-text)))
(insert-year t) exit-code)
(if (string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" date)
(setq date (encode-time 0 0 0 (string-to-int (match-string 3 date))
@@ -117,7 +116,7 @@
(save-excursion
(insert
(with-temp-buffer
- (setq exit-code (apply 'ledger-run-ledger "entry" args))
+ (setq exit-code (ledger-run-ledger "entry" entry-text))
(if (= 0 exit-code)
(progn
(goto-char (point-min))
@@ -125,8 +124,8 @@
(if insert-year
(buffer-string)
(buffer-substring 5 (point-max))))
- (concat (if insert-year entry
- (substring entry 5)) "\n"))) "\n"))))
+ (concat (if insert-year entry-text
+ (substring entry-text 5)) "\n"))) "\n"))))
(defun ledger-expand-entry ()
(interactive)
@@ -303,8 +302,11 @@
(defun ledger-run-ledger (&rest args)
"run ledger with supplied arguments"
- (apply 'call-process ledger-binary-path nil t nil
- (append (list "-f" ledger-data-file) args)))
+ (let ((command (mapconcat 'identity
+ (append (list ledger-binary-path
+ "-f" ledger-data-file) args) " ")))
+ (insert (shell-command-to-string command)))
+ 0)
(provide 'ledger)
diff --git a/ledger.h b/ledger.h
index 7be88463..22069a6d 100644
--- a/ledger.h
+++ b/ledger.h
@@ -116,6 +116,8 @@ class entry_t
bool valid() const;
};
+bool finalize_entry(entry_t& entry);
+
typedef std::map<const std::string, account_t *> accounts_map;
typedef std::pair<const std::string, account_t *> accounts_pair;
@@ -181,6 +183,30 @@ class account_t
std::ostream& operator<<(std::ostream& out, const account_t& account);
+template <typename T>
+void add_hook(std::list<T>& list, T func, const bool prepend = false) {
+ if (prepend)
+ list.push_front(func);
+ else
+ list.push_back(func);
+}
+
+template <typename T>
+void remove_hook(std::list<T>& list, T func) {
+ list.remove(func);
+}
+
+template <typename T, typename Data>
+bool run_hooks(std::list<T>& list, Data& entry) {
+ for (typename std::list<T>::const_iterator i = list.begin();
+ i != list.end();
+ i++)
+ if (! (*i)(entry))
+ return false;
+ return true;
+}
+
+
typedef std::list<entry_t *> entries_list;
typedef std::list<std::string> strings_list;
@@ -195,9 +221,14 @@ class journal_t
mutable accounts_map accounts_cache;
+ typedef bool (*entry_finalize_hook_t)(entry_t& entry);
+
+ std::list<entry_finalize_hook_t> entry_finalize_hooks;
+
journal_t() {
master = new account_t(NULL, "");
item_pool = item_pool_end = NULL;
+ add_hook(entry_finalize_hooks, finalize_entry);
}
~journal_t();
diff --git a/main.cc b/main.cc
index 639a565c..e0f6c883 100644
--- a/main.cc
+++ b/main.cc
@@ -164,6 +164,8 @@ int parse_and_report(int argc, char * argv[], char * envp[])
{
std::auto_ptr<journal_t> journal(new journal_t);
+ add_hook(journal->entry_finalize_hooks, handle_auto_xacts);
+
// Parse command-line arguments, and those set in the environment
TIMER_START(process_opts);
@@ -224,7 +226,6 @@ int parse_and_report(int argc, char * argv[], char * envp[])
#endif
std::auto_ptr<qif_parser_t> qif_parser(new qif_parser_t);
std::auto_ptr<textual_parser_t> text_parser(new textual_parser_t);
- text_parser->add_finalize_hook(handle_auto_xacts);
register_parser(bin_parser.get());
#ifdef READ_GNUCASH
diff --git a/python.cc b/python.cc
index b5d7d493..a7abb1ec 100644
--- a/python.cc
+++ b/python.cc
@@ -28,6 +28,7 @@ void export_walk();
void export_format();
void export_valexpr();
void export_datetime();
+void export_autoxact();
void initialize_ledger_for_python()
{
@@ -48,6 +49,7 @@ void initialize_ledger_for_python()
export_format();
export_valexpr();
export_datetime();
+ export_autoxact();
module_initialized = true;
}
diff --git a/textual.cc b/textual.cc
index ef646661..18eef1e0 100644
--- a/textual.cc
+++ b/textual.cc
@@ -145,93 +145,6 @@ void parse_automated_transactions(std::istream& in,
add_automated_transaction(new automated_transaction_t(line + 1, xacts));
}
-bool finalize_entry(entry_t * entry)
-{
- // Scan through and compute the total balance for the entry. This
- // is used for auto-calculating the value of entries with no cost,
- // and the per-unit price of unpriced commodities.
-
- value_t balance;
-
- bool no_amounts = true;
- for (transactions_list::const_iterator x = entry->transactions.begin();
- x != entry->transactions.end();
- x++)
- if (! ((*x)->flags & TRANSACTION_VIRTUAL) ||
- ((*x)->flags & TRANSACTION_BALANCE)) {
- amount_t * p = (*x)->cost ? (*x)->cost : &(*x)->amount;
- if (*p) {
- if (no_amounts) {
- balance = *p;
- no_amounts = false;
- } else {
- balance += *p;
- }
- }
- }
-
- // If it's a null entry, then let the user have their fun
- if (no_amounts)
- return true;
-
- // If one transaction of a two-line transaction is of a different
- // commodity than the others, and it has no per-unit price,
- // determine its price by dividing the unit count into the value of
- // the balance. This is done for the last eligible commodity.
-
- if (balance.type == value_t::BALANCE &&
- ((balance_t *) balance.data)->amounts.size() == 2)
- for (transactions_list::const_iterator x = entry->transactions.begin();
- x != entry->transactions.end();
- x++) {
- if ((*x)->cost || ((*x)->flags & TRANSACTION_VIRTUAL))
- continue;
-
- for (amounts_map::const_iterator i
- = ((balance_t *) balance.data)->amounts.begin();
- i != ((balance_t *) balance.data)->amounts.end();
- i++)
- if ((*i).second.commodity() != (*x)->amount.commodity()) {
- assert((*x)->amount);
- balance -= (*x)->amount;
- assert(! (*x)->cost);
- (*x)->cost = new amount_t(- (*i).second);
- balance += *(*x)->cost;
- break;
- }
-
- break;
- }
-
- // Walk through each of the transactions, fixing up any that we
- // can, and performing any on-the-fly calculations.
-
- bool empty_allowed = true;
-
- for (transactions_list::const_iterator x = entry->transactions.begin();
- x != entry->transactions.end();
- x++) {
- if ((*x)->amount || ((*x)->flags & TRANSACTION_VIRTUAL))
- continue;
-
- if (! empty_allowed || ! balance || balance.type != value_t::AMOUNT)
- return false;
-
- empty_allowed = false;
-
- // If one transaction gives no value at all -- and all the
- // rest are of the same commodity -- then its value is the
- // inverse of the computed value of the others.
-
- (*x)->amount = *((amount_t *) balance.data);
- (*x)->amount.negate();
-
- balance = 0U;
- }
-
- return ! balance;
-}
-
namespace {
TIMER_DEF(entry_finish, "finalizing entry");
TIMER_DEF(entry_xacts, "parsing transactions");
@@ -294,17 +207,6 @@ entry_t * parse_entry(std::istream& in, account_t * master,
TIMER_STOP(entry_xacts);
- // If there were no transactions, throw away the entry
-
- TIMER_START(entry_finish);
-
- if (curr->transactions.empty() ||
- ! parser.run_finalize_hooks(curr.get())) {
- return NULL; // ~auto_ptr will delete curr
- }
-
- TIMER_STOP(entry_finish);
-
return curr.release();
}
@@ -423,10 +325,10 @@ unsigned int textual_parser_t::parse(std::istream& in,
= new transaction_t(last_account, amt, TRANSACTION_VIRTUAL);
curr->add_transaction(xact);
- if (! run_finalize_hooks(curr.get()) ||
- ! journal->add_entry(curr.release()))
+ if (! journal->add_entry(curr.release()))
throw parse_error(path, linenum,
"Failed to record 'out' timelog entry");
+
count++;
} else {
throw parse_error(path, linenum, "Cannot parse timelog entry date");
diff --git a/textual.h b/textual.h
index a58153da..5fe17dd8 100644
--- a/textual.h
+++ b/textual.h
@@ -5,42 +5,13 @@
namespace ledger {
-bool finalize_entry(entry_t * entry);
-
class textual_parser_t : public parser_t
{
public:
- typedef bool (*finalize_hook_t)(entry_t * entry);
-
- std::list<finalize_hook_t> finalize_hooks;
-
- textual_parser_t() {
- add_finalize_hook(finalize_entry);
- }
-
virtual bool test(std::istream& in) const {
return true;
}
- void add_finalize_hook(finalize_hook_t func, bool prepend = false) {
- if (prepend)
- finalize_hooks.push_front(func);
- else
- finalize_hooks.push_back(func);
- }
- void remove_finalize_hook(finalize_hook_t func) {
- finalize_hooks.remove(func);
- }
- bool run_finalize_hooks(entry_t * entry) {
- for (std::list<finalize_hook_t>::const_iterator i
- = finalize_hooks.begin();
- i != finalize_hooks.end();
- i++)
- if (! (*i)(entry))
- return false;
- return true;
- }
-
virtual unsigned int parse(std::istream& in,
journal_t * journal,
account_t * master = NULL,