diff options
-rw-r--r-- | Makefile.am | 6 | ||||
-rwxr-xr-x | acprep | 4 | ||||
-rw-r--r-- | config.cc | 1 | ||||
-rw-r--r-- | config.h | 1 | ||||
-rw-r--r-- | csv.cc | 105 | ||||
-rw-r--r-- | csv.h | 24 | ||||
-rw-r--r-- | format.cc | 25 | ||||
-rw-r--r-- | journal.cc | 21 | ||||
-rw-r--r-- | journal.h | 2 | ||||
-rw-r--r-- | ledger.h | 1 | ||||
-rw-r--r-- | main.cc | 8 | ||||
-rw-r--r-- | option.cc | 54 | ||||
-rw-r--r-- | option.h | 2 | ||||
-rwxr-xr-x | tests/confirm.py | 7 |
14 files changed, 200 insertions, 61 deletions
diff --git a/Makefile.am b/Makefile.am index bdf5b5d6..8a50450c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -18,6 +18,7 @@ libledger_la_CXXFLAGS = libledger_la_SOURCES = \ binary.cc \ config.cc \ + csv.cc \ derive.cc \ emacs.cc \ format.cc \ @@ -63,6 +64,7 @@ pkginclude_HEADERS = \ \ binary.h \ config.h \ + csv.h \ derive.h \ emacs.h \ error.h \ @@ -151,11 +153,11 @@ endif alltests.cc: $(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 $@ \ alltests.cc -L. -L.libs -lamounts -lledger -runtests: alltests ledger +runtests: alltests ./alltests && tests/regress && tests/regtest verify: runtests @@ -19,9 +19,9 @@ autoconf 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/python2.4" +INCDIRS="$INCDIRS -I/usr/include/python2.3" 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 shift 1 @@ -85,7 +85,6 @@ config_t::config_t() "%32|%-.22A %12.67t %!12.80T\n"); wide_register_format = ("%D %-.35P %-.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_total_format = "%D %(@S(@T))\n"; print_format = "\n%d %Y%C%P\n %-34W %12o%n\n%/ %-34W %12o%n\n"; @@ -21,7 +21,6 @@ class config_t std::string balance_format; std::string register_format; std::string wide_register_format; - std::string csv_register_format; std::string plot_amount_format; std::string plot_total_format; std::string print_format; @@ -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 @@ -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 @@ -300,27 +300,6 @@ element_t * format_t::parse_elements(const std::string& fmt) 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 { inline void mark_red(std::ostream& out, const element_t * elem) { 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: if (details.entry) { transaction_t::state_t state; - if (entry_state(details.entry, &state)) + if (details.entry->get_state(&state)) switch (state) { case transaction_t::CLEARED: out << "* "; @@ -688,7 +667,7 @@ void format_t::format(std::ostream& out_str, const details_t& details) const case element_t::OPT_ACCOUNT: if (details.entry && details.xact) { transaction_t::state_t state; - if (! entry_state(details.entry, &state)) + if (! details.entry->get_state(&state)) switch (details.xact->state) { case transaction_t::CLEARED: name = "* "; @@ -281,6 +281,27 @@ entry_t::entry_t(const entry_t& e) (*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) { xact->entry = this; @@ -193,6 +193,8 @@ class entry_t : public entry_base_t virtual void add_transaction(transaction_t * xact); virtual bool valid() const; + + bool get_state(transaction_t::state_t * state) const; }; struct entry_finalizer_t { @@ -19,6 +19,7 @@ #include <datetime.h> #include <format.h> #include <emacs.h> +#include <csv.h> #include <quotes.h> #include <valexpr.h> #include <walk.h> @@ -115,10 +115,8 @@ int parse_and_report(config_t& config, report_t& report, command = "P"; else if (command == "pricesdb") command = "D"; - else if (command == "csv") { - config.register_format = config.csv_register_format; - command = "r"; - } + else if (command == "csv") + command = "c"; else if (command == "parse") { 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); else if (command == "X") formatter = new format_xml_entries(*out, report.show_totals); + else if (command == "c") + formatter = new format_csv_transactions(*out); else formatter = new format_transactions(*out, *format); @@ -86,47 +86,56 @@ void process_arguments(option_t * options, int argc, char ** argv, // --long-option or -s again: - option_t * opt = NULL; - char * value = NULL; - if ((*i)[1] == '-') { if ((*i)[2] == '\0') break; - char * name = *i + 2; + char * name = *i + 2; + char * value = NULL; if (char * p = std::strchr(name, '=')) { *p++ = '\0'; value = p; } - opt = search_options(options, name); + option_t * opt = search_options(options, name); if (! opt) throw new option_error(std::string("illegal option --") + name); - if (opt->wants_arg && ! value) { + if (opt->wants_arg && value == NULL) { value = *++i; - if (! value) + if (value == NULL) throw new option_error(std::string("missing option argument for --") + name); } process_option(opt, value); - } else { - char c = (*i)[1]; - opt = search_options(options, c); - if (! opt) - throw new option_error(std::string("illegal option -") + c); + } + else if ((*i)[1] == '\0') { + 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) + throw new option_error(std::string("illegal option -") + c); + opt_queue.push_back(opt); + } - if (opt->wants_arg) { - value = *++i; - if (! value) - throw new option_error(std::string("missing option argument for -") + c); + for (std::list<option_t *>::iterator o = opt_queue.begin(); + o != opt_queue.end(); o++) { + char * value = NULL; + if ((*o)->wants_arg) { + value = *++i; + if (value == NULL) + 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: ; } @@ -562,10 +571,6 @@ OPT_BEGIN(wide_register_format, ":") { config->wide_register_format = optarg; } 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, ":") { config->plot_amount_format = optarg; } OPT_END(plot_amount_format); @@ -977,7 +982,6 @@ option_t config_options[CONFIG_OPTIONS_SIZE] = { { "collapse", 'n', false, opt_collapse, false }, { "comm-as-payee", 'x', false, opt_comm_as_payee, false }, { "cost", '\0', false, opt_basis, false }, - { "csv-register-format", '\0', true, opt_csv_register_format, false }, { "current", 'c', false, opt_current, false }, { "daily", '\0', false, opt_daily, false }, { "date-format", 'y', true, opt_date_format, false }, @@ -38,7 +38,7 @@ class report_t; extern config_t * config; extern report_t * report; -#define CONFIG_OPTIONS_SIZE 98 +#define CONFIG_OPTIONS_SIZE 97 extern option_t config_options[CONFIG_OPTIONS_SIZE]; void option_help(std::ostream& out); diff --git a/tests/confirm.py b/tests/confirm.py index 0881001b..f4947224 100755 --- a/tests/confirm.py +++ b/tests/confirm.py @@ -17,8 +17,11 @@ errors = 0 report = sys.argv[1] for line in os.popen("./ledger -f utils/standard.dat -e 2004/4 %s reg %s" % (report, sys.argv[2])): - value = clean(line[55:67]) - total = clean(line[68:]) + match = re.match("\\s*([-$,0-9.]+)\\s+([-$,0-9.]+)", line[55:]) + if not match: + continue + value = clean(match.group(1)) + total = clean(match.group(2)) running_total += value diff = abs(running_total - total) |