added back sorting support

This commit is contained in:
John Wiegley 2004-08-05 19:12:23 -04:00
parent 1741c80fe4
commit 7e87a0a0b1
12 changed files with 200 additions and 169 deletions

View file

@ -1,5 +1,4 @@
#include "ledger.h"
#include "textual.h"
#include <vector>
#include <fstream>
@ -97,7 +96,7 @@ transaction_t * read_binary_transaction(std::istream& in, entry_t * entry)
return xact;
}
entry_t * read_binary_entry(std::istream& in, ledger_t * ledger)
entry_t * read_binary_entry(std::istream& in, journal_t * journal)
{
entry_t * entry = new entry_t;
@ -258,7 +257,7 @@ account_t * read_binary_account(std::istream& in, account_t * master = NULL)
// If all of the subaccounts will be added to a different master
// account, throw away what we've learned about the recorded
// ledger's own master account.
// journal's own master account.
if (master) {
delete acct;
@ -282,9 +281,9 @@ account_t * read_binary_account(std::istream& in, account_t * master = NULL)
return acct;
}
unsigned int read_binary_ledger(std::istream& in,
unsigned int read_binary_journal(std::istream& in,
const std::string& leader,
ledger_t * ledger,
journal_t * journal,
account_t * master)
{
ident = 0;
@ -325,7 +324,7 @@ unsigned int read_binary_ledger(std::istream& in,
in.read(buf, len);
buf[len] = '\0';
ledger->sources.push_back(buf);
journal->sources.push_back(buf);
std::time_t old_mtime;
struct stat info;
@ -335,7 +334,7 @@ unsigned int read_binary_ledger(std::istream& in,
return 0;
}
ledger->master = read_binary_account(in, master);
journal->master = read_binary_account(in, master);
unsigned long count;
in.read((char *)&count, sizeof(count));
@ -351,8 +350,8 @@ unsigned int read_binary_ledger(std::istream& in,
in.read((char *)&count, sizeof(count));
for (int i = count; --i >= 0; ) {
entry_t * entry = read_binary_entry(in, ledger);
ledger->entries.push_back(entry);
entry_t * entry = read_binary_entry(in, journal);
journal->entries.push_back(entry);
}
#ifdef DEBUG
@ -557,7 +556,7 @@ void write_binary_account(std::ostream& out, account_t * account)
#endif
}
void write_binary_ledger(std::ostream& out, ledger_t * ledger,
void write_binary_journal(std::ostream& out, journal_t * journal,
const std::string& leader)
{
out.write((char *)&binary_magic_number, sizeof(binary_magic_number));
@ -576,11 +575,11 @@ void write_binary_ledger(std::ostream& out, ledger_t * ledger,
out.write((char *)&len, sizeof(len));
out.write(leader.c_str(), len);
len = ledger->sources.size();
len = journal->sources.size();
out.write((char *)&len, sizeof(len));
for (std::list<std::string>::const_iterator i = ledger->sources.begin();
i != ledger->sources.end();
for (std::list<std::string>::const_iterator i = journal->sources.begin();
i != journal->sources.end();
i++) {
len = (*i).length();
out.write((char *)&len, sizeof(len));
@ -591,7 +590,7 @@ void write_binary_ledger(std::ostream& out, ledger_t * ledger,
out.write((char *)&info.st_mtime, sizeof(info.st_mtime));
}
write_binary_account(out, ledger->master);
write_binary_account(out, journal->master);
unsigned long count = commodity_t::commodities.size();
out.write((char *)&count, sizeof(count));
@ -601,11 +600,11 @@ void write_binary_ledger(std::ostream& out, ledger_t * ledger,
i++)
write_binary_commodity(out, (*i).second);
count = ledger->entries.size();
count = journal->entries.size();
out.write((char *)&count, sizeof(count));
for (entries_list::const_iterator i = ledger->entries.begin();
i != ledger->entries.end();
for (entries_list::const_iterator i = journal->entries.begin();
i != journal->entries.end();
i++)
write_binary_entry(out, *i);

View file

@ -1,21 +0,0 @@
#ifndef _BINARY_H
#define _BINARY_H
#include "ledger.h"
namespace ledger {
extern unsigned long binary_magic_number;
extern unsigned int read_binary_ledger(std::istream& in,
const std::string& leader,
ledger_t * journal,
account_t * master = NULL);
extern void write_binary_ledger(std::ostream& out,
ledger_t * ledger,
const std::string& leader);
} // namespace ledger
#endif // _BINARY_H

View file

@ -147,7 +147,7 @@ element_t * format_t::parse_elements(const std::string& fmt)
void format_t::format_elements(std::ostream& out,
const details_t& details) const
{
for (const element_t * elem = elements.get(); elem; elem = elem->next) {
for (const element_t * elem = elements; elem; elem = elem->next) {
if (elem->align_left)
out << std::left;
else
@ -158,7 +158,7 @@ void format_t::format_elements(std::ostream& out,
switch (elem->type) {
case element_t::STRING:
out << elem->chars;;
out << elem->chars;
break;
case element_t::VALUE_EXPR: {

View file

@ -49,13 +49,16 @@ struct element_t
struct format_t
{
std::auto_ptr<element_t> elements;
element_t * elements;
static std::auto_ptr<node_t> value_expr;
static std::auto_ptr<node_t> total_expr;
static std::auto_ptr<node_t> value_expr;
static std::auto_ptr<node_t> total_expr;
format_t(const std::string& _format) {
elements.reset(parse_elements(_format));
elements = parse_elements(_format);
}
~format_t() {
if (elements) delete elements;
}
static element_t * parse_elements(const std::string& fmt);

View file

@ -15,7 +15,7 @@ typedef std::pair<const std::string, account_t *> accounts_pair;
typedef std::map<account_t *, commodity_t *> account_comm_map;
typedef std::pair<account_t *, commodity_t *> account_comm_pair;
static ledger_t * curr_ledger;
static journal_t * curr_journal;
static account_t * curr_account;
static commodity_t * curr_account_comm;
static std::string curr_account_id;
@ -107,7 +107,7 @@ static void endElement(void *userData, const char *name)
if (std::strcmp(name, "gnc:account") == 0) {
assert(curr_account);
if (! curr_account->parent)
curr_ledger->add_account(curr_account);
curr_journal->add_account(curr_account);
accounts_by_id.insert(accounts_pair(curr_account_id, curr_account));
curr_account = NULL;
}
@ -118,7 +118,7 @@ static void endElement(void *userData, const char *name)
}
else if (std::strcmp(name, "gnc:transaction") == 0) {
assert(curr_entry);
if (! curr_ledger->add_entry(curr_entry))
if (! curr_journal->add_entry(curr_entry))
assert(0);
curr_entry = NULL;
}
@ -256,13 +256,13 @@ static void dataHandler(void *userData, const char *s, int len)
}
}
int parse_gnucash(std::istream& in, ledger_t * ledger, account_t * master)
int parse_gnucash(std::istream& in, journal_t * journal, account_t * master)
{
char buf[BUFSIZ];
count = 0;
action = NO_ACTION;
curr_ledger = ledger;
curr_journal = journal;
curr_account = NULL;
curr_entry = NULL;
curr_comm = NULL;

View file

@ -1,8 +1,6 @@
#include "ledger.h"
#include "valexpr.h"
#include "datetime.h"
#include "textual.h"
#include "binary.h"
#include <fstream>
@ -10,30 +8,7 @@ namespace ledger {
const std::string version = "2.0b";
#if 0
struct cmp_items {
const node_t * sort_order;
cmp_items(const node_t * _sort_order) : sort_order(_sort_order) {
assert(sort_order);
}
bool operator()(const item_t * left, const item_t * right) const {
assert(left);
assert(right);
return sort_order->compute(left) < sort_order->compute(right);
}
};
void item_t::sort(const node_t * sort_order)
{
std::stable_sort(subitems.begin(), subitems.end(), cmp_items(sort_order));
}
#endif
ledger_t::~ledger_t()
journal_t::~journal_t()
{
delete master;
@ -46,7 +21,7 @@ ledger_t::~ledger_t()
delete *i;
}
bool ledger_t::add_entry(entry_t * entry)
bool journal_t::add_entry(entry_t * entry)
{
entries.push_back(entry);
@ -64,7 +39,7 @@ bool ledger_t::add_entry(entry_t * entry)
return true;
}
bool ledger_t::remove_entry(entry_t * entry)
bool journal_t::remove_entry(entry_t * entry)
{
entries.remove(entry);
@ -77,7 +52,7 @@ bool ledger_t::remove_entry(entry_t * entry)
return true;
}
entry_t * ledger_t::derive_entry(int argc, char **argv) const
entry_t * journal_t::derive_entry(int argc, char **argv) const
{
entry_t * added = new entry_t;
entry_t * matching = NULL;
@ -204,7 +179,7 @@ entry_t * ledger_t::derive_entry(int argc, char **argv) const
return added;
}
int parse_ledger_file(char * p, ledger_t * journal)
int parse_journal_file(char * p, journal_t * journal)
{
char * sep = std::strrchr(p, '=');
if (sep) *sep++ = '\0';
@ -225,9 +200,9 @@ int parse_ledger_file(char * p, ledger_t * journal)
stream.seekg(start);
if (magic == binary_magic_number)
return read_binary_ledger(stream, "", journal, master);
return read_binary_journal(stream, "", journal, master);
else
return parse_textual_ledger(stream, journal, master);
return parse_textual_journal(stream, journal, master);
}
} // namespace ledger

View file

@ -133,14 +133,14 @@ class account_t
return fullname();
}
// These functions should only be called from ledger_t::add_entry
// and ledger_t::remove_entry; or from the various parsers.
// These functions should only be called from journal_t::add_entry
// and journal_t::remove_entry; or from the various parsers.
void add_transaction(transaction_t * xact) {
transactions.push_back(xact);
}
bool remove_transaction(transaction_t * xact);
friend class ledger_t;
friend class journal_t;
};
inline std::ostream& operator<<(std::ostream& out, const account_t& acct) {
@ -151,7 +151,7 @@ inline std::ostream& operator<<(std::ostream& out, const account_t& acct) {
typedef std::list<entry_t *> entries_list;
class ledger_t
class journal_t
{
public:
account_t * master;
@ -159,13 +159,13 @@ class ledger_t
mutable accounts_map accounts_cache;
std::list<std::string> sources;
ledger_t() {
journal_t() {
master = new account_t(NULL, "");
master->ident = 0;
account_t::next_ident = 1;
}
~ledger_t();
~journal_t();
void add_account(account_t * acct) {
master->add_account(acct);
@ -186,7 +186,7 @@ class ledger_t
account_t * find_account(const std::string& name) const {
// With auto_create false, the other `find_account' will not
// change the object.
return const_cast<ledger_t *>(this)->find_account(name, false);
return const_cast<journal_t *>(this)->find_account(name, false);
}
bool add_entry(entry_t * entry);
@ -195,7 +195,21 @@ class ledger_t
entry_t * derive_entry(int argc, char **argv) const;
};
int parse_ledger_file(char * p, ledger_t * journal);
int parse_journal_file(char * p, journal_t * journal);
unsigned int parse_textual_journal(std::istream& in, journal_t * ledger,
account_t * master = NULL);
extern unsigned long binary_magic_number;
unsigned int read_binary_journal(std::istream& in,
const std::string& leader,
journal_t * journal,
account_t * master = NULL);
void write_binary_journal(std::ostream& out,
journal_t * journal,
const std::string& leader);
extern const std::string version;

125
main.cc
View file

@ -1,7 +1,5 @@
#include "ledger.h"
#include "error.h"
#include "textual.h"
#include "binary.h"
#include "valexpr.h"
#include "format.h"
#include "walk.h"
@ -350,11 +348,13 @@ static void show_help(std::ostream& out)
int main(int argc, char * argv[])
{
std::auto_ptr<ledger::ledger_t> journal(new ledger::ledger_t);
std::list<std::string> files;
std::auto_ptr<ledger::node_t> predicate;
std::auto_ptr<ledger::node_t> display_predicate;
std::auto_ptr<ledger::node_t> sort_order;
using namespace ledger;
std::auto_ptr<journal_t> journal(new journal_t);
std::list<std::string> files;
std::auto_ptr<node_t> predicate;
std::auto_ptr<node_t> display_predicate;
std::auto_ptr<node_t> sort_order;
std::string predicate_string;
std::string display_predicate_string;
@ -376,10 +376,10 @@ int main(int argc, char * argv[])
// Initialize some variables based on environment variable settings
if (char * p = std::getenv("PRICE_HIST"))
ledger::price_db = p;
price_db = p;
if (char * p = std::getenv("PRICE_EXP"))
ledger::pricing_leeway = std::atol(p) * 60;
pricing_leeway = std::atol(p) * 60;
// A ledger data file must be specified
@ -392,18 +392,18 @@ int main(int argc, char * argv[])
break;
}
ledger::cache_dirty = true;
cache_dirty = true;
if (use_cache)
if (const char * p = std::getenv("LEDGER_CACHE"))
if (access(p, R_OK) != -1) {
std::ifstream instr(p);
if (! ledger::read_binary_ledger(instr, std::getenv("LEDGER"),
journal.get())) {
if (! read_binary_journal(instr, std::getenv("LEDGER"),
journal.get())) {
// Throw away what's been read, and create a new journal
journal.reset(new ledger::ledger_t);
journal.reset(new journal_t);
} else {
ledger::cache_dirty = false;
cache_dirty = false;
}
}
}
@ -428,7 +428,7 @@ int main(int argc, char * argv[])
case 'v':
std::cout
<< "Ledger " << ledger::version
<< "Ledger " << version
<< ", the command-line accounting tool" << std::endl
<< " Copyright (c) 2003-2004, New Artisans LLC. All rights reserved."
<< std::endl << std::endl
@ -445,7 +445,7 @@ int main(int argc, char * argv[])
break;
case 'p':
ledger::set_price_conversion(optarg);
set_price_conversion(optarg);
break;
case 'b':
@ -536,15 +536,15 @@ int main(int argc, char * argv[])
// Commodity reporting
case 'P':
ledger::price_db = optarg;
price_db = optarg;
break;
case 'L':
ledger::pricing_leeway = std::atol(optarg) * 60;
pricing_leeway = std::atol(optarg) * 60;
break;
case 'Q':
ledger::commodity_t::updater = ledger::download_price_quote;
commodity_t::updater = download_price_quote;
break;
case 't':
@ -566,15 +566,15 @@ int main(int argc, char * argv[])
break;
case 'V':
ledger::show_commodities_revalued = true;
show_commodities_revalued = true;
value_expr = "v";
total_expr = "V";
break;
case 'G':
ledger::show_commodities_revalued =
ledger::show_commodities_revalued_only = true;
show_commodities_revalued =
show_commodities_revalued_only = true;
value_expr = "c";
total_expr = "G";
@ -622,36 +622,36 @@ int main(int argc, char * argv[])
// Read the ledger file, unless we already read it from the cache
if (! use_cache || ledger::cache_dirty) {
if (! use_cache || cache_dirty) {
int entry_count = 0;
try {
if (files.empty()) {
if (char * p = std::getenv("LEDGER"))
for (p = std::strtok(p, ":"); p; p = std::strtok(NULL, ":"))
entry_count += parse_ledger_file(p, journal.get());
entry_count += parse_journal_file(p, journal.get());
} else {
for (std::list<std::string>::iterator i = files.begin();
i != files.end(); i++) {
char buf[4096];
char * p = buf;
std::strcpy(p, (*i).c_str());
entry_count += parse_ledger_file(p, journal.get());
entry_count += parse_journal_file(p, journal.get());
}
}
// Read prices from their own ledger file, after all others have
// been read.
if (! ledger::price_db.empty()) {
const char * path = ledger::price_db.c_str();
if (! price_db.empty()) {
const char * path = price_db.c_str();
std::ifstream db(path);
journal->sources.push_back(path);
entry_count += ledger::parse_textual_ledger(db, journal.get(),
entry_count += parse_textual_journal(db, journal.get(),
journal->master);
}
}
catch (ledger::error& err) {
catch (error& err) {
std::cerr << "Fatal: " << err.what() << std::endl;
return 1;
}
@ -685,7 +685,7 @@ int main(int argc, char * argv[])
// Process the remaining command-line arguments
std::auto_ptr<ledger::entry_t> new_entry;
std::auto_ptr<entry_t> new_entry;
if (command == "entry") {
new_entry.reset(journal->derive_entry(argc - index, &argv[index]));
} else {
@ -770,7 +770,7 @@ int main(int argc, char * argv[])
if (debug)
std::cerr << "predicate = " << predicate_string << std::endl;
#endif
predicate.reset(ledger::parse_expr(predicate_string));
predicate.reset(parse_expr(predicate_string));
}
if (display_predicate_string.empty() && command == "b" && ! show_empty)
@ -782,18 +782,18 @@ int main(int argc, char * argv[])
std::cerr << "display predicate = " << display_predicate_string
<< std::endl;
#endif
display_predicate.reset(ledger::parse_expr(display_predicate_string));
display_predicate.reset(parse_expr(display_predicate_string));
}
// Compile the sorting string
if (! sort_string.empty())
sort_order.reset(ledger::parse_expr(sort_string));
sort_order.reset(parse_expr(sort_string));
// Setup the meaning of %t and %T encountered in format strings
ledger::format_t::value_expr.reset(ledger::parse_expr(value_expr));
ledger::format_t::total_expr.reset(ledger::parse_expr(total_expr));
format_t::value_expr.reset(parse_expr(value_expr));
format_t::total_expr.reset(parse_expr(total_expr));
// Now handle the command that was identified above.
@ -812,26 +812,24 @@ int main(int argc, char * argv[])
if (! format_string.empty())
f = format_string.c_str();
else if (command == "b")
f = ledger::bal_fmt.c_str();
f = bal_fmt.c_str();
else if (command == "r")
f = ledger::reg_fmt.c_str();
f = reg_fmt.c_str();
else
f = ledger::print_fmt.c_str();
f = print_fmt.c_str();
if (command == "b") {
std::auto_ptr<ledger::format_t> format(new ledger::format_t(f));
ledger::walk_accounts(journal->master,
ledger::format_account(std::cout, *format.get()),
predicate.get(), show_related, show_inverted,
show_subtotals, display_predicate.get());
format_t format(f);
walk_accounts(journal->master, format_account(std::cout, format),
predicate.get(), show_related, show_inverted,
show_subtotals, display_predicate.get());
if (! display_predicate.get() ||
ledger::item_predicate(display_predicate.get())(journal->master)) {
item_predicate(display_predicate.get())(journal->master)) {
std::string end_format = "--------------------\n";
end_format += f;
format.get()->elements.reset(ledger::format_t::parse_elements(end_format));
ledger::format_account(std::cout, *format.get())(journal->master, true);
format.elements = format_t::parse_elements(end_format);
format_account(std::cout, format)(journal->master, true);
}
} else {
std::string first_line_format;
@ -844,27 +842,34 @@ int main(int argc, char * argv[])
first_line_format = next_lines_format = f;
}
std::auto_ptr<ledger::format_t>
format(new ledger::format_t(first_line_format));
std::auto_ptr<ledger::format_t>
nformat(new ledger::format_t(next_lines_format));
format_t format(first_line_format);
format_t nformat(next_lines_format);
format_transaction formatter(std::cout, format, nformat);
ledger::walk_entries(journal->entries.begin(), journal->entries.end(),
ledger::format_transaction(std::cout,
first_line_format,
next_lines_format),
predicate.get(), show_related, show_inverted,
display_predicate.get());
if (! sort_order.get()) {
walk_entries(journal->entries.begin(), journal->entries.end(),
formatter, predicate.get(), show_related, show_inverted,
display_predicate.get());
} else {
transactions_deque transactions_pool;
walk_entries(journal->entries.begin(), journal->entries.end(),
collect_transactions(transactions_pool), predicate.get(),
show_related, show_inverted, display_predicate.get());
std::stable_sort(transactions_pool.begin(), transactions_pool.end(),
compare_transactions(sort_order.get()));
walk_transactions(transactions_pool.begin(), transactions_pool.end(),
formatter, NULL, show_related, show_inverted,
display_predicate.get());
}
}
// Save the cache, if need be
if (use_cache && ledger::cache_dirty)
if (use_cache && cache_dirty)
if (const char * p = std::getenv("LEDGER_CACHE")) {
std::ofstream outstr(p);
assert(std::getenv("LEDGER"));
ledger::write_binary_ledger(outstr, journal.get(),
std::getenv("LEDGER"));
write_binary_journal(outstr, journal.get(), std::getenv("LEDGER"));
}
return 0;

View file

@ -1,4 +1,3 @@
#include "textual.h"
#include "datetime.h"
#include "autoxact.h"
#include "valexpr.h"
@ -287,8 +286,8 @@ entry_t * parse_entry(std::istream& in, account_t * master)
return curr;
}
unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
account_t * master)
unsigned int parse_textual_journal(std::istream& in, journal_t * journal,
account_t * master)
{
static char line[MAX_LINE + 1];
char c;
@ -518,8 +517,8 @@ unsigned int parse_textual_ledger(std::istream& in, ledger_t * journal,
unsigned int curr_linenum = linenum;
std::string curr_path = path;
count += parse_textual_ledger(stream, journal,
account_stack.front());
count += parse_textual_journal(stream, journal,
account_stack.front());
linenum = curr_linenum;
path = curr_path;

View file

@ -1,13 +0,0 @@
#ifndef _TEXTUAL_H
#define _TEXTUAL_H
#include "ledger.h"
namespace ledger {
extern unsigned int parse_textual_ledger(std::istream& in, ledger_t * ledger,
account_t * master = NULL);
} // namespace ledger
#endif // _TEXTUAL_H

View file

@ -1,7 +1,6 @@
#include "valexpr.h"
#include "error.h"
#include "datetime.h"
#include "textual.h"
#include <pcre.h>

73
walk.h
View file

@ -7,6 +7,7 @@
#include "valexpr.h"
#include <iostream>
#include <deque>
namespace ledger {
@ -84,7 +85,45 @@ class format_transaction
void operator()(transaction_t * xact,
balance_pair_t * balance,
unsigned int * index,
const bool inverted) const;
const bool inverted) const;
};
struct compare_transactions {
const node_t * sort_order;
compare_transactions(const node_t * _sort_order)
: sort_order(_sort_order) {
assert(sort_order);
}
bool operator()(const transaction_t * left,
const transaction_t * right) const {
assert(left);
assert(right);
balance_t left_result;
sort_order->compute(left_result, details_t(left));
balance_t right_result;
sort_order->compute(right_result, details_t(right));
return left_result < right_result;
}
};
typedef std::deque<transaction_t *> transactions_deque;
class collect_transactions
{
transactions_deque& transactions;
public:
collect_transactions(transactions_deque& _transactions)
: transactions(_transactions) {}
void operator()(transaction_t * xact,
balance_pair_t * balance,
unsigned int * index,
const bool inverted) {
transactions.push_back(xact);
}
};
class ignore_transaction
@ -153,6 +192,38 @@ void walk_entries(entries_list::iterator begin,
related, inverted, &balance, &index);
}
template <typename Function>
void walk_transactions(transactions_list::iterator begin,
transactions_list::iterator end,
Function functor,
const node_t * predicate,
const bool related,
const bool inverted,
const node_t * display_predicate = NULL)
{
balance_pair_t balance;
unsigned int index;
for (transactions_list::iterator i = begin; i != end; i++)
functor(*i, &balance, &index, inverted);
}
template <typename Function>
void walk_transactions(transactions_deque::iterator begin,
transactions_deque::iterator end,
Function functor,
const node_t * predicate,
const bool related,
const bool inverted,
const node_t * display_predicate = NULL)
{
balance_pair_t balance;
unsigned int index;
for (transactions_deque::iterator i = begin; i != end; i++)
functor(*i, &balance, &index, inverted);
}
class format_account
{
std::ostream& output_stream;