summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am6
-rwxr-xr-xacprep4
-rw-r--r--config.cc1
-rw-r--r--config.h1
-rw-r--r--csv.cc105
-rw-r--r--csv.h24
-rw-r--r--format.cc25
-rw-r--r--journal.cc21
-rw-r--r--journal.h2
-rw-r--r--ledger.h1
-rw-r--r--main.cc8
-rw-r--r--option.cc54
-rw-r--r--option.h2
-rwxr-xr-xtests/confirm.py7
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
diff --git a/acprep b/acprep
index e85d212e..0938d926 100755
--- a/acprep
+++ b/acprep
@@ -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
diff --git a/config.cc b/config.cc
index 84442df0..847feff7 100644
--- a/config.cc
+++ b/config.cc
@@ -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";
diff --git a/config.h b/config.h
index 005fba58..3bb8ed0d 100644
--- a/config.h
+++ b/config.h
@@ -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;
diff --git a/csv.cc b/csv.cc
new file mode 100644
index 00000000..4a8c1157
--- /dev/null
+++ b/csv.cc
@@ -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
diff --git a/csv.h b/csv.h
new file mode 100644
index 00000000..3b370631
--- /dev/null
+++ b/csv.h
@@ -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
diff --git a/format.cc b/format.cc
index 2872e794..cb890926 100644
--- a/format.cc
+++ b/format.cc
@@ -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 = "* ";
diff --git a/journal.cc b/journal.cc
index 017f82c5..065825f2 100644
--- a/journal.cc
+++ b/journal.cc
@@ -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;
diff --git a/journal.h b/journal.h
index b5228ac4..212590cf 100644
--- a/journal.h
+++ b/journal.h
@@ -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 {
diff --git a/ledger.h b/ledger.h
index adeebb2e..a59d562d 100644
--- a/ledger.h
+++ b/ledger.h
@@ -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>
diff --git a/main.cc b/main.cc
index 78ae0f5d..30eb2794 100644
--- a/main.cc
+++ b/main.cc
@@ -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);
diff --git a/option.cc b/option.cc
index 266e6b08..a9ef802d 100644
--- a/option.cc
+++ b/option.cc
@@ -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 },
diff --git a/option.h b/option.h
index b812fb09..91838b99 100644
--- a/option.h
+++ b/option.h
@@ -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)