diff options
author | John Wiegley <johnw@newartisans.com> | 2010-03-17 06:22:43 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2010-03-17 06:22:43 -0400 |
commit | 7ca8149ec5c7fa88d98df83e6260210372223036 (patch) | |
tree | 4c5741d682e89b1ea4e567c32a78d32ba17737db /src | |
parent | 3bb4cb6b6055f180112f357cf23463a1b8c186c5 (diff) | |
parent | 8dd362b57cf2b49c5268e72898ae873522d8756f (diff) | |
download | fork-ledger-7ca8149ec5c7fa88d98df83e6260210372223036.tar.gz fork-ledger-7ca8149ec5c7fa88d98df83e6260210372223036.tar.bz2 fork-ledger-7ca8149ec5c7fa88d98df83e6260210372223036.zip |
Merge branch 'next'
Diffstat (limited to 'src')
123 files changed, 1740 insertions, 267 deletions
diff --git a/src/account.cc b/src/account.cc index f8729409..245d61fc 100644 --- a/src/account.cc +++ b/src/account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/account.h b/src/account.h index 73cd35ac..0a3a75e4 100644 --- a/src/account.h +++ b/src/account.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/accum.cc b/src/accum.cc index b918c76a..62e36deb 100644 --- a/src/accum.cc +++ b/src/accum.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/accum.h b/src/accum.h index 878c2b7c..94e79948 100644 --- a/src/accum.h +++ b/src/accum.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/amount.cc b/src/amount.cc index eddbca18..3a64577f 100644 --- a/src/amount.cc +++ b/src/amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/amount.h b/src/amount.h index a37efdb8..5c1bca46 100644 --- a/src/amount.h +++ b/src/amount.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/annotate.cc b/src/annotate.cc index 146a7afd..99c12fc3 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/annotate.h b/src/annotate.h index 38ebaeae..6ef26476 100644 --- a/src/annotate.h +++ b/src/annotate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/archive.cc b/src/archive.cc index 7306f8d3..e7fc072e 100644 --- a/src/archive.cc +++ b/src/archive.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/archive.h b/src/archive.h index 03fc970a..e954cd3e 100644 --- a/src/archive.h +++ b/src/archive.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/balance.cc b/src/balance.cc index 4fcc83fa..9b39a49a 100644 --- a/src/balance.cc +++ b/src/balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/balance.h b/src/balance.h index 826de134..f8455d49 100644 --- a/src/balance.h +++ b/src/balance.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/chain.cc b/src/chain.cc index ecb39e0b..86f639ad 100644 --- a/src/chain.cc +++ b/src/chain.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -112,10 +112,12 @@ post_handler_ptr chain_post_handlers(report_t& report, else handler.reset(new sort_posts(handler, report.HANDLER(sort_).str())); } +#if 0 else if (! report.HANDLED(period_) && ! report.HANDLED(unsorted)) { handler.reset(new sort_posts(handler, "date")); } +#endif // collapse_posts causes xacts with multiple posts to appear as xacts // with a subtotaled post for each commodity used. @@ -167,7 +169,7 @@ post_handler_ptr chain_post_handlers(report_t& report, } else if (report.HANDLED(pivot_)) { string pivot = report.HANDLER(pivot_).str(); - pivot = string("\"") + pivot + ":\" + tag(/" + pivot + "/)"; + pivot = string("\"") + pivot + ":\" + tag(\"" + pivot + "\")"; handler.reset(new transfer_details(handler, transfer_details::SET_ACCOUNT, report.session.journal->master, pivot, report)); diff --git a/src/chain.h b/src/chain.h index a2b75b3a..94d54317 100644 --- a/src/chain.h +++ b/src/chain.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/commodity.cc b/src/commodity.cc index 24016830..e5f10e34 100644 --- a/src/commodity.cc +++ b/src/commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/commodity.h b/src/commodity.h index d2d8af21..10f209fa 100644 --- a/src/commodity.h +++ b/src/commodity.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/compare.cc b/src/compare.cc index 36884a46..f3c13cea 100644 --- a/src/compare.cc +++ b/src/compare.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/compare.h b/src/compare.h index 740ba275..eeecd7cf 100644 --- a/src/compare.h +++ b/src/compare.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/convert.cc b/src/convert.cc new file mode 100644 index 00000000..6c02cff3 --- /dev/null +++ b/src/convert.cc @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "convert.h" +#include "csv.h" +#include "scope.h" +#include "interactive.h" +#include "iterators.h" +#include "report.h" +#include "xact.h" +#include "print.h" +#include "lookup.h" + +namespace ledger { + +value_t convert_command(call_scope_t& scope) +{ + interactive_t args(scope, "s"); + report_t& report(find_scope<report_t>(scope)); + journal_t& journal(*report.session.journal.get()); + + string bucket_name; + if (report.HANDLED(account_)) + bucket_name = report.HANDLER(account_).str(); + else + bucket_name = "Equity:Unknown"; + + account_t * bucket = journal.master->find_account(bucket_name); + account_t * unknown = journal.master->find_account(_("Expenses:Unknown")); + + // Make an amounts mapping for the account under consideration + + typedef std::map<value_t, std::list<post_t *> > post_map_t; + post_map_t post_map; + + xacts_iterator journal_iter(journal); + while (xact_t * xact = journal_iter()) { + post_t * post = NULL; + xact_posts_iterator xact_iter(*xact); + while ((post = xact_iter()) != NULL) { + if (post->account == bucket) + break; + } + if (post) { + post_map_t::iterator i = post_map.find(post->amount); + if (i == post_map.end()) { + std::list<post_t *> post_list; + post_list.push_back(post); + post_map.insert(post_map_t::value_type(post->amount, post_list)); + } else { + (*i).second.push_back(post); + } + } + } + + // Create a flat list o + xacts_list current_xacts(journal.xacts_begin(), journal.xacts_end()); + + // Read in the series of transactions from the CSV file + + print_xacts formatter(report); + ifstream data(path(args.get<string>(0))); + csv_reader reader(data); + + while (xact_t * xact = reader.read_xact(journal, bucket)) { + bool matched = false; + post_map_t::iterator i = post_map.find(- xact->posts.front()->amount); + if (i != post_map.end()) { + std::list<post_t *>& post_list((*i).second); + foreach (post_t * post, post_list) { + if (xact->code && post->xact->code && + *xact->code == *post->xact->code) { + matched = true; + break; + } + else if (xact->actual_date() == post->actual_date()) { + matched = true; + break; + } + } + } + + if (matched) { + DEBUG("convert.csv", "Ignored xact with code: " << *xact->code); + delete xact; // ignore it + } + else { + if (xact->posts.front()->account == NULL) { + xacts_iterator xi; + xi.xacts_i = current_xacts.begin(); + xi.xacts_end = current_xacts.end(); + xi.xacts_uninitialized = false; + + // jww (2010-03-07): Bind this logic to an option: --auto-match + if (account_t * acct = + lookup_probable_account(xact->payee, xi, bucket).second) + xact->posts.front()->account = acct; + else + xact->posts.front()->account = unknown; + } + + if (! journal.add_xact(xact)) { + delete xact; + throw_(std::runtime_error, + _("Failed to finalize derived transaction (check commodities)")); + } + else { + xact_posts_iterator xact_iter(*xact); + while (post_t * post = xact_iter()) + formatter(*post); + } + } + } + formatter.flush(); + + // If not, transform the payee according to regexps + + // Set the account to a default vaule, then transform the account according + // to the payee + + // Print out the final form of the transaction + + return true; +} + +} // namespace ledger diff --git a/src/convert.h b/src/convert.h new file mode 100644 index 00000000..6d02f24a --- /dev/null +++ b/src/convert.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup data + */ + +/** + * @file convert.h + * @author John Wiegley + * + * @ingroup data + */ +#ifndef _CONVERT_H +#define _CONVERT_H + +#include "value.h" + +namespace ledger { + +class call_scope_t; + +value_t convert_command(call_scope_t& scope); + +} // namespace ledger + +#endif // _CONVERT_H diff --git a/src/csv.cc b/src/csv.cc new file mode 100644 index 00000000..5a74232f --- /dev/null +++ b/src/csv.cc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "csv.h" +#include "xact.h" +#include "post.h" +#include "account.h" +#include "journal.h" +#include "pool.h" + +namespace ledger { + +string csv_reader::read_field(std::istream& in) +{ + string field; + + char c; + if (in.peek() == '"' || in.peek() == '|') { + in.get(c); + char x; + while (in.good() && ! in.eof()) { + in.get(x); + if (x == '\\') { + in.get(x); + } + else if (x == '"' && in.peek() == '"') { + in.get(x); + } + else if (x == c) { + if (x == '|') + in.unget(); + else if (in.peek() == ',') + in.get(c); + break; + } + if (x != '\0') + field += x; + } + } + else { + while (in.good() && ! in.eof()) { + in.get(c); + if (c == ',') + break; + if (c != '\0') + field += c; + } + } + trim(field); + return field; +} + +char * csv_reader::next_line(std::istream& in) +{ + static char linebuf[MAX_LINE + 1]; + + while (in.good() && ! in.eof() && in.peek() == '#') + in.getline(linebuf, MAX_LINE); + + if (! in.good() || in.eof()) + return NULL; + + in.getline(linebuf, MAX_LINE); + + return linebuf; +} + +void csv_reader::read_index(std::istream& in) +{ + char * line = next_line(in); + if (! line) + return; + + std::istringstream instr(line); + + while (instr.good() && ! instr.eof()) { + string field = read_field(instr); + names.push_back(field); + + if (date_mask.match(field)) + index.push_back(FIELD_DATE); + else if (date_eff_mask.match(field)) + index.push_back(FIELD_DATE_EFF); + else if (code_mask.match(field)) + index.push_back(FIELD_CODE); + else if (payee_mask.match(field)) + index.push_back(FIELD_PAYEE); + else if (amount_mask.match(field)) + index.push_back(FIELD_AMOUNT); + else if (cost_mask.match(field)) + index.push_back(FIELD_COST); + else if (total_mask.match(field)) + index.push_back(FIELD_TOTAL); + else if (note_mask.match(field)) + index.push_back(FIELD_NOTE); + else + index.push_back(FIELD_UNKNOWN); + + DEBUG("csv.parse", "Header field: " << field); + } +} + +xact_t * csv_reader::read_xact(journal_t& journal, account_t * bucket) +{ + restart: + char * line = next_line(in); + if (! line || index.empty()) + return NULL; + + std::istringstream instr(line); + + std::auto_ptr<xact_t> xact(new xact_t); + std::auto_ptr<post_t> post(new post_t); + + xact->set_state(item_t::CLEARED); + + xact->pos = position_t(); + xact->pos->pathname = "jww (2010-03-05): unknown"; + xact->pos->beg_pos = in.tellg(); + xact->pos->beg_line = 0; + xact->pos->sequence = 0; + + post->xact = xact.get(); + +#if 0 + post->pos = position_t(); + post->pos->pathname = pathname; + post->pos->beg_pos = line_beg_pos; + post->pos->beg_line = linenum; + post->pos->sequence = context.sequence++; +#endif + + post->set_state(item_t::CLEARED); + post->account = NULL; + + int n = 0; + amount_t amt; + string total; + + while (instr.good() && ! instr.eof()) { + string field = read_field(instr); + + switch (index[n]) { + case FIELD_DATE: + if (field.empty()) + goto restart; + try { + xact->_date = parse_date(field); + } + catch (date_error&) { + goto restart; + } + break; + + case FIELD_DATE_EFF: + xact->_date_eff = parse_date(field); + break; + + case FIELD_CODE: + if (! field.empty()) + xact->code = field; + break; + + case FIELD_PAYEE: { + bool found = false; + foreach (payee_mapping_t& value, journal.payee_mappings) { + DEBUG("csv.mappings", "Looking for payee mapping: " << value.first); + if (value.first.match(field)) { + xact->payee = value.second; + found = true; + break; + } + } + if (! found) + xact->payee = field; + break; + } + + case FIELD_AMOUNT: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); + post->amount = amt; + break; + } + + case FIELD_COST: { + std::istringstream amount_str(field); + amt.parse(amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity + (*commodity_pool_t::current_pool->default_commodity); + post->cost = amt; + break; + } + + case FIELD_TOTAL: + total = field; + break; + + case FIELD_NOTE: + xact->note = field; + break; + + case FIELD_UNKNOWN: + if (! names[n].empty() && ! field.empty()) + xact->set_tag(names[n], field); + break; + } + n++; + } + +#if 0 + xact->set_tag(_("Imported"), + string(format_date(CURRENT_DATE(), FMT_WRITTEN))); + xact->set_tag(_("Original"), string(line)); + xact->set_tag(_("SHA1"), string(sha1sum(line))); +#endif + + // Translate the account name, if we have enough information to do so + + foreach (account_mapping_t& value, journal.account_mappings) { + if (value.first.match(xact->payee)) { + post->account = value.second; + break; + } + } + + xact->add_post(post.release()); + + // Create the "balancing post", which refers to the account for this data + + post.reset(new post_t); + + post->xact = xact.get(); + +#if 0 + post->pos = position_t(); + post->pos->pathname = pathname; + post->pos->beg_pos = line_beg_pos; + post->pos->beg_line = linenum; + post->pos->sequence = context.sequence++; +#endif + + post->set_state(item_t::CLEARED); + post->account = bucket; + + if (! amt.is_null()) + post->amount = - amt; + + if (! total.empty()) { + std::istringstream assigned_amount_str(total); + amt.parse(assigned_amount_str, PARSE_NO_REDUCE); + if (! amt.has_commodity() && + commodity_pool_t::current_pool->default_commodity) + amt.set_commodity(*commodity_pool_t::current_pool->default_commodity); + post->assigned_amount = amt; + } + + xact->add_post(post.release()); + + return xact.release(); +} + +} // namespace ledger diff --git a/src/csv.h b/src/csv.h new file mode 100644 index 00000000..5ff8b59e --- /dev/null +++ b/src/csv.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup data + */ + +/** + * @file csv.h + * @author John Wiegley + * + * @ingroup data + */ +#ifndef _CSV_H +#define _CSV_H + +#include "value.h" + +namespace ledger { + +class xact_t; +class journal_t; +class account_t; + +class csv_reader +{ + static const std::size_t MAX_LINE = 1024; + + std::istream& in; + + enum headers_t { + FIELD_DATE = 0, + FIELD_DATE_EFF, + FIELD_CODE, + FIELD_PAYEE, + FIELD_AMOUNT, + FIELD_COST, + FIELD_TOTAL, + FIELD_NOTE, + + FIELD_UNKNOWN + }; + + mask_t date_mask; + mask_t date_eff_mask; + mask_t code_mask; + mask_t payee_mask; + mask_t amount_mask; + mask_t cost_mask; + mask_t total_mask; + mask_t note_mask; + + std::vector<int> index; + std::vector<string> names; + std::vector<string> fields; + + typedef std::map<string, string> string_map; + +public: + csv_reader(std::istream& _in) + : in(_in), + date_mask("date"), + date_eff_mask("posted( ?date)?"), + code_mask("code"), + payee_mask("(payee|desc(ription)?|title)"), + amount_mask("amount"), + cost_mask("cost"), + total_mask("total"), + note_mask("note") { + read_index(in); + } + + string read_field(std::istream& in); + char * next_line(std::istream& in); + void read_index(std::istream& in); + + xact_t * read_xact(journal_t& journal, account_t * bucket); +}; + +} // namespace ledger + +#endif // _CSV_H diff --git a/src/draft.cc b/src/draft.cc index fc53e956..18075731 100644 --- a/src/draft.cc +++ b/src/draft.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -38,7 +38,8 @@ #include "journal.h" #include "session.h" #include "report.h" -#include "output.h" +#include "lookup.h" +#include "print.h" namespace ledger { @@ -240,20 +241,27 @@ void draft_t::parse_args(const value_t& args) xact_t * draft_t::insert(journal_t& journal) { if (tmpl->payee_mask.empty()) - throw std::runtime_error(_("xact' command requires at least a payee")); + throw std::runtime_error(_("'xact' command requires at least a payee")); - xact_t * matching = NULL; + xact_t * matching = NULL; std::auto_ptr<xact_t> added(new xact_t); - for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); - j != journal.xacts.rend(); - j++) { - if (tmpl->payee_mask.match((*j)->payee)) { - matching = *j; - DEBUG("derive.xact", - "Found payee match: transaction on line " << (*j)->pos->beg_line); - break; + xacts_iterator xi(journal); + if (xact_t * xact = lookup_probable_account(tmpl->payee_mask.str(), xi).first) { + DEBUG("derive.xact", "Found payee by lookup: transaction on line " + << xact->pos->beg_line); + matching = xact; + } else { + for (xacts_list::reverse_iterator j = journal.xacts.rbegin(); + j != journal.xacts.rend(); + j++) { + if (tmpl->payee_mask.match((*j)->payee)) { + matching = *j; + DEBUG("derive.xact", + "Found payee match: transaction on line " << (*j)->pos->beg_line); + break; + } } } @@ -269,15 +277,15 @@ xact_t * draft_t::insert(journal_t& journal) if (matching) { added->payee = matching->payee; - added->code = matching->code; - added->note = matching->note; + //added->code = matching->code; + //added->note = matching->note; #if defined(DEBUG_ON) DEBUG("derive.xact", "Setting payee from match: " << added->payee); - if (added->code) - DEBUG("derive.xact", "Setting code from match: " << *added->code); - if (added->note) - DEBUG("derive.xact", "Setting note from match: " << *added->note); + //if (added->code) + // DEBUG("derive.xact", "Setting code from match: " << *added->code); + //if (added->note) + // DEBUG("derive.xact", "Setting note from match: " << *added->note); #endif } else { added->payee = tmpl->payee_mask.str(); @@ -520,10 +528,7 @@ value_t xact_command(call_scope_t& args) // Only consider actual postings for the "xact" command report.HANDLER(limit_).on(string("#xact"), "actual"); - report.xact_report(post_handler_ptr - (new format_posts(report, - report.HANDLER(print_format_).str())), - *new_xact); + report.xact_report(post_handler_ptr(new print_xacts(report)), *new_xact); return true; } diff --git a/src/draft.h b/src/draft.h index 93e98ff5..59039f77 100644 --- a/src/draft.h +++ b/src/draft.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/emacs.cc b/src/emacs.cc index dc1a18ae..d47f04ad 100644 --- a/src/emacs.cc +++ b/src/emacs.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/emacs.h b/src/emacs.h index 4b2a452a..97292728 100644 --- a/src/emacs.h +++ b/src/emacs.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/error.cc b/src/error.cc index d5abe4de..cfc91ff1 100644 --- a/src/error.cc +++ b/src/error.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/error.h b/src/error.h index 5369faf1..0d33f020 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/expr.cc b/src/expr.cc index c59f8a57..79fb3611 100644 --- a/src/expr.cc +++ b/src/expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/exprbase.h b/src/exprbase.h index 0b3466b0..0dbf3bff 100644 --- a/src/exprbase.h +++ b/src/exprbase.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/filters.cc b/src/filters.cc index 2926eb08..0c45d356 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -107,6 +107,9 @@ void truncate_xacts::flush() void truncate_xacts::operator()(post_t& post) { + if (completed) + return; + if (last_xact != post.xact) { if (last_xact) xacts_seen++; @@ -114,8 +117,11 @@ void truncate_xacts::operator()(post_t& post) } if (tail_count == 0 && head_count > 0 && - static_cast<int>(xacts_seen) >= head_count) + static_cast<int>(xacts_seen) >= head_count) { + flush(); + completed = true; return; + } posts.push_back(&post); } diff --git a/src/filters.h b/src/filters.h index 92148dbe..82fbf687 100644 --- a/src/filters.h +++ b/src/filters.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -125,8 +125,9 @@ public: class truncate_xacts : public item_handler<post_t> { - int head_count; - int tail_count; + int head_count; + int tail_count; + bool completed; posts_list posts; std::size_t xacts_seen; @@ -139,7 +140,7 @@ public: int _head_count, int _tail_count) : item_handler<post_t>(handler), head_count(_head_count), tail_count(_tail_count), - xacts_seen(0), last_xact(NULL) { + completed(false), xacts_seen(0), last_xact(NULL) { TRACE_CTOR(truncate_xacts, "post_handler_ptr, int, int"); } virtual ~truncate_xacts() { diff --git a/src/flags.h b/src/flags.h index 69e40e4b..03f47ff9 100644 --- a/src/flags.h +++ b/src/flags.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/format.cc b/src/format.cc index f26a86a1..c546926e 100644 --- a/src/format.cc +++ b/src/format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/format.h b/src/format.h index a2bf1015..72d44ee5 100644 --- a/src/format.h +++ b/src/format.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/generate.cc b/src/generate.cc index 3549adc8..c1eb1d14 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/generate.h b/src/generate.h index 66513fc8..3d9965a5 100644 --- a/src/generate.h +++ b/src/generate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/global.cc b/src/global.cc index e120e5d5..170509b0 100644 --- a/src/global.cc +++ b/src/global.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/global.h b/src/global.h index ab3afed4..05e3bdb7 100644 --- a/src/global.h +++ b/src/global.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -110,7 +110,7 @@ public: out << "Ledger " << ledger::version << _(", the command-line accounting tool"); out << - _("\n\nCopyright (c) 2003-2009, John Wiegley. All rights reserved.\n\n\ + _("\n\nCopyright (c) 2003-2010, John Wiegley. All rights reserved.\n\n\ This program is made available under the terms of the BSD Public License.\n\ See LICENSE file included with the distribution for details and disclaimer."); out << std::endl; diff --git a/src/interactive.cc b/src/interactive.cc index 35382e8f..d0baf07a 100644 --- a/src/interactive.cc +++ b/src/interactive.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/interactive.h b/src/interactive.h index 199b7b71..fbc4ffeb 100644 --- a/src/interactive.h +++ b/src/interactive.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/item.cc b/src/item.cc index 8d1ba34f..14a0896f 100644 --- a/src/item.cc +++ b/src/item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -65,8 +65,8 @@ bool item_t::has_tag(const mask_t& tag_mask, if (tag_mask.match(data.first)) { if (! value_mask) return true; - else if (data.second) - return value_mask->match(*data.second); + else if (data.second.first) + return value_mask->match(*data.second.first); } } } @@ -81,7 +81,7 @@ optional<string> item_t::get_tag(const string& tag) const string_map::const_iterator i = metadata->find(tag); if (i != metadata->end()) { DEBUG("item.meta", "Found the item!"); - return (*i).second; + return (*i).second.first; } } return none; @@ -94,28 +94,44 @@ optional<string> item_t::get_tag(const mask_t& tag_mask, foreach (const string_map::value_type& data, *metadata) { if (tag_mask.match(data.first) && (! value_mask || - (data.second && value_mask->match(*data.second)))) - return data.second; + (data.second.first && value_mask->match(*data.second.first)))) + return data.second.first; } } return none; } -void item_t::set_tag(const string& tag, - const optional<string>& value) +item_t::string_map::iterator +item_t::set_tag(const string& tag, + const optional<string>& value, + const bool overwrite_existing) { + assert(! tag.empty()); + if (! metadata) metadata = string_map(); DEBUG("item.meta", "Setting tag '" << tag << "' to value '" << (value ? *value : string("<none>")) << "'"); - std::pair<string_map::iterator, bool> result - = metadata->insert(string_map::value_type(tag, value)); - assert(result.second); + optional<string> data = value; + if (data && data->empty()) + data = none; + + string_map::iterator i = metadata->find(tag); + if (i == metadata->end()) { + std::pair<string_map::iterator, bool> result + = metadata->insert(string_map::value_type(tag, tag_data_t(data, false))); + assert(result.second); + return result.first; + } else { + if (overwrite_existing) + (*i).second = tag_data_t(data, false); + return i; + } } -void item_t::parse_tags(const char * p, +void item_t::parse_tags(const char * p, bool overwrite_existing, optional<date_t::year_type> current_year) { if (const char * b = std::strchr(p, '[')) { @@ -149,15 +165,18 @@ void item_t::parse_tags(const char * p, q = std::strtok(NULL, " \t")) { const string::size_type len = std::strlen(q); if (! tag.empty()) { - if (! has_tag(tag)) - set_tag(tag, string(p + (q - buf.get()))); + string_map::iterator i = set_tag(tag, string(p + (q - buf.get())), + overwrite_existing); + (*i).second.second = true; break; } else if (q[0] == ':' && q[len - 1] == ':') { // a series of tags for (char * r = std::strtok(q + 1, ":"); r; - r = std::strtok(NULL, ":")) - set_tag(r); + r = std::strtok(NULL, ":")) { + string_map::iterator i = set_tag(r, none, overwrite_existing); + (*i).second.second = true; + } } else if (q[len - 1] == ':') { // a metadata setting tag = string(q, len - 1); @@ -165,7 +184,7 @@ void item_t::parse_tags(const char * p, } } -void item_t::append_note(const char * p, +void item_t::append_note(const char * p, bool overwrite_existing, optional<date_t::year_type> current_year) { if (note) { @@ -175,7 +194,7 @@ void item_t::append_note(const char * p, note = p; } - parse_tags(p, current_year); + parse_tags(p, overwrite_existing, current_year); } namespace { @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -106,7 +106,8 @@ public: enum state_t { UNCLEARED = 0, CLEARED, PENDING }; - typedef std::map<string, optional<string> > string_map; + typedef std::pair<optional<string>, bool> tag_data_t; + typedef std::map<string, tag_data_t> string_map; state_t _state; optional<date_t> _date; @@ -156,12 +157,14 @@ public: virtual optional<string> get_tag(const mask_t& tag_mask, const optional<mask_t>& value_mask = none) const; - virtual void set_tag(const string& tag, - const optional<string>& value = none); + virtual string_map::iterator + set_tag(const string& tag, + const optional<string>& value = none, + const bool overwrite_existing = true); - virtual void parse_tags(const char * p, + virtual void parse_tags(const char * p, bool overwrite_existing = true, optional<date_t::year_type> current_year = none); - virtual void append_note(const char * p, + virtual void append_note(const char * p, bool overwrite_existing = true, optional<date_t::year_type> current_year = none); static bool use_effective_date; diff --git a/src/iterators.cc b/src/iterators.cc index 540ba8ae..f3a68978 100644 --- a/src/iterators.cc +++ b/src/iterators.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/iterators.h b/src/iterators.h index 1cbe4c25..5e9f5fbe 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/journal.cc b/src/journal.cc index 5aa2e7f7..158fac42 100644 --- a/src/journal.cc +++ b/src/journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/journal.h b/src/journal.h index 8d59e3b4..e54814aa 100644 --- a/src/journal.h +++ b/src/journal.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -44,6 +44,7 @@ #include "utils.h" #include "times.h" +#include "mask.h" namespace ledger { @@ -58,6 +59,11 @@ typedef std::list<xact_t *> xacts_list; typedef std::list<auto_xact_t *> auto_xacts_list; typedef std::list<period_xact_t *> period_xacts_list; +typedef std::pair<mask_t, string> payee_mapping_t; +typedef std::list<payee_mapping_t> payee_mappings_t; +typedef std::pair<mask_t, account_t *> account_mapping_t; +typedef std::list<account_mapping_t> account_mappings_t; + class journal_t : public noncopyable { public: @@ -110,6 +116,8 @@ public: period_xacts_list period_xacts; std::list<fileinfo_t> sources; bool was_loaded; + payee_mappings_t payee_mappings; + account_mappings_t account_mappings; journal_t(); journal_t(const path& pathname); diff --git a/src/lookup.cc b/src/lookup.cc new file mode 100644 index 00000000..40b6c185 --- /dev/null +++ b/src/lookup.cc @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "lookup.h" +#include "unistring.h" + +namespace ledger { + +namespace { + typedef std::pair<xact_t *, int> score_entry_t; + typedef std::deque<score_entry_t> scorecard_t; + typedef std::map<uint32_t, std::size_t> char_positions_map; + + struct score_sorter { + bool operator()(const score_entry_t& left, + const score_entry_t& right) const { + return left.second > right.second; + } + }; + + typedef std::map<account_t *, int> account_use_map; + typedef std::pair<account_t *, int> account_use_pair; + + struct usage_sorter { + bool operator()(const account_use_pair& left, + const account_use_pair& right) const { + return left.second > right.second; + } + }; +} + +std::pair<xact_t *, account_t *> +lookup_probable_account(const string& ident, + xacts_iterator& iter_func, + account_t * ref_account) +{ + scorecard_t scores; + +#if !defined(HAVE_BOOST_REGEX_UNICODE) + string lident = ident; + to_lower(lident); + unistring lowered_ident(lident); +#else + // jww (2010-03-07): Not yet implemented + unistring lowered_ident(ident); +#endif + + DEBUG("lookup.account", + "Looking up identifier '" << lowered_ident.extract() << "'"); +#if defined(DEBUG_ON) + if (ref_account != NULL) + DEBUG("lookup.account", + " with reference account: " << ref_account->fullname()); +#endif + + while (xact_t * xact = iter_func()) { +#if 0 + // Only consider transactions from the last two years (jww (2010-03-07): + // make this an option) + if ((CURRENT_DATE() - xact->date()).days() > 700) + continue; +#endif + + // An exact match is worth a score of 100 and terminates the search + if (ident == xact->payee) { + DEBUG("lookup", " we have an exact match, score = 100"); + scores.push_back(score_entry_t(xact, 100)); + break; + } + +#if !defined(HAVE_BOOST_REGEX_UNICODE) + string payee = xact->payee; + to_lower(payee); + unistring value_key(payee); +#else + // jww (2010-03-07): Not yet implemented + unistring value_key(xact->payee); +#endif + + DEBUG("lookup", "Considering payee: " << value_key.extract()); + + std::size_t index = 0; + std::size_t last_match_pos = unistring::npos; + int bonus = 0; + int score = 0; + std::size_t pos; + char_positions_map positions; + + // Walk each letter in the source identifier + foreach (const uint32_t& ch, lowered_ident.utf32chars) { + int addend = 0; + bool added_bonus = false; + std::size_t value_len = value_key.length(); + + pos = value_key.find(ch); + + // Ensure that a letter which has been matched is not matched twice, so + // that the two x's of Exxon don't both match to the single x in Oxford. + // This part of the loop is very expensive, but avoids a lot of bogus + // matches. + + char_positions_map::iterator pi = positions.find(ch); + while (pi != positions.end() && + pos != unistring::npos && pos <= (*pi).second && + (*pi).second + 1 < value_len) + pos = value_key.find(ch, (*pi).second + 1); + + if (pos != unistring::npos) { + if (pi != positions.end()) + (*pi).second = pos; + else + positions.insert(char_positions_map::value_type(ch, pos)); + + // If it occurs in the same order as the source identifier -- that is, + // without intervening letters to break the pattern -- it's worth 10 + // points. Plus, an extra point is added for every letter in chains + // of 3 or more. + + if (last_match_pos == unistring::npos ? + index == 0 && pos == 0 : pos == last_match_pos + 1) { + DEBUG("lookup", + " char " << index << " in-sequence match with bonus " << bonus); + addend += 10; + if (bonus > 2) + addend += bonus - 2; + bonus++; + added_bonus = true; + + last_match_pos = pos; + } + + // If it occurs in the same general sequence as the source identifier, + // it's worth 5 points, plus an extra point if it's within the next 3 + // characters, and an extra point if it's preceded by a non-alphabetic + // character. + // + // If the letter occurs at all in the target identifier, it's worth 1 + // point, plus an extra point if it's within 3 characters, and an + // extra point if it's preceded by a non-alphabetic character. + + else { + bool in_order_match = (last_match_pos != unistring::npos && + pos > last_match_pos); + DEBUG("lookup", " char " << index << " " << + (in_order_match ? "in-order" : "out-of-order") + << " match" << (in_order_match && pos - index < 3 ? + " with proximity bonus of 1" : "")); + + if (pos < index) + addend += 1; + else + addend += 5; + + if (in_order_match && pos - index < 3) + addend++; + +#if !defined(HAVE_BOOST_REGEX_UNICODE) + if (pos == 0 || (pos > 0 && !std::isalnum(value_key[pos - 1]))) + addend++; +#else + // jww (2010-03-07): Not yet implemented +#endif + + last_match_pos = pos; + } + + // If the letter does not appear at all, decrease the score by 1 + + } else { + last_match_pos = unistring::npos; + + DEBUG("lookup", " char " << index << " does not match"); + addend--; + } + + // Finally, decay what is to be added to the score based on its position + // in the word. Since credit card payees in particular often share + // information at the end (such as the location where the purchase was + // made), we want to give much more credence to what occurs at the + // beginning. Every 5 character positions from the beginning becomes a + // divisor for the addend. + + if ((int(index / 5) + 1) > 1) { + DEBUG("lookup", + " discounting the addend by / " << (int(index / 5) + 1)); + addend = int(double(addend) / (int(index / 5) + 1)); + } + + DEBUG("lookup", " final addend is " << addend); + score += addend; + DEBUG("lookup", " score is " << score); + + if (! added_bonus) + bonus = 0; + + index++; + } + + // Only consider payees with a score of 30 or greater + if (score >= 30) + scores.push_back(score_entry_t(xact, score)); + } + + // Sort the results by descending score, then look at every account ever + // used among the top five. Rank these by number of times used. Lastly, + // "decay" any latter accounts, so that we give recently used accounts a + // slightly higher rating in case of a tie. + + std::stable_sort(scores.begin(), scores.end(), score_sorter()); + + scorecard_t::iterator si = scores.begin(); + int decay = 0; + xact_t * best_xact = si != scores.end() ? (*si).first : NULL; + account_use_map account_usage; + + for (int i = 0; i < 5 && si != scores.end(); i++, si++) { + DEBUG("lookup.account", + "Payee: " << std::setw(5) << std::right << (*si).second << + " - " << (*si).first->payee); + + foreach (post_t * post, (*si).first->posts) { + if (! post->has_flags(ITEM_TEMP | ITEM_GENERATED) && + post->account != ref_account && + ! post->account->has_flags(ACCOUNT_TEMP | ACCOUNT_GENERATED)) { + account_use_map::iterator x = account_usage.find(post->account); + if (x == account_usage.end()) + account_usage.insert(account_use_pair(post->account, + ((*si).second - decay))); + else + (*x).second += ((*si).second - decay); + } + decay++; + } + } + + if (account_usage.size() > 0) { +#if defined(DEBUG_ON) + if (SHOW_DEBUG("lookup.account")) { + foreach (const account_use_pair& value, account_usage) { + DEBUG("lookup.account", + "Account: " << value.second << " - " << value.first->fullname()); + } + } +#endif + return std::pair<xact_t *, account_t *> + (best_xact, (*std::max_element(account_usage.begin(), account_usage.end(), + usage_sorter())).first); + } else { + return std::pair<xact_t *, account_t *>(best_xact, NULL); + } +} + +} // namespace ledger diff --git a/src/lookup.h b/src/lookup.h new file mode 100644 index 00000000..53cb5837 --- /dev/null +++ b/src/lookup.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup data + */ + +/** + * @file lookup.h + * @author John Wiegley + * + * @ingroup data + */ +#ifndef _LOOKUP_H +#define _LOOKUP_H + +#include "iterators.h" + +namespace ledger { + +std::pair<xact_t *, account_t *> +lookup_probable_account(const string& ident, + xacts_iterator& iter_func, + account_t * ref_account = NULL); + +} // namespace ledger + +#endif // _LOOKUP_H diff --git a/src/main.cc b/src/main.cc index 0aec8886..23593533 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/mask.cc b/src/mask.cc index c1e66ced..cd516fe2 100644 --- a/src/mask.cc +++ b/src/mask.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -52,4 +52,38 @@ mask_t& mask_t::operator=(const string& pat) return *this; } +mask_t& mask_t::assign_glob(const string& pat) +{ + string re_pat = ""; + string::size_type len = pat.length(); + for (string::size_type i = 0; i < len; i++) { + switch (pat[i]) { + case '?': + re_pat += '.'; + break; + case '*': + re_pat += ".*"; + break; + case '[': + while (i < len && pat[i] != ']') + re_pat += pat[i++]; + if (i < len) + re_pat += pat[i]; + break; + + case '\\': + if (i + 1 < len) { + re_pat += pat[++i]; + break; + } else { + // fallthrough... + } + default: + re_pat += pat[i]; + break; + } + } + return (*this = re_pat); +} + } // namespace ledger @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -73,7 +73,11 @@ public: } mask_t& operator=(const string& other); + mask_t& assign_glob(const string& other); + bool operator<(const mask_t& other) const { + return expr < other.expr; + } bool operator==(const mask_t& other) const { return expr == other.expr; } @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/option.cc b/src/option.cc index d2ec8808..b375dd8d 100644 --- a/src/option.cc +++ b/src/option.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/option.h b/src/option.h index b81c2ce7..9688171e 100644 --- a/src/option.h +++ b/src/option.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/output.cc b/src/output.cc index bb7eff5c..30775310 100644 --- a/src/output.cc +++ b/src/output.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -42,10 +42,8 @@ namespace ledger { format_posts::format_posts(report_t& _report, const string& format, - bool _print_raw, const optional<string>& _prepend_format) - : report(_report), last_xact(NULL), last_post(NULL), - print_raw(_print_raw) + : report(_report), last_xact(NULL), last_post(NULL) { TRACE_CTOR(format_posts, "report&, const string&, bool"); @@ -80,24 +78,8 @@ void format_posts::operator()(post_t& post) { std::ostream& out(report.output_stream); - if (print_raw) { - if (! post.has_xdata() || - ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { - if (last_xact != post.xact) { - if (last_xact) { - bind_scope_t xact_scope(report, *last_xact); - out << between_format(xact_scope); - } - print_item(out, *post.xact); - out << '\n'; - last_xact = post.xact; - } - post.xdata().add_flags(POST_EXT_DISPLAYED); - last_post = &post; - } - } - else if (! post.has_xdata() || - ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { + if (! post.has_xdata() || + ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { bind_scope_t bound_scope(report, post); if (prepend_format) diff --git a/src/output.h b/src/output.h index 778a9335..7618e567 100644 --- a/src/output.h +++ b/src/output.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -63,11 +63,9 @@ protected: format_t prepend_format; xact_t * last_xact; post_t * last_post; - bool print_raw; public: format_posts(report_t& _report, const string& format, - bool _print_raw = false, const optional<string>& _prepend_format = none); virtual ~format_posts() { TRACE_DTOR(format_posts); diff --git a/src/parser.cc b/src/parser.cc index ef778411..5bb06f84 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/parser.h b/src/parser.h index 7a69fe08..5eba4ffd 100644 --- a/src/parser.h +++ b/src/parser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pool.cc b/src/pool.cc index f895a8bc..8d9f0c28 100644 --- a/src/pool.cc +++ b/src/pool.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/post.cc b/src/post.cc index 43cfe55f..41ae04dd 100644 --- a/src/post.cc +++ b/src/post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -576,7 +576,7 @@ void to_xml(std::ostream& out, const post_t& post) if (post.metadata) { push_xml y(out, "metadata"); foreach (const item_t::string_map::value_type& pair, *post.metadata) { - if (pair.second) { + if (pair.second.first) { push_xml z(out, "variable"); { push_xml z(out, "key"); @@ -584,7 +584,7 @@ void to_xml(std::ostream& out, const post_t& post) } { push_xml z(out, "value"); - out << y.guard(*pair.second); + out << y.guard(*pair.second.first); } } else { push_xml z(out, "tag"); @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -52,10 +52,11 @@ class account_t; class post_t : public item_t { public: -#define POST_VIRTUAL 0x10 // the account was specified with (parens) -#define POST_MUST_BALANCE 0x20 // posting must balance in the transaction -#define POST_CALCULATED 0x40 // posting's amount was calculated -#define POST_COST_CALCULATED 0x80 // posting's cost was calculated +#define POST_VIRTUAL 0x08 // the account was specified with (parens) +#define POST_MUST_BALANCE 0x10 // posting must balance in the transaction +#define POST_CALCULATED 0x20 // posting's amount was calculated +#define POST_COST_CALCULATED 0x40 // posting's cost was calculated +#define POST_COST_IN_FULL 0x80 // cost specified using @@ xact_t * xact; // only set for posts of regular xacts account_t * account; diff --git a/src/precmd.cc b/src/precmd.cc index 632caeae..e545a783 100644 --- a/src/precmd.cc +++ b/src/precmd.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/precmd.h b/src/precmd.h index 88d66ab2..277933c3 100644 --- a/src/precmd.h +++ b/src/precmd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/predicate.cc b/src/predicate.cc index 4da4decf..369120e6 100644 --- a/src/predicate.cc +++ b/src/predicate.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/predicate.h b/src/predicate.h index b3b81f9b..c21167d7 100644 --- a/src/predicate.h +++ b/src/predicate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/print.cc b/src/print.cc new file mode 100644 index 00000000..eb102a87 --- /dev/null +++ b/src/print.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <system.hh> + +#include "print.h" +#include "xact.h" +#include "post.h" +#include "account.h" +#include "session.h" +#include "report.h" + +namespace ledger { + +print_xacts::print_xacts(report_t& _report, + bool _print_raw) + : report(_report), print_raw(_print_raw) +{ + TRACE_CTOR(print_xacts, "report&, bool"); +} + +namespace { + void print_note(std::ostream& out, const string& note) + { + if (note.length() > 15) + out << "\n ;"; + else + out << " ;"; + + bool need_separator = false; + for (const char * p = note.c_str(); *p; p++) { + if (*p == '\n') { + need_separator = true; + } else { + if (need_separator) { + out << "\n ;"; + need_separator = false; + } + out << *p; + } + } + } + + void print_xact(report_t& report, std::ostream& out, xact_t& xact) + { + out << format_date(item_t::use_effective_date ? + xact.date() : xact.actual_date(), + FMT_WRITTEN); + if (! item_t::use_effective_date && xact.effective_date()) + out << '=' << format_date(*xact.effective_date(), FMT_WRITTEN); + out << ' '; + + out << (xact.state() == item_t::CLEARED ? "* " : + (xact.state() == item_t::PENDING ? "! " : "")); + + if (xact.code) + out << '(' << *xact.code << ") "; + + out << xact.payee; + + if (xact.note) + print_note(out, *xact.note); + out << '\n'; + + if (xact.metadata) { + foreach (const item_t::string_map::value_type& data, *xact.metadata) { + if (! data.second.second) { + out << " ; "; + if (data.second.first) + out << data.first << ": " << *data.second.first; + else + out << ':' << data.first << ":"; + out << '\n'; + } + } + } + + foreach (post_t * post, xact.posts) { + if (post->has_flags(ITEM_TEMP | ITEM_GENERATED) && + ! report.HANDLED(print_virtual)) + continue; + + out << " "; + + std::ostringstream buf; + + if (xact.state() == item_t::UNCLEARED) + buf << (post->state() == item_t::CLEARED ? "* " : + (post->state() == item_t::PENDING ? "! " : "")); + + if (post->has_flags(POST_VIRTUAL)) { + if (post->has_flags(POST_MUST_BALANCE)) + buf << '['; + else + buf << '('; + } + + buf << post->account->fullname(); + + if (post->has_flags(POST_VIRTUAL)) { + if (post->has_flags(POST_MUST_BALANCE)) + buf << ']'; + else + buf << ')'; + } + + if (! post->has_flags(POST_CALCULATED) || report.HANDLED(print_virtual)) { + unistring name(buf.str()); + + out << name.extract(); + int slip = 36 - static_cast<int>(name.length()); + if (slip > 0) + out << string(slip, ' '); + + std::ostringstream amt_str; + report.scrub(post->amount).print(amt_str, 12, -1, true); + string amt = amt_str.str(); + string trimmed_amt(amt); + trim_left(trimmed_amt); + int amt_slip = (static_cast<int>(amt.length()) - + static_cast<int>(trimmed_amt.length())); + if (slip + amt_slip < 2) + out << string(2 - (slip + amt_slip), ' '); + out << amt; + + if (post->cost && ! post->has_flags(POST_CALCULATED)) { + if (post->has_flags(POST_COST_IN_FULL)) + out << " @@ " << report.scrub(post->cost->abs()); + else + out << " @ " << report.scrub((*post->cost / post->amount).abs()); + } + + if (post->assigned_amount) + out << " = " << report.scrub(*post->assigned_amount); + } else { + out << buf.str(); + } + + if (post->note) + print_note(out, *post->note); + out << '\n'; + } + } +} + +void print_xacts::flush() +{ + std::ostream& out(report.output_stream); + + bool first = true; + foreach (xact_t * xact, xacts) { + if (first) + first = false; + else + out << '\n'; + + if (print_raw) { + print_item(out, *xact); + out << '\n'; + } else { + print_xact(report, out, *xact); + } + } + + out.flush(); +} + +void print_xacts::operator()(post_t& post) +{ + if (! post.has_xdata() || + ! post.xdata().has_flags(POST_EXT_DISPLAYED)) { + if (xacts_present.find(post.xact) == xacts_present.end()) { + xacts_present.insert(xacts_present_map::value_type(post.xact, true)); + xacts.push_back(post.xact); + } + post.xdata().add_flags(POST_EXT_DISPLAYED); + } +} + +} // namespace ledger diff --git a/src/print.h b/src/print.h new file mode 100644 index 00000000..f323b153 --- /dev/null +++ b/src/print.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of New Artisans LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * @addtogroup data + */ + +/** + * @file convert.h + * @author John Wiegley + * + * @ingroup data + */ +#ifndef _PRINT_H +#define _PRINT_H + +#include "chain.h" +#include "predicate.h" +#include "format.h" + +namespace ledger { + +class xact_t; +class post_t; +class report_t; + +class print_xacts : public item_handler<post_t> +{ +protected: + typedef std::list<xact_t *> xacts_list; + typedef std::map<xact_t *, bool> xacts_present_map; + + report_t& report; + xacts_present_map xacts_present; + xacts_list xacts; + bool print_raw; + +public: + print_xacts(report_t& _report, bool _print_raw = false); + virtual ~print_xacts() { + TRACE_DTOR(print_xacts); + } + + virtual void flush(); + virtual void operator()(post_t& post); +}; + + +} // namespace ledger + +#endif // _PRINT_H diff --git a/src/pstream.h b/src/pstream.h index 00caf4e0..bba2845e 100644 --- a/src/pstream.h +++ b/src/pstream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions diff --git a/src/py_account.cc b/src/py_account.cc index 056cd722..3114cc0b 100644 --- a/src/py_account.cc +++ b/src/py_account.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_amount.cc b/src/py_amount.cc index 8fb507a3..5afe8c6b 100644 --- a/src/py_amount.cc +++ b/src/py_amount.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_balance.cc b/src/py_balance.cc index 8c0c4c58..7be75444 100644 --- a/src/py_balance.cc +++ b/src/py_balance.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_commodity.cc b/src/py_commodity.cc index 984be5f0..22a4f153 100644 --- a/src/py_commodity.cc +++ b/src/py_commodity.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_expr.cc b/src/py_expr.cc index cad2c0fa..027125e2 100644 --- a/src/py_expr.cc +++ b/src/py_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_format.cc b/src/py_format.cc index d4825b42..36f89ae5 100644 --- a/src/py_format.cc +++ b/src/py_format.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_item.cc b/src/py_item.cc index 8a6e495a..edebd532 100644 --- a/src/py_item.cc +++ b/src/py_item.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_journal.cc b/src/py_journal.cc index 5be9cbe1..81ce290d 100644 --- a/src/py_journal.cc +++ b/src/py_journal.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_post.cc b/src/py_post.cc index 20cdba6b..e4a50980 100644 --- a/src/py_post.cc +++ b/src/py_post.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_times.cc b/src/py_times.cc index a62bb9b6..39b0df7a 100644 --- a/src/py_times.cc +++ b/src/py_times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_utils.cc b/src/py_utils.cc index 5203599f..4b364e5d 100644 --- a/src/py_utils.cc +++ b/src/py_utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_value.cc b/src/py_value.cc index 713dc3d4..449320ec 100644 --- a/src/py_value.cc +++ b/src/py_value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/py_xact.cc b/src/py_xact.cc index 59c599d9..c4e1fd32 100644 --- a/src/py_xact.cc +++ b/src/py_xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pyfstream.h b/src/pyfstream.h index 3da37523..02e4d240 100644 --- a/src/pyfstream.h +++ b/src/pyfstream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pyinterp.cc b/src/pyinterp.cc index 0701176f..1330fc5c 100644 --- a/src/pyinterp.cc +++ b/src/pyinterp.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pyinterp.h b/src/pyinterp.h index f2d7b760..e3a091ad 100644 --- a/src/pyinterp.h +++ b/src/pyinterp.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pyledger.cc b/src/pyledger.cc index cf7e1527..4a53532a 100644 --- a/src/pyledger.cc +++ b/src/pyledger.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/pyutils.h b/src/pyutils.h index 54d6fa28..e0f77003 100644 --- a/src/pyutils.h +++ b/src/pyutils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/query.cc b/src/query.cc index cfa321b0..1f086df8 100644 --- a/src/query.cc +++ b/src/query.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/query.h b/src/query.h index ebc14020..2b0bc75d 100644 --- a/src/query.h +++ b/src/query.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/quotes.cc b/src/quotes.cc index f892e93f..4e3ec33e 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/quotes.h b/src/quotes.h index d00c5bfd..e981fdad 100644 --- a/src/quotes.h +++ b/src/quotes.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/report.cc b/src/report.cc index b882ca92..2189812b 100644 --- a/src/report.cc +++ b/src/report.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -37,12 +37,14 @@ #include "format.h" #include "query.h" #include "output.h" +#include "print.h" #include "iterators.h" #include "filters.h" #include "precmd.h" #include "stats.h" #include "generate.h" #include "draft.h" +#include "convert.h" #include "xml.h" #include "emacs.h" @@ -91,6 +93,33 @@ void report_t::normalize_options(const string& verb) start_of_week = *weekday; } + long meta_width = -1; + + if (! HANDLED(prepend_format_) && HANDLED(meta_)) { + if (! HANDLED(meta_width_)) { + string::size_type i = HANDLER(meta_).str().find(':'); + if (i != string::npos) { + HANDLED(meta_width_).on_with + (string("?normalize"), + lexical_cast<long>(string(HANDLER(meta_).str(), i + 1))); + HANDLED(meta_).on(string("?normalize"), + string(HANDLER(meta_).str(), 0, i)); + } + } + if (HANDLED(meta_width_)) { + HANDLER(prepend_format_).on + (string("?normalize"), + string("%(justify(truncated(tag(\"") + + HANDLER(meta_).str() + "\"), " + + HANDLED(meta_width_).value.to_string() + " - 1), " + + HANDLED(meta_width_).value.to_string() + "))"); + meta_width = HANDLED(meta_width_).value.to_long(); + } else { + HANDLER(prepend_format_).on(string("?normalize"), string("%(tag(\"") + + HANDLER(meta_).str() + "\"))"); + } + } + if (verb == "print" || verb == "xact" || verb == "dump") { HANDLER(related).on_only(string("?normalize")); HANDLER(related_all).on_only(string("?normalize")); @@ -163,6 +192,9 @@ void report_t::normalize_options(const string& verb) else cols = 80L; + if (meta_width > 0) + cols -= meta_width; + if (cols > 0) { DEBUG("auto.columns", "cols = " << cols); @@ -185,11 +217,11 @@ void report_t::normalize_options(const string& verb) HANDLER(total_width_).value.to_long() : amount_width); - DEBUG("auto.columns", "date_width = " << date_width); - DEBUG("auto.columns", "payee_width = " << payee_width); + DEBUG("auto.columns", "date_width = " << date_width); + DEBUG("auto.columns", "payee_width = " << payee_width); DEBUG("auto.columns", "account_width = " << account_width); - DEBUG("auto.columns", "amount_width = " << amount_width); - DEBUG("auto.columns", "total_width = " << total_width); + DEBUG("auto.columns", "amount_width = " << amount_width); + DEBUG("auto.columns", "total_width = " << total_width); if (! HANDLER(date_width_).specified && ! HANDLER(payee_width_).specified && @@ -205,6 +237,8 @@ void report_t::normalize_options(const string& verb) } } + if (! HANDLED(meta_width_)) + HANDLER(meta_width_).on_with(string("?normalize"), 0L); if (! HANDLER(date_width_).specified) HANDLER(date_width_).on_with(string("?normalize"), date_width); if (! HANDLER(payee_width_).specified) @@ -409,15 +443,20 @@ value_t report_t::fn_trim(call_scope_t& args) } } -value_t report_t::fn_scrub(call_scope_t& args) +value_t report_t::scrub(value_t val) { - value_t temp(args.value().strip_annotations(what_to_keep())); + value_t temp(val.strip_annotations(what_to_keep())); if (HANDLED(base)) return temp; else return temp.unreduced(); } +value_t report_t::fn_scrub(call_scope_t& args) +{ + return scrub(args.value()); +} + value_t report_t::fn_rounded(call_scope_t& args) { return args.value().rounded(); @@ -469,11 +508,11 @@ value_t report_t::fn_justify(call_scope_t& scope) value_t report_t::fn_quoted(call_scope_t& scope) { - interactive_t args(scope, "s"); + interactive_t args(scope, "v"); std::ostringstream out; out << '"'; - foreach (const char ch, args.get<string>(0)) { + foreach (const char ch, args.value_at(0).to_string()) { if (ch == '"') out << "\\\""; else @@ -855,6 +894,8 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'm': OPT(market); else OPT(monthly); + else OPT(meta_); + else OPT(meta_width_); break; case 'n': OPT_CH(collapse); @@ -879,9 +920,9 @@ option_t<report_t> * report_t::lookup_option(const char * p) else OPT(price); else OPT(prices_format_); else OPT(pricedb_format_); - else OPT(print_format_); else OPT(payee_width_); else OPT(prepend_format_); + else OPT(print_virtual); break; case 'q': OPT(quantity); @@ -1199,21 +1240,21 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, else if (is_eq(p, "cleared")) { HANDLER(amount_).set_expr(string("#cleared"), "(amount, cleared ? amount : 0)"); - return expr_t::op_t::wrap_functor (reporter<account_t, acct_handler_ptr, &report_t::accounts_report> (new format_accounts(*this, report_format(HANDLER(cleared_format_)), maybe_format(HANDLER(prepend_format_))), *this, "#cleared")); } + else if (is_eq(p, "convert")) + return WRAP_FUNCTOR(convert_command); break; case 'e': - if (is_eq(p, "equity")) - return WRAP_FUNCTOR - (reporter<> - (new format_posts(*this, report_format(HANDLER(print_format_))), - *this, "#equity")); + if (is_eq(p, "equity")) { + HANDLER(print_virtual).on_only(string("#equity")); + return WRAP_FUNCTOR(reporter<>(new print_xacts(*this), *this, "#equity")); + } else if (is_eq(p, "entry")) return WRAP_FUNCTOR(xact_command); else if (is_eq(p, "emacs")) @@ -1226,9 +1267,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, case 'p': if (*(p + 1) == '\0' || is_eq(p, "print")) return WRAP_FUNCTOR - (reporter<> - (new format_posts(*this, report_format(HANDLER(print_format_)), - HANDLED(raw)), *this, "#print")); + (reporter<>(new print_xacts(*this, HANDLED(raw)), *this, "#print")); else if (is_eq(p, "prices")) return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::commodities_report> @@ -1248,7 +1287,7 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR (reporter<> (new format_posts(*this, report_format(HANDLER(register_format_)), - false, maybe_format(HANDLER(prepend_format_))), + maybe_format(HANDLER(prepend_format_))), *this, "#register")); else if (is_eq(p, "reload")) return MAKE_FUNCTOR(report_t::reload_command); @@ -1283,11 +1322,13 @@ expr_t::ptr_op_t report_t::lookup(const symbol_t::kind_t kind, return WRAP_FUNCTOR(format_command); break; case 'g': - if (is_eq(p, "generate")) + if (is_eq(p, "generate")) { + HANDLER(print_virtual).on_only(string("#generate")); return expr_t::op_t::wrap_functor (reporter<post_t, post_handler_ptr, &report_t::generate_report> - (new format_posts(*this, report_format(HANDLER(print_format_)), - false), *this, "#generate")); + (new print_xacts(*this), *this, "#generate")); + } + break; case 'p': if (is_eq(p, "parse")) return WRAP_FUNCTOR(parse_command); diff --git a/src/report.h b/src/report.h index 08819e23..50071940 100644 --- a/src/report.h +++ b/src/report.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -144,6 +144,7 @@ public: value_t fn_is_seq(call_scope_t& scope); value_t fn_strip(call_scope_t& scope); value_t fn_trim(call_scope_t& scope); + value_t scrub(value_t val); value_t fn_scrub(call_scope_t& scope); value_t fn_quantity(call_scope_t& scope); value_t fn_rounded(call_scope_t& scope); @@ -263,6 +264,7 @@ public: HANDLER(lots).report(out); HANDLER(lots_actual).report(out); HANDLER(market).report(out); + HANDLER(meta_).report(out); HANDLER(monthly).report(out); HANDLER(no_total).report(out); HANDLER(now_).report(out); @@ -280,7 +282,7 @@ public: HANDLER(price).report(out); HANDLER(prices_format_).report(out); HANDLER(pricedb_format_).report(out); - HANDLER(print_format_).report(out); + HANDLER(print_virtual).report(out); HANDLER(quantity).report(out); HANDLER(quarterly).report(out); HANDLER(raw).report(out); @@ -311,6 +313,7 @@ public: HANDLER(weekly).report(out); HANDLER(wide).report(out); HANDLER(yearly).report(out); + HANDLER(meta_width_).report(out); HANDLER(date_width_).report(out); HANDLER(payee_width_).report(out); HANDLER(account_width_).report(out); @@ -618,6 +621,8 @@ public: .set_expr(string("--market"), "market(total_expr, date, exchange)"); }); + OPTION(report_t, meta_); + OPTION_(report_t, monthly, DO() { // -M parent->HANDLER(period_).on(string("--monthly"), "monthly"); }); @@ -748,23 +753,7 @@ public: "P %(datetime) %(account) %(scrub(display_amount))\n"); }); - OPTION__(report_t, print_format_, CTOR(report_t, print_format_) { - on(none, - "%(xact.date)" - "%(!effective & xact.effective_date ?" - " \"=\" + xact.effective_date : \"\")" - "%(xact.cleared ? \" *\" : (xact.pending ? \" !\" : \"\"))" - "%(code ? \" (\" + code + \")\" :" - " \"\") %(payee)%(xact.comment)\n" - " %(xact.uncleared ?" - " (cleared ? \"* \" : (pending ? \"! \" : \"\")) : \"\")" - "%(calculated ? account : justify(account, 34, -1, false))" - "%(calculated ? \"\" : \" \" + justify(scrub(amount), 12, -1, true))" - "%(has_cost & !cost_calculated ?" - " \" @ \" + justify(scrub(abs(cost / amount)), 0) : \"\")" - "%(comment)\n%/" - " %$7%$8%$9%$A%$B\n%/\n"); - }); + OPTION(report_t, print_virtual); OPTION_(report_t, quantity, DO() { // -O parent->HANDLER(revalued).off(); @@ -791,12 +780,13 @@ public: " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " " account_width), blue if color))" " %(justify(scrub(display_amount), amount_width, " - " 3 + date_width + payee_width + account_width + amount_width, " - " true, color))" + " 3 + meta_width + date_width + payee_width + account_width" + " + amount_width, true, color))" " %(justify(scrub(display_total), total_width, " - " 4 + date_width + payee_width + account_width + amount_width " - " + total_width, true, color))\n%/" - "%(justify(\" \", 2 + date_width + payee_width))%$3 %$4 %$5\n"); + " 4 + meta_width + date_width + payee_width + account_width" + " + amount_width + total_width, true, color))\n%/" + "%(justify(\" \", 2 + date_width + payee_width))" + "%$3 %$4 %$5\n"); }); OPTION(report_t, related); // -r @@ -906,6 +896,10 @@ public: parent->HANDLER(period_).on(string("--yearly"), "yearly"); }); + OPTION__(report_t, meta_width_, + bool specified; + CTOR(report_t, meta_width_) { specified = false; } + DO_(args) { value = args[1].to_long(); specified = true; }); OPTION__(report_t, date_width_, bool specified; CTOR(report_t, date_width_) { specified = false; } diff --git a/src/scope.cc b/src/scope.cc index 64736ca3..72cf4ad3 100644 --- a/src/scope.cc +++ b/src/scope.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/scope.h b/src/scope.h index 93f80230..675fa4e1 100644 --- a/src/scope.h +++ b/src/scope.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/session.cc b/src/session.cc index 1882e554..8e5536b0 100644 --- a/src/session.cc +++ b/src/session.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/session.h b/src/session.h index 5c4612a0..de1771ad 100644 --- a/src/session.h +++ b/src/session.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/stats.cc b/src/stats.cc index b89a5949..86a8338d 100644 --- a/src/stats.cc +++ b/src/stats.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/stats.h b/src/stats.h index d2d10de6..b7bf94c5 100644 --- a/src/stats.h +++ b/src/stats.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/stream.cc b/src/stream.cc index e39b74e0..272d4f1c 100644 --- a/src/stream.cc +++ b/src/stream.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/stream.h b/src/stream.h index e87da67a..356df08c 100644 --- a/src/stream.h +++ b/src/stream.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/system.hh.in b/src/system.hh.in index 341ce129..5de7150e 100644 --- a/src/system.hh.in +++ b/src/system.hh.in @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -136,8 +136,7 @@ typedef std::ostream::pos_type ostream_pos_type; #include <editline/readline.h> #endif -#include <boost/algorithm/string/classification.hpp> -#include <boost/algorithm/string/predicate.hpp> +#include <boost/algorithm/string.hpp> #include <boost/bind.hpp> #include <boost/cast.hpp> #include <boost/current_function.hpp> diff --git a/src/temps.cc b/src/temps.cc index fd099e9a..68b9ffa0 100644 --- a/src/temps.cc +++ b/src/temps.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/temps.h b/src/temps.h index 4243079c..ac6d08cd 100644 --- a/src/temps.h +++ b/src/temps.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/textual.cc b/src/textual.cc index 8953d2b8..dfca7943 100644 --- a/src/textual.cc +++ b/src/textual.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -144,6 +144,8 @@ namespace { void end_directive(char * line); void alias_directive(char * line); void fixed_directive(char * line); + void payee_mapping_directive(char * line); + void account_mapping_directive(char * line); void tag_directive(char * line); void define_directive(char * line); bool general_directive(char * line); @@ -663,14 +665,42 @@ void instance_t::include_directive(char * line) filename = resolve_path(filename); DEBUG("textual.include", "resolved path: " << filename.string()); - if (! exists(filename)) + mask_t glob; +#if BOOST_VERSION >= 103700 + path parent_path = filename.parent_path(); + glob.assign_glob(filename.filename()); +#else // BOOST_VERSION >= 103700 + path parent_path = filename.branch_path(); + glob.assign_glob(filename.leaf()); +#endif // BOOST_VERSION >= 103700 + + bool files_found = false; + if (exists(parent_path)) { + filesystem::directory_iterator end; + for (filesystem::directory_iterator iter(parent_path); + iter != end; + ++iter) { + if (is_regular_file(*iter)) { +#if BOOST_VERSION >= 103700 + string base = (*iter).filename(); +#else // BOOST_VERSION >= 103700 + string base = (*iter).leaf(); +#endif // BOOST_VERSION >= 103700 + if (glob.match(base)) { + path inner_file(*iter); + ifstream stream(inner_file); + instance_t instance(context, stream, master, &inner_file, this); + instance.parse(); + files_found = true; + } + } + } + } + + if (! files_found) throw_(std::runtime_error, _("File to include was not found: '%1'") << filename); - ifstream stream(filename); - - instance_t instance(context, stream, master, &filename, this); - instance.parse(); } void instance_t::master_account_directive(char * line) @@ -735,6 +765,52 @@ void instance_t::fixed_directive(char * line) } } +void instance_t::payee_mapping_directive(char * line) +{ + char * payee = skip_ws(line); + char * regex = next_element(payee, true); + + if (regex) + context.journal.payee_mappings.push_back + (payee_mapping_t(mask_t(regex), payee)); + + while (peek_whitespace_line()) { + std::streamsize len = read_line(line); + assert(len > 0); + + regex = skip_ws(line); + if (! *regex) + break; + + context.journal.payee_mappings.push_back + (payee_mapping_t(mask_t(regex), payee)); + } +} + +void instance_t::account_mapping_directive(char * line) +{ + char * account_name = skip_ws(line); + char * payee_regex = next_element(account_name, true); + + if (payee_regex) + context.journal.account_mappings.push_back + (account_mapping_t(mask_t(payee_regex), + context.top_account()->find_account(account_name))); + + while (peek_whitespace_line()) { + std::streamsize len = read_line(line); + assert(len > 0); + + payee_regex = skip_ws(line); + if (! *payee_regex) + break; + + context.journal.account_mappings.push_back + (account_mapping_t(mask_t(payee_regex), + context.top_account()->find_account(account_name))); + } +} + void instance_t::tag_directive(char * line) { string tag(trim_ws(line)); @@ -782,6 +858,13 @@ bool instance_t::general_directive(char * line) } break; + case 'c': + if (std::strcmp(p, "capture") == 0) { + account_mapping_directive(arg); + return true; + } + break; + case 'd': if (std::strcmp(p, "def") == 0 || std::strcmp(p, "define") == 0) { define_directive(arg); @@ -810,6 +893,13 @@ bool instance_t::general_directive(char * line) } break; + case 'p': + if (std::strcmp(p, "payee") == 0) { + payee_mapping_directive(arg); + return true; + } + break; + case 't': if (std::strcmp(p, "tag") == 0) { tag_directive(arg); @@ -929,6 +1019,15 @@ post_t * instance_t::parse_post(char * line, post->account->add_flags(ACCOUNT_KNOWN); } + if (post->account->name == _("Unknown")) { + foreach (account_mapping_t& value, context.journal.account_mappings) { + if (value.first.match(xact->payee)) { + post->account = value.second; + break; + } + } + } + // Parse the optional amount bool saw_amount = false; @@ -988,6 +1087,7 @@ post_t * instance_t::parse_post(char * line, if (*++next == '@') { per_unit = false; + post->add_flags(POST_COST_IN_FULL); DEBUG("textual.parse", "line " << linenum << ": " << "And it's for a total price"); } @@ -1127,7 +1227,7 @@ post_t * instance_t::parse_post(char * line, // Parse the optional note if (next && *next == ';') { - post->append_note(++next, current_year); + post->append_note(++next, true, current_year); next = line + len; DEBUG("textual.parse", "line " << linenum << ": " << "Parsed a posting note"); @@ -1146,7 +1246,7 @@ post_t * instance_t::parse_post(char * line, if (! context.state_stack.empty()) { foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) - post->parse_tags(boost::get<string>(state).c_str()); + post->parse_tags(boost::get<string>(state).c_str(), true); } TRACE_STOP(post_details, 1); @@ -1242,7 +1342,14 @@ xact_t * instance_t::parse_xact(char * line, if (next && *next) { char * p = next_element(next, true); - xact->payee = next; + foreach (payee_mapping_t& value, context.journal.payee_mappings) { + if (value.first.match(next)) { + xact->payee = value.second; + break; + } + } + if (xact->payee.empty()) + xact->payee = next; next = p; } else { xact->payee = _("<Unspecified payee>"); @@ -1276,7 +1383,7 @@ xact_t * instance_t::parse_xact(char * line, item = xact.get(); // This is a trailing note, and possibly a metadata info tag - item->append_note(p + 1, current_year); + item->append_note(p + 1, true, current_year); item->pos->end_pos = curr_pos; item->pos->end_line++; } else { @@ -1310,7 +1417,7 @@ xact_t * instance_t::parse_xact(char * line, if (! context.state_stack.empty()) { foreach (const state_t& state, context.state_stack) if (state.type() == typeid(string)) - xact->parse_tags(boost::get<string>(state).c_str()); + xact->parse_tags(boost::get<string>(state).c_str(), false); } TRACE_STOP(xact_details, 1); diff --git a/src/timelog.cc b/src/timelog.cc index 25bf8e94..217aa581 100644 --- a/src/timelog.cc +++ b/src/timelog.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/timelog.h b/src/timelog.h index 83256dfa..caa23ec0 100644 --- a/src/timelog.h +++ b/src/timelog.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/times.cc b/src/times.cc index 963639f1..be488baf 100644 --- a/src/times.cc +++ b/src/times.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/times.h b/src/times.h index 826937bb..48a9dd2a 100644 --- a/src/times.h +++ b/src/times.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/token.cc b/src/token.cc index 81c54a82..a34cdcd0 100644 --- a/src/token.cc +++ b/src/token.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/token.h b/src/token.h index 670f16e3..8d70996b 100644 --- a/src/token.h +++ b/src/token.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/unistring.h b/src/unistring.h index 7c433d9d..d42f2b28 100644 --- a/src/unistring.h +++ b/src/unistring.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -55,6 +55,8 @@ namespace ledger { class unistring { public: + static const std::size_t npos = static_cast<std::size_t>(-1); + std::vector<boost::uint32_t> utf32chars; unistring() { @@ -96,6 +98,23 @@ public: return utf8result; } + + std::size_t find(const boost::uint32_t __s, std::size_t __pos = 0) const { + std::size_t idx = 0; + foreach (const boost::uint32_t& ch, utf32chars) { + if (idx >= __pos && ch == __s) + return idx; + idx++; + } + return npos; + } + + boost::uint32_t& operator[](const std::size_t index) { + return utf32chars[index]; + } + const boost::uint32_t& operator[](const std::size_t index) const { + return utf32chars[index]; + } }; inline void justify(std::ostream& out, diff --git a/src/utils.cc b/src/utils.cc index f2460ba1..c3fecca0 100644 --- a/src/utils.cc +++ b/src/utils.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/src/utils.h b/src/utils.h index ab8fb495..a0c3f49f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -659,6 +659,16 @@ inline string to_hex(uint_least32_t * message_digest, const int len = 1) return buf.str(); } +inline string sha1sum(const string& str) +{ + SHA1 sha; + sha.Reset(); + sha << str.c_str(); + uint_least32_t message_digest[5]; + sha.Result(message_digest); + return to_hex(message_digest, 5); +} + class push_xml { std::ostream& out; diff --git a/src/value.cc b/src/value.cc index 7d079caf..54798162 100644 --- a/src/value.cc +++ b/src/value.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -857,12 +857,12 @@ bool value_t::is_less_than(const value_t& val) const case INTEGER: return as_amount() < val.as_long(); case AMOUNT: - try { + if (as_amount().commodity() == val.as_amount().commodity() || + ! as_amount().has_commodity() || + ! val.as_amount().has_commodity()) return as_amount() < val.as_amount(); - } - catch (const amount_error&) { + else return commodity_t::compare_by_commodity()(&as_amount(), &val.as_amount()); - } default: break; } diff --git a/src/value.h b/src/value.h index ffbb89e8..1c1d8b6c 100644 --- a/src/value.h +++ b/src/value.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -919,11 +919,11 @@ public: /** * Printing methods. */ - void print(std::ostream& out, - const int first_width = -1, - const int latter_width = -1, - const bool right_justify = false, - const bool colorize = false) const; + void print(std::ostream& out, + const int first_width = -1, + const int latter_width = -1, + const bool right_justify = false, + const bool colorize = false) const; void dump(std::ostream& out, const bool relaxed = true) const; /** diff --git a/src/xact.cc b/src/xact.cc index 623c5772..1a022387 100644 --- a/src/xact.cc +++ b/src/xact.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -454,12 +454,7 @@ string xact_t::idstring() const string xact_t::id() const { - SHA1 sha; - sha.Reset(); - sha << idstring().c_str(); - uint_least32_t message_digest[5]; - sha.Result(message_digest); - return to_hex(message_digest, 5); + return sha1sum(idstring()); } namespace { @@ -760,7 +755,7 @@ void to_xml(std::ostream& out, const xact_t& xact) if (xact.metadata) { push_xml y(out, "metadata"); foreach (const item_t::string_map::value_type& pair, *xact.metadata) { - if (pair.second) { + if (pair.second.first) { push_xml z(out, "variable"); { push_xml w(out, "key"); @@ -768,7 +763,7 @@ void to_xml(std::ostream& out, const xact_t& xact) } { push_xml w(out, "value"); - out << y.guard(*pair.second); + out << y.guard(*pair.second.first); } } else { push_xml z(out, "tag"); @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003-2009, John Wiegley. All rights reserved. + * Copyright (c) 2003-2010, John Wiegley. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are |