Made separate modules for the csv command, since the prior method was
not fully correct.
This commit is contained in:
parent
db0ef2e257
commit
04dfda2282
14 changed files with 200 additions and 61 deletions
|
|
@ -18,6 +18,7 @@ libledger_la_CXXFLAGS =
|
||||||
libledger_la_SOURCES = \
|
libledger_la_SOURCES = \
|
||||||
binary.cc \
|
binary.cc \
|
||||||
config.cc \
|
config.cc \
|
||||||
|
csv.cc \
|
||||||
derive.cc \
|
derive.cc \
|
||||||
emacs.cc \
|
emacs.cc \
|
||||||
format.cc \
|
format.cc \
|
||||||
|
|
@ -63,6 +64,7 @@ pkginclude_HEADERS = \
|
||||||
\
|
\
|
||||||
binary.h \
|
binary.h \
|
||||||
config.h \
|
config.h \
|
||||||
|
csv.h \
|
||||||
derive.h \
|
derive.h \
|
||||||
emacs.h \
|
emacs.h \
|
||||||
error.h \
|
error.h \
|
||||||
|
|
@ -151,11 +153,11 @@ endif
|
||||||
alltests.cc: $(TESTSUITES)
|
alltests.cc: $(TESTSUITES)
|
||||||
test -f $(TESTGEN) && python $(TESTGEN) -o $@ --error-printer $(TESTSUITES)
|
test -f $(TESTGEN) && python $(TESTGEN) -o $@ --error-printer $(TESTSUITES)
|
||||||
|
|
||||||
alltests: alltests.cc
|
alltests: alltests.cc ledger
|
||||||
$(CXXCOMPILE) -I$(CXXTEST_DIR) -lexpat -lgmp -lpcre -o $@ \
|
$(CXXCOMPILE) -I$(CXXTEST_DIR) -lexpat -lgmp -lpcre -o $@ \
|
||||||
alltests.cc -L. -L.libs -lamounts -lledger
|
alltests.cc -L. -L.libs -lamounts -lledger
|
||||||
|
|
||||||
runtests: alltests ledger
|
runtests: alltests
|
||||||
./alltests && tests/regress && tests/regtest
|
./alltests && tests/regress && tests/regtest
|
||||||
|
|
||||||
verify: runtests
|
verify: runtests
|
||||||
|
|
|
||||||
4
acprep
4
acprep
|
|
@ -19,9 +19,9 @@ autoconf
|
||||||
|
|
||||||
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml"
|
INCDIRS="-I/sw/include -I/usr/local/include/boost-1_33 -I/usr/include/httpd/xml"
|
||||||
#INCDIRS="$INCDIRS -I/sw/include/libofx"
|
#INCDIRS="$INCDIRS -I/sw/include/libofx"
|
||||||
INCDIRS="$INCDIRS -I/sw/include/python2.4"
|
INCDIRS="$INCDIRS -I/usr/include/python2.3"
|
||||||
INCDIRS="$INCDIRS -Wno-long-double"
|
INCDIRS="$INCDIRS -Wno-long-double"
|
||||||
LIBDIRS="-L/sw/lib -L/usr/local/lib -L/sw/lib/python2.4/config"
|
LIBDIRS="-L/sw/lib -L/usr/local/lib -L/usr/lib/python2.3/config"
|
||||||
|
|
||||||
if [ "$1" = "--debug" ]; then
|
if [ "$1" = "--debug" ]; then
|
||||||
shift 1
|
shift 1
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,6 @@ config_t::config_t()
|
||||||
"%32|%-.22A %12.67t %!12.80T\n");
|
"%32|%-.22A %12.67t %!12.80T\n");
|
||||||
wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
|
wide_register_format = ("%D %-.35P %-.38A %22.108t %!22.132T\n%/"
|
||||||
"%48|%-.38A %22.108t %!22.132T\n");
|
"%48|%-.38A %22.108t %!22.132T\n");
|
||||||
csv_register_format = "\"%D\",\"%P\",\"%A\",\"%t\",\"%T\"\n";
|
|
||||||
plot_amount_format = "%D %(@S(@t))\n";
|
plot_amount_format = "%D %(@S(@t))\n";
|
||||||
plot_total_format = "%D %(@S(@T))\n";
|
plot_total_format = "%D %(@S(@T))\n";
|
||||||
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
|
print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n";
|
||||||
|
|
|
||||||
1
config.h
1
config.h
|
|
@ -21,7 +21,6 @@ class config_t
|
||||||
std::string balance_format;
|
std::string balance_format;
|
||||||
std::string register_format;
|
std::string register_format;
|
||||||
std::string wide_register_format;
|
std::string wide_register_format;
|
||||||
std::string csv_register_format;
|
|
||||||
std::string plot_amount_format;
|
std::string plot_amount_format;
|
||||||
std::string plot_total_format;
|
std::string plot_total_format;
|
||||||
std::string print_format;
|
std::string print_format;
|
||||||
|
|
|
||||||
105
csv.cc
Normal file
105
csv.cc
Normal file
|
|
@ -0,0 +1,105 @@
|
||||||
|
#include "csv.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
inline void write_escaped_string(std::ostream& out, const std::string& xact)
|
||||||
|
{
|
||||||
|
out << "\"";
|
||||||
|
for (std::string::const_iterator i = xact.begin(); i != xact.end(); i++)
|
||||||
|
if (*i == '"') {
|
||||||
|
out << "\\";
|
||||||
|
out << "\"";
|
||||||
|
} else {
|
||||||
|
out << *i;
|
||||||
|
}
|
||||||
|
out << "\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_csv_transactions::operator()(transaction_t& xact)
|
||||||
|
{
|
||||||
|
if (! transaction_has_xdata(xact) ||
|
||||||
|
! (transaction_xdata_(xact).dflags & TRANSACTION_DISPLAYED)) {
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%D");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%P");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%A");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%t");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%T");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
switch (xact.state) {
|
||||||
|
case transaction_t::CLEARED:
|
||||||
|
write_escaped_string(out, "*");
|
||||||
|
break;
|
||||||
|
case transaction_t::PENDING:
|
||||||
|
write_escaped_string(out, "!");
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
transaction_t::state_t state;
|
||||||
|
if (xact.entry->get_state(&state))
|
||||||
|
switch (state) {
|
||||||
|
case transaction_t::CLEARED:
|
||||||
|
write_escaped_string(out, "*");
|
||||||
|
break;
|
||||||
|
case transaction_t::PENDING:
|
||||||
|
write_escaped_string(out, "!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
write_escaped_string(out, "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
write_escaped_string(out, xact.entry->code);
|
||||||
|
out << ',';
|
||||||
|
|
||||||
|
{
|
||||||
|
format_t fmt("%N");
|
||||||
|
std::ostringstream str;
|
||||||
|
fmt.format(str, details_t(xact));
|
||||||
|
write_escaped_string(out, str.str());
|
||||||
|
}
|
||||||
|
out << '\n';
|
||||||
|
|
||||||
|
transaction_xdata(xact).dflags |= TRANSACTION_DISPLAYED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
24
csv.h
Normal file
24
csv.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef _CSV_H
|
||||||
|
#define _CSV_H
|
||||||
|
|
||||||
|
#include "journal.h"
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
namespace ledger {
|
||||||
|
|
||||||
|
class format_csv_transactions : public item_handler<transaction_t>
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
std::ostream& out;
|
||||||
|
|
||||||
|
public:
|
||||||
|
format_csv_transactions(std::ostream& _out) : out(_out) {}
|
||||||
|
virtual void flush() {
|
||||||
|
out.flush();
|
||||||
|
}
|
||||||
|
virtual void operator()(transaction_t& xact);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ledger
|
||||||
|
|
||||||
|
#endif // _REPORT_H
|
||||||
25
format.cc
25
format.cc
|
|
@ -300,27 +300,6 @@ element_t * format_t::parse_elements(const std::string& fmt)
|
||||||
return result.release();
|
return result.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool entry_state(const entry_t * entry, transaction_t::state_t * state)
|
|
||||||
{
|
|
||||||
bool first = true;
|
|
||||||
bool hetero = false;
|
|
||||||
|
|
||||||
for (transactions_list::const_iterator i = entry->transactions.begin();
|
|
||||||
i != entry->transactions.end();
|
|
||||||
i++) {
|
|
||||||
if (first) {
|
|
||||||
*state = (*i)->state;
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
else if (*state != (*i)->state) {
|
|
||||||
hetero = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ! hetero;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
inline void mark_red(std::ostream& out, const element_t * elem) {
|
inline void mark_red(std::ostream& out, const element_t * elem) {
|
||||||
out.setf(std::ios::left);
|
out.setf(std::ios::left);
|
||||||
|
|
@ -643,7 +622,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
case element_t::ENTRY_CLEARED:
|
case element_t::ENTRY_CLEARED:
|
||||||
if (details.entry) {
|
if (details.entry) {
|
||||||
transaction_t::state_t state;
|
transaction_t::state_t state;
|
||||||
if (entry_state(details.entry, &state))
|
if (details.entry->get_state(&state))
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case transaction_t::CLEARED:
|
case transaction_t::CLEARED:
|
||||||
out << "* ";
|
out << "* ";
|
||||||
|
|
@ -688,7 +667,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const
|
||||||
case element_t::OPT_ACCOUNT:
|
case element_t::OPT_ACCOUNT:
|
||||||
if (details.entry && details.xact) {
|
if (details.entry && details.xact) {
|
||||||
transaction_t::state_t state;
|
transaction_t::state_t state;
|
||||||
if (! entry_state(details.entry, &state))
|
if (! details.entry->get_state(&state))
|
||||||
switch (details.xact->state) {
|
switch (details.xact->state) {
|
||||||
case transaction_t::CLEARED:
|
case transaction_t::CLEARED:
|
||||||
name = "* ";
|
name = "* ";
|
||||||
|
|
|
||||||
21
journal.cc
21
journal.cc
|
|
@ -281,6 +281,27 @@ entry_t::entry_t(const entry_t& e)
|
||||||
(*i)->entry = this;
|
(*i)->entry = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool entry_t::get_state(transaction_t::state_t * state) const
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
bool hetero = false;
|
||||||
|
|
||||||
|
for (transactions_list::const_iterator i = transactions.begin();
|
||||||
|
i != transactions.end();
|
||||||
|
i++) {
|
||||||
|
if (first) {
|
||||||
|
*state = (*i)->state;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
else if (*state != (*i)->state) {
|
||||||
|
hetero = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ! hetero;
|
||||||
|
}
|
||||||
|
|
||||||
void entry_t::add_transaction(transaction_t * xact)
|
void entry_t::add_transaction(transaction_t * xact)
|
||||||
{
|
{
|
||||||
xact->entry = this;
|
xact->entry = this;
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,8 @@ class entry_t : public entry_base_t
|
||||||
virtual void add_transaction(transaction_t * xact);
|
virtual void add_transaction(transaction_t * xact);
|
||||||
|
|
||||||
virtual bool valid() const;
|
virtual bool valid() const;
|
||||||
|
|
||||||
|
bool get_state(transaction_t::state_t * state) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct entry_finalizer_t {
|
struct entry_finalizer_t {
|
||||||
|
|
|
||||||
1
ledger.h
1
ledger.h
|
|
@ -19,6 +19,7 @@
|
||||||
#include <datetime.h>
|
#include <datetime.h>
|
||||||
#include <format.h>
|
#include <format.h>
|
||||||
#include <emacs.h>
|
#include <emacs.h>
|
||||||
|
#include <csv.h>
|
||||||
#include <quotes.h>
|
#include <quotes.h>
|
||||||
#include <valexpr.h>
|
#include <valexpr.h>
|
||||||
#include <walk.h>
|
#include <walk.h>
|
||||||
|
|
|
||||||
8
main.cc
8
main.cc
|
|
@ -115,10 +115,8 @@ int parse_and_report(config_t& config, report_t& report,
|
||||||
command = "P";
|
command = "P";
|
||||||
else if (command == "pricesdb")
|
else if (command == "pricesdb")
|
||||||
command = "D";
|
command = "D";
|
||||||
else if (command == "csv") {
|
else if (command == "csv")
|
||||||
config.register_format = config.csv_register_format;
|
command = "c";
|
||||||
command = "r";
|
|
||||||
}
|
|
||||||
else if (command == "parse") {
|
else if (command == "parse") {
|
||||||
value_expr expr(ledger::parse_value_expr(*arg));
|
value_expr expr(ledger::parse_value_expr(*arg));
|
||||||
|
|
||||||
|
|
@ -326,6 +324,8 @@ appending the output of this command to your Ledger file if you so choose."
|
||||||
formatter = new format_emacs_transactions(*out);
|
formatter = new format_emacs_transactions(*out);
|
||||||
else if (command == "X")
|
else if (command == "X")
|
||||||
formatter = new format_xml_entries(*out, report.show_totals);
|
formatter = new format_xml_entries(*out, report.show_totals);
|
||||||
|
else if (command == "c")
|
||||||
|
formatter = new format_csv_transactions(*out);
|
||||||
else
|
else
|
||||||
formatter = new format_transactions(*out, *format);
|
formatter = new format_transactions(*out, *format);
|
||||||
|
|
||||||
|
|
|
||||||
46
option.cc
46
option.cc
|
|
@ -86,47 +86,56 @@ void process_arguments(option_t * options, int argc, char ** argv,
|
||||||
|
|
||||||
// --long-option or -s
|
// --long-option or -s
|
||||||
again:
|
again:
|
||||||
option_t * opt = NULL;
|
|
||||||
char * value = NULL;
|
|
||||||
|
|
||||||
if ((*i)[1] == '-') {
|
if ((*i)[1] == '-') {
|
||||||
if ((*i)[2] == '\0')
|
if ((*i)[2] == '\0')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
char * name = *i + 2;
|
char * name = *i + 2;
|
||||||
|
char * value = NULL;
|
||||||
if (char * p = std::strchr(name, '=')) {
|
if (char * p = std::strchr(name, '=')) {
|
||||||
*p++ = '\0';
|
*p++ = '\0';
|
||||||
value = p;
|
value = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
opt = search_options(options, name);
|
option_t * opt = search_options(options, name);
|
||||||
if (! opt)
|
if (! opt)
|
||||||
throw new option_error(std::string("illegal option --") + name);
|
throw new option_error(std::string("illegal option --") + name);
|
||||||
|
|
||||||
if (opt->wants_arg && ! value) {
|
if (opt->wants_arg && value == NULL) {
|
||||||
value = *++i;
|
value = *++i;
|
||||||
if (! value)
|
if (value == NULL)
|
||||||
throw new option_error(std::string("missing option argument for --") +
|
throw new option_error(std::string("missing option argument for --") +
|
||||||
name);
|
name);
|
||||||
}
|
}
|
||||||
process_option(opt, value);
|
process_option(opt, value);
|
||||||
} else {
|
}
|
||||||
char c = (*i)[1];
|
else if ((*i)[1] == '\0') {
|
||||||
opt = search_options(options, c);
|
throw new option_error(std::string("illegal option -"));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
std::list<option_t *> opt_queue;
|
||||||
|
|
||||||
|
int x = 1;
|
||||||
|
for (char c = (*i)[x]; c != '\0'; x++, c = (*i)[x]) {
|
||||||
|
option_t * opt = search_options(options, c);
|
||||||
if (! opt)
|
if (! opt)
|
||||||
throw new option_error(std::string("illegal option -") + c);
|
throw new option_error(std::string("illegal option -") + c);
|
||||||
|
opt_queue.push_back(opt);
|
||||||
|
}
|
||||||
|
|
||||||
if (opt->wants_arg) {
|
for (std::list<option_t *>::iterator o = opt_queue.begin();
|
||||||
|
o != opt_queue.end(); o++) {
|
||||||
|
char * value = NULL;
|
||||||
|
if ((*o)->wants_arg) {
|
||||||
value = *++i;
|
value = *++i;
|
||||||
if (! value)
|
if (value == NULL)
|
||||||
throw new option_error(std::string("missing option argument for -") + c);
|
throw new option_error(std::string("missing option argument for -") +
|
||||||
|
(*o)->short_opt);
|
||||||
|
}
|
||||||
|
process_option(*o, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(opt);
|
|
||||||
assert(! value || opt->wants_arg);
|
|
||||||
process_option(opt, value);
|
|
||||||
|
|
||||||
next:
|
next:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
@ -562,10 +571,6 @@ OPT_BEGIN(wide_register_format, ":") {
|
||||||
config->wide_register_format = optarg;
|
config->wide_register_format = optarg;
|
||||||
} OPT_END(wide_register_format);
|
} OPT_END(wide_register_format);
|
||||||
|
|
||||||
OPT_BEGIN(csv_register_format, ":") {
|
|
||||||
config->csv_register_format = optarg;
|
|
||||||
} OPT_END(csv_register_format);
|
|
||||||
|
|
||||||
OPT_BEGIN(plot_amount_format, ":") {
|
OPT_BEGIN(plot_amount_format, ":") {
|
||||||
config->plot_amount_format = optarg;
|
config->plot_amount_format = optarg;
|
||||||
} OPT_END(plot_amount_format);
|
} OPT_END(plot_amount_format);
|
||||||
|
|
@ -977,7 +982,6 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = {
|
||||||
{ "collapse", 'n', false, opt_collapse, false },
|
{ "collapse", 'n', false, opt_collapse, false },
|
||||||
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false },
|
{ "comm-as-payee", 'x', false, opt_comm_as_payee, false },
|
||||||
{ "cost", '\0', false, opt_basis, false },
|
{ "cost", '\0', false, opt_basis, false },
|
||||||
{ "csv-register-format", '\0', true, opt_csv_register_format, false },
|
|
||||||
{ "current", 'c', false, opt_current, false },
|
{ "current", 'c', false, opt_current, false },
|
||||||
{ "daily", '\0', false, opt_daily, false },
|
{ "daily", '\0', false, opt_daily, false },
|
||||||
{ "date-format", 'y', true, opt_date_format, false },
|
{ "date-format", 'y', true, opt_date_format, false },
|
||||||
|
|
|
||||||
2
option.h
2
option.h
|
|
@ -38,7 +38,7 @@ class report_t;
|
||||||
extern config_t * config;
|
extern config_t * config;
|
||||||
extern report_t * report;
|
extern report_t * report;
|
||||||
|
|
||||||
#define CONFIG_OPTIONS_SIZE 98
|
#define CONFIG_OPTIONS_SIZE 97
|
||||||
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
extern option_t config_options[CONFIG_OPTIONS_SIZE];
|
||||||
|
|
||||||
void option_help(std::ostream& out);
|
void option_help(std::ostream& out);
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,11 @@ errors = 0
|
||||||
report = sys.argv[1]
|
report = sys.argv[1]
|
||||||
for line in os.popen("./ledger -f utils/standard.dat -e 2004/4 %s reg %s" %
|
for line in os.popen("./ledger -f utils/standard.dat -e 2004/4 %s reg %s" %
|
||||||
(report, sys.argv[2])):
|
(report, sys.argv[2])):
|
||||||
value = clean(line[55:67])
|
match = re.match("\\s*([-$,0-9.]+)\\s+([-$,0-9.]+)", line[55:])
|
||||||
total = clean(line[68:])
|
if not match:
|
||||||
|
continue
|
||||||
|
value = clean(match.group(1))
|
||||||
|
total = clean(match.group(2))
|
||||||
|
|
||||||
running_total += value
|
running_total += value
|
||||||
diff = abs(running_total - total)
|
diff = abs(running_total - total)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue