diff options
author | John Wiegley <johnw@newartisans.com> | 2008-08-05 13:17:04 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2008-08-05 18:05:49 -0400 |
commit | f6f4a46cf5b14f9a2170cd6475958efbf320caec (patch) | |
tree | 05bc1defcdebc201de3dd10477483d906a842821 /textual.cc | |
parent | b7970b29855563e4c67f85af8b31233eda80c22a (diff) | |
download | fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.gz fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.tar.bz2 fork-ledger-f6f4a46cf5b14f9a2170cd6475958efbf320caec.zip |
Moved around most of the files so that source code is in src/, documentation
is in doc/, etc.
Diffstat (limited to 'textual.cc')
-rw-r--r-- | textual.cc | 1131 |
1 files changed, 0 insertions, 1131 deletions
diff --git a/textual.cc b/textual.cc deleted file mode 100644 index e46ae22a..00000000 --- a/textual.cc +++ /dev/null @@ -1,1131 +0,0 @@ -/* - * Copyright (c) 2003-2008, 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. - */ - -#if defined(__GNUG__) && __GNUG__ < 3 -#define _XOPEN_SOURCE -#endif - -#include "textual.h" -#include "expr.h" -#include "parser.h" -#include "session.h" -#include "option.h" -#include "acconf.h" - -#define TIMELOG_SUPPORT 1 - -namespace ledger { - -#define MAX_LINE 1024 - -static path pathname; -static unsigned int linenum; -static unsigned int src_idx; -static accounts_map account_aliases; - -static std::list<std::pair<path, int> > include_stack; - -#ifdef TIMELOG_SUPPORT -struct time_entry_t -{ - datetime_t checkin; - account_t * account; - string desc; - - time_entry_t() : account(NULL) { - TRACE_CTOR(time_entry_t, ""); - } - time_entry_t(const datetime_t& _checkin, - account_t * _account = NULL, - const string& _desc = "") - : checkin(_checkin), account(_account), desc(_desc) { - TRACE_CTOR(time_entry_t, "const datetime_t&, account_t *, const string&"); - } - time_entry_t(const time_entry_t& entry) - : checkin(entry.checkin), account(entry.account), - desc(entry.desc) { - TRACE_CTOR(time_entry_t, "copy"); - } - ~time_entry_t() throw() { - TRACE_DTOR(time_entry_t); - } -}; -#endif - -namespace { - optional<expr_t> parse_amount_expr(std::istream& in, - amount_t& amount, - xact_t * xact, - unsigned short flags = 0) - { - expr_t expr(in, flags | EXPR_PARSE_PARTIAL); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed an amount expression"); - -#ifdef DEBUG_ENABLED - DEBUG_IF("ledger.textual.parse") { - if (_debug_stream) { - ledger::dump_value_expr(*_debug_stream, expr); - *_debug_stream << std::endl; - } - } -#endif - - if (expr) { - amount = expr.calc(*xact).as_amount(); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "The transaction amount is " << amount); - return expr; - } - return none; - } -} - -xact_t * parse_xact(char * line, account_t * account, entry_t * entry = NULL) -{ - std::istringstream in(line); - - string err_desc; - try { - - // The account will be determined later... - std::auto_ptr<xact_t> xact(new xact_t(NULL)); - if (entry) - xact->entry = entry; - - // Parse the state flag - - char p = peek_next_nonws(in); - switch (p) { - case '*': - xact->state = xact_t::CLEARED; - in.get(p); - p = peek_next_nonws(in); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed the CLEARED flag"); - break; - case '!': - xact->state = xact_t::PENDING; - in.get(p); - p = peek_next_nonws(in); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed the PENDING flag"); - break; - } - - // Parse the account name - - unsigned long account_beg = static_cast<unsigned long>(in.tellg()); - unsigned long account_end = account_beg; - while (! in.eof()) { - in.get(p); - if (in.eof() || (std::isspace(p) && - (p == '\t' || in.peek() == EOF || - std::isspace(in.peek())))) - break; - account_end++; - } - - if (account_beg == account_end) - throw parse_error("No account was specified"); - - char * b = &line[account_beg]; - char * e = &line[account_end]; - if ((*b == '[' && *(e - 1) == ']') || - (*b == '(' && *(e - 1) == ')')) { - xact->add_flags(XACT_VIRTUAL); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a virtual account name"); - if (*b == '[') { - xact->add_flags(XACT_BALANCE); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a balanced virtual account name"); - } - b++; e--; - } - - string name(b, e - b); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed account name " << name); - if (account_aliases.size() > 0) { - accounts_map::const_iterator i = account_aliases.find(name); - if (i != account_aliases.end()) - xact->account = (*i).second; - } - if (! xact->account) - xact->account = account->find_account(name); - - // Parse the optional amount - - bool saw_amount = false; - - if (in.good() && ! in.eof()) { - p = peek_next_nonws(in); - if (in.eof()) - goto finished; - if (p == ';') - goto parse_note; - if (p == '=' && entry) - goto parse_assign; - - try { - unsigned long beg = static_cast<unsigned long>(in.tellg()); - - xact->amount_expr = - parse_amount_expr(in, xact->amount, xact.get(), - EXPR_PARSE_NO_REDUCE | EXPR_PARSE_NO_ASSIGN); - saw_amount = true; - - if (! xact->amount.is_null()) { - xact->amount.reduce(); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Reduced amount is " << xact->amount); - } - - // We don't need to store the actual expression that resulted in the - // amount if it's constant - if (xact->amount_expr) { - if (xact->amount_expr->is_constant()) - xact->amount_expr = expr_t(); - - unsigned long end = static_cast<unsigned long>(in.tellg()); - xact->amount_expr->set_text(string(line, beg, end - beg)); - } - } - catch (const std::exception& err) { - add_error_context("While parsing transaction amount:\n"); - throw err; - } - } - - // Parse the optional cost (@ PER-UNIT-COST, @@ TOTAL-COST) - - if (in.good() && ! in.eof()) { - p = peek_next_nonws(in); - if (p == '@') { - if (! saw_amount) - throw parse_error - ("Transaction cannot have a cost expression with an amount"); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Found a price indicator"); - bool per_unit = true; - in.get(p); - if (in.peek() == '@') { - in.get(p); - per_unit = false; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "And it's for a total price"); - } - - if (in.good() && ! in.eof()) { - xact->cost = amount_t(); - - try { - unsigned long beg = static_cast<unsigned long>(in.tellg()); - - xact->cost_expr = - parse_amount_expr(in, *xact->cost, xact.get(), - EXPR_PARSE_NO_MIGRATE | - EXPR_PARSE_NO_ASSIGN); - - if (xact->cost_expr) { - unsigned long end = static_cast<unsigned long>(in.tellg()); - if (per_unit) - xact->cost_expr->set_text(string("@") + - string(line, beg, end - beg)); - else - xact->cost_expr->set_text(string("@@") + - string(line, beg, end - beg)); - } - } - catch (const std::exception& err) { - add_error_context("While parsing transaction cost:\n"); - throw err; - } - - if (xact->cost->sign() < 0) - throw parse_error("A transaction's cost may not be negative"); - - amount_t per_unit_cost(*xact->cost); - if (per_unit) - *xact->cost *= xact->amount; - else - per_unit_cost /= xact->amount; - - if (xact->amount.commodity() && - ! xact->amount.commodity().annotated) { - if (xact->entry) - xact->amount.annotate(annotation_t(per_unit_cost, - xact->entry->actual_date(), - xact->entry->code)); - else - xact->amount.annotate(annotation_t(per_unit_cost)); - } - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Total cost is " << *xact->cost); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Per-unit cost is " << per_unit_cost); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Annotated amount is " << xact->amount); - } - } - } - - parse_assign: - if (entry != NULL) { - // Add this amount to the related account now - - account_t::xdata_t& xdata(xact->account->xdata()); - - if (! xact->amount.is_null()) { - if (xdata.value.is_null()) - xdata.value = xact->amount; - else - xdata.value += xact->amount; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "XACT assign: account total = " << xdata.value); - } - - // Parse the optional assigned (= AMOUNT) - - if (in.good() && ! in.eof()) { - p = peek_next_nonws(in); - if (p == '=') { - in.get(p); - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Found a balance assignment indicator"); - if (in.good() && ! in.eof()) { - amount_t amt; - - try { - unsigned long beg = static_cast<unsigned long>(in.tellg()); - - optional<expr_t> total_expr = - parse_amount_expr(in, amt, xact.get(), EXPR_PARSE_NO_MIGRATE); - - if (amt.is_null()) - throw parse_error - ("An assigned balance must evaluate to a constant value"); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "XACT assign: parsed amt = " << amt); - - if (total_expr) { - unsigned long end = static_cast<unsigned long>(in.tellg()); - total_expr->set_text(string("=") + - string(line, beg, end - beg)); - } - - // jww (2008-08-02): Save total_expr somewhere! - - amount_t diff; - if (xdata.value.is_amount()) { - diff = amt - xdata.value.as_amount(); - } - else if (xdata.value.is_balance()) { - optional<amount_t> comm_bal = - xdata.value.as_balance().commodity_amount(amt.commodity()); - diff = amt - (comm_bal ? *comm_bal : amount_t(0L)); - } - else if (xdata.value.is_balance_pair()) { - optional<amount_t> comm_bal = - xdata.value.as_balance_pair().commodity_amount(amt.commodity()); - diff = amt - (comm_bal ? *comm_bal : amount_t(0L)); - } - else { - diff = amt; - } - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "XACT assign: diff = " << diff); - - if (! diff.is_realzero()) { - if (! xact->amount.is_null()) { - xact_t * temp = - new xact_t(xact->account, diff, - XACT_GENERATED | XACT_CALCULATED); - entry->add_xact(temp); - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Created balancing transaction"); - } else { - xact->amount = diff; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Overwrite null transaction"); - } - xdata.value = amt; - } - } - catch (const std::exception& err) { - add_error_context("While parsing assigned balance:\n"); - throw err; - } - } - } - } - } - - // Parse the optional note - - parse_note: - if (in.good() && ! in.eof()) { - p = peek_next_nonws(in); - if (p == ';') { - in.get(p); - p = peek_next_nonws(in); - xact->note = &line[static_cast<unsigned long>(in.tellg())]; - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a note '" << *xact->note << "'"); - - if (char * b = std::strchr(xact->note->c_str(), '[')) - if (char * e = std::strchr(xact->note->c_str(), ']')) { - char buf[256]; - std::strncpy(buf, b + 1, e - b - 1); - buf[e - b - 1] = '\0'; - - DEBUG("ledger.textual.parse", "line " << linenum << ": " << - "Parsed a transaction date " << buf); - - if (char * p = std::strchr(buf, '=')) { - *p++ = '\0'; - xact->_date_eff = parse_date(p); - } - if (buf[0]) - xact->_date = parse_date(buf); - } - } - } - - finished: - return xact.release(); - - } - catch (const std::exception& err) { - add_error_context("While parsing transaction:\n"); - add_error_context(line_context(line, static_cast<unsigned long>(in.tellg()) - 1)); - throw err; - } -} - -bool parse_xacts(std::istream& in, - account_t * account, - entry_base_t& entry, - const string& kind, - unsigned long beg_pos) -{ - TRACE_START(entry_xacts, 1, "Time spent parsing transactions:"); - - static char line[MAX_LINE + 1]; - bool added = false; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { - in.getline(line, MAX_LINE); - if (in.eof()) - break; - - int len = std::strlen(line); - if (line[len - 1] == '\r') - line[--len] = '\0'; - - beg_pos += len + 1; - linenum++; - - if (line[0] == ' ' || line[0] == '\t') { - char * p = skip_ws(line); - if (! *p) - break; - } - if (xact_t * xact = parse_xact(line, account)) { - entry.add_xact(xact); - added = true; - } - } - - TRACE_STOP(entry_xacts, 1); - - return added; -} - -entry_t * parse_entry(std::istream& in, char * line, account_t * master, - textual_parser_t& parser, unsigned long& pos) -{ - TRACE_START(entry_text, 1, "Time spent preparing entry text:"); - - std::auto_ptr<entry_t> curr(new entry_t); - - // Parse the date - - char * next = next_element(line); - - if (char * p = std::strchr(line, '=')) { - *p++ = '\0'; - curr->_date_eff = parse_date(p); - } - curr->_date = parse_date(line); - - // Parse the optional cleared flag: * - - xact_t::state_t state = xact_t::UNCLEARED; - if (next) { - switch (*next) { - case '*': - state = xact_t::CLEARED; - next = skip_ws(++next); - break; - case '!': - state = xact_t::PENDING; - next = skip_ws(++next); - break; - } - } - - // Parse the optional code: (TEXT) - - if (next && *next == '(') { - if (char * p = std::strchr(next++, ')')) { - *p++ = '\0'; - curr->code = next; - next = skip_ws(p); - } - } - - // Parse the description text - - curr->payee = next ? next : "<Unspecified payee>"; - - TRACE_STOP(entry_text, 1); - - // Parse all of the xacts associated with this entry - - TRACE_START(entry_details, 1, "Time spent parsing entry details:"); - - unsigned long end_pos; - unsigned long beg_line = linenum; - - while (! in.eof() && (in.peek() == ' ' || in.peek() == '\t')) { - unsigned long beg_pos = static_cast<unsigned long>(in.tellg()); - - line[0] = '\0'; - in.getline(line, MAX_LINE); - if (in.eof() && line[0] == '\0') - break; - - int len = std::strlen(line); - if (line[len - 1] == '\r') - line[--len] = '\0'; - - end_pos = beg_pos + len + 1; - linenum++; - - if (line[0] == ' ' || line[0] == '\t') { - char * p = skip_ws(line); - if (! *p) - break; - } - - if (xact_t * xact = parse_xact(line, master, curr.get())) { - if (state != xact_t::UNCLEARED && - xact->state == xact_t::UNCLEARED) - xact->state = state; - - xact->beg_pos = beg_pos; - xact->beg_line = beg_line; - xact->end_pos = end_pos; - xact->end_line = linenum; - pos = end_pos; - - curr->add_xact(xact); - } - - if (in.eof()) - break; - } - - TRACE_STOP(entry_details, 1); - - return curr.release(); -} - -static inline void parse_symbol(char *& p, string& symbol) -{ - if (*p == '"') { - char * q = std::strchr(p + 1, '"'); - if (! q) - throw parse_error("Quoted commodity symbol lacks closing quote"); - symbol = string(p + 1, 0, q - p - 1); - p = q + 2; - } else { - char * q = next_element(p); - symbol = p; - if (q) - p = q; - else - p += symbol.length(); - } - if (symbol.empty()) - throw parse_error("Failed to parse commodity"); -} - -bool textual_parser_t::test(std::istream& in) const -{ - char buf[5]; - - in.read(buf, 5); - if (std::strncmp(buf, "<?xml", 5) == 0) { -#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE) - throw parse_error("Ledger file contains XML data, but format was not recognized"); -#else - throw parse_error("Ledger file contains XML data, but no XML support present"); -#endif - } - - in.clear(); - in.seekg(0, std::ios::beg); - assert(in.good()); - return true; -} - -static void clock_out_from_timelog(std::list<time_entry_t>& time_entries, - const datetime_t& when, - account_t * account, - const char * desc, - journal_t& journal) -{ - time_entry_t event; - - if (time_entries.size() == 1) { - event = time_entries.back(); - time_entries.clear(); - } - else if (time_entries.empty()) { - throw parse_error("Timelog check-out event without a check-in"); - } - else if (! account) { - throw parse_error - ("When multiple check-ins are active, checking out requires an account"); - } - else { - bool found = false; - - for (std::list<time_entry_t>::iterator i = time_entries.begin(); - i != time_entries.end(); - i++) - if (account == (*i).account) { - event = *i; - found = true; - time_entries.erase(i); - break; - } - - if (! found) - throw parse_error - ("Timelog check-out event does not match any current check-ins"); - } - - if (desc && event.desc.empty()) { - event.desc = desc; - desc = NULL; - } - - std::auto_ptr<entry_t> curr(new entry_t); - curr->_date = when.date(); - curr->code = desc ? desc : ""; - curr->payee = event.desc; - - if (when < event.checkin) - throw parse_error - ("Timelog check-out date less than corresponding check-in"); - - char buf[32]; - std::sprintf(buf, "%lds", long((when - event.checkin).seconds())); - amount_t amt; - amt.parse(buf); - assert(amt.valid()); - - xact_t * xact - = new xact_t(event.account, amt, XACT_VIRTUAL); - xact->state = xact_t::CLEARED; - curr->add_xact(xact); - - if (! journal.add_entry(curr.get())) - throw parse_error("Failed to record 'out' timelog entry"); - else - curr.release(); -} - -unsigned int textual_parser_t::parse(std::istream& in, - session_t& session, - journal_t& journal, - account_t * master, - const path * original_file) -{ - TRACE_START(parsing_total, 1, "Total time spent parsing text:"); - - static bool added_auto_entry_hook = false; - static char line[MAX_LINE + 1]; - unsigned int count = 0; - unsigned int errors = 0; - - std::list<account_t *> account_stack; - auto_entry_finalizer_t auto_entry_finalizer(&journal); - std::list<time_entry_t> time_entries; - - if (! master) - master = journal.master; - - account_stack.push_front(master); - - pathname = journal.sources.back(); - src_idx = journal.sources.size() - 1; - linenum = 1; - - INFO("Parsing file '" << pathname.string() << "'"); - - unsigned long beg_pos = static_cast<unsigned long>(in.tellg()); - unsigned long end_pos; - unsigned long beg_line = linenum; - - while (in.good() && ! in.eof()) { - try { - in.getline(line, MAX_LINE); - if (in.eof()) - break; - - int len = std::strlen(line); - if (line[len - 1] == '\r') - line[--len] = '\0'; - - end_pos = beg_pos + len + 1; - linenum++; - - switch (line[0]) { - case '\0': - break; - - case ' ': - case '\t': { - char * p = skip_ws(line); - if (*p) - throw parse_error("Line begins with whitespace"); - break; - } - -#ifdef TIMELOG_SUPPORT - case 'i': - case 'I': { - string date(line, 2, 19); - - char * p = skip_ws(line + 22); - char * n = next_element(p, true); - - time_entry_t event(parse_datetime(date), - account_stack.front()->find_account(p), n ? n : ""); - - if (! time_entries.empty()) - foreach (time_entry_t& time_entry, time_entries) - if (event.account == time_entry.account) - throw parse_error("Cannot double check-in to the same account"); - - time_entries.push_back(event); - break; - } - - case 'o': - case 'O': - if (time_entries.empty()) { - throw parse_error("Timelog check-out event without a check-in"); - } else { - string date(line, 2, 19); - - char * p = skip_ws(line + 22); - char * n = next_element(p, true); - - clock_out_from_timelog - (time_entries, parse_datetime(date), - p ? account_stack.front()->find_account(p) : NULL, n, journal); - count++; - } - break; -#endif // TIMELOG_SUPPORT - - case 'D': { // a default commodity for "entry" - amount_t amt(skip_ws(line + 1)); - assert(amt.valid()); - amount_t::current_pool->default_commodity = &amt.commodity(); - break; - } - - case 'A': // a default account for unbalanced xacts - journal.basket = - account_stack.front()->find_account(skip_ws(line + 1)); - break; - - case 'C': // a set of conversions - if (char * p = std::strchr(line + 1, '=')) { - *p++ = '\0'; - // jww (2008-04-22): NYI! -#if 0 - parse_conversion(line + 1, p); -#endif - } - break; - - case 'P': { // a pricing entry - char * date_field_ptr = skip_ws(line + 1); - char * time_field_ptr = next_element(date_field_ptr); - if (! time_field_ptr) break; - string date_field = date_field_ptr; - - char * symbol_and_price; - datetime_t datetime; - - if (std::isdigit(time_field_ptr[0])) { - symbol_and_price = next_element(time_field_ptr); - if (! symbol_and_price) break; - datetime = parse_datetime(date_field + " " + time_field_ptr); - } else { - symbol_and_price = time_field_ptr; - datetime = parse_datetime(date_field); - } - - string symbol; - parse_symbol(symbol_and_price, symbol); - amount_t price(symbol_and_price); - assert(price.valid()); - - if (commodity_t * commodity = - amount_t::current_pool->find_or_create(symbol)) - commodity->add_price(datetime, price); - break; - } - - case 'N': { // don't download prices - char * p = skip_ws(line + 1); - string symbol; - parse_symbol(p, symbol); - - if (commodity_t * commodity = - amount_t::current_pool->find_or_create(symbol)) - commodity->add_flags(COMMODITY_STYLE_NOMARKET); - break; - } - - case 'Y': // set the current year - current_year = std::atoi(skip_ws(line + 1)); - break; - -#ifdef TIMELOG_SUPPORT - case 'h': - case 'b': -#endif - case '*': // comment line - case ';': // comment line - break; - - case '-': { // option setting - char * p = next_element(line); - if (! p) { - p = std::strchr(line, '='); - if (p) - *p++ = '\0'; - } - process_option(line + 2, session, p); - break; - } - - case '=': { // automated entry - if (! added_auto_entry_hook) { - journal.add_entry_finalizer(&auto_entry_finalizer); - added_auto_entry_hook = true; - } - - auto_entry_t * ae = new auto_entry_t(skip_ws(line + 1)); - if (parse_xacts(in, account_stack.front(), *ae, - "automated", end_pos)) { - journal.auto_entries.push_back(ae); - ae->src_idx = src_idx; - ae->beg_pos = beg_pos; - ae->beg_line = beg_line; - ae->end_pos = end_pos; - ae->end_line = linenum; - } - break; - } - - case '~': { // period entry - period_entry_t * pe = new period_entry_t(skip_ws(line + 1)); - if (! pe->period) - throw_(parse_error, "Parsing time period '" << line << "'"); - - if (parse_xacts(in, account_stack.front(), *pe, - "period", end_pos)) { - if (pe->finalize()) { - extend_entry_base(&journal, *pe, true); - journal.period_entries.push_back(pe); - pe->src_idx = src_idx; - pe->beg_pos = beg_pos; - pe->beg_line = beg_line; - pe->end_pos = end_pos; - pe->end_line = linenum; - } else { - throw new parse_error("Period entry failed to balance"); - } - } - break; - } - - case '@': - case '!': { // directive - char * p = next_element(line); - string word(line + 1); - if (word == "include") { - push_variable<path> save_pathname(pathname); - push_variable<unsigned int> save_src_idx(src_idx); - push_variable<unsigned long> save_beg_pos(beg_pos); - push_variable<unsigned long> save_end_pos(end_pos); - push_variable<unsigned int> save_linenum(linenum); - - pathname = p; -#if 0 - if (pathname[0] != '/' && pathname[0] != '\\' && pathname[0] != '~') { - string::size_type pos = save_pathname.prev.rfind('/'); - if (pos == string::npos) - pos = save_pathname.prev.rfind('\\'); - if (pos != string::npos) - pathname = string(save_pathname.prev, 0, pos + 1) + pathname; - } - pathname = resolve_path(pathname); - - DEBUG("ledger.textual.include", "line " << linenum << ": " << - "Including path '" << pathname << "'"); - - include_stack.push_back(std::pair<path, int> - (journal.sources.back(), linenum - 1)); - count += parse_journal_file(pathname, config, journal, - account_stack.front()); - include_stack.pop_back(); -#endif - } - else if (word == "account") { - account_t * acct; - acct = account_stack.front()->find_account(p); - account_stack.push_front(acct); - } - else if (word == "end") { - account_stack.pop_front(); - } - else if (word == "alias") { - char * b = p; - if (char * e = std::strchr(b, '=')) { - char * z = e - 1; - while (std::isspace(*z)) - *z-- = '\0'; - *e++ = '\0'; - e = skip_ws(e); - - // Once we have an alias name (b) and the target account - // name (e), add a reference to the account in the - // `account_aliases' map, which is used by the xact - // parser to resolve alias references. - account_t * acct = account_stack.front()->find_account(e); - std::pair<accounts_map::iterator, bool> result - = account_aliases.insert(accounts_map::value_type(b, acct)); - assert(result.second); - } - } - else if (word == "def") { - expr_t def(p); - def.compile(session); // causes definitions to be established - } - break; - } - - default: { - unsigned long pos = beg_pos; - TRACE_START(entries, 1, "Time spent handling entries:"); - if (entry_t * entry = - parse_entry(in, line, account_stack.front(), *this, pos)) { - if (journal.add_entry(entry)) { - entry->src_idx = src_idx; - entry->beg_pos = beg_pos; - entry->beg_line = beg_line; - entry->end_pos = pos; - entry->end_line = linenum; - count++; - } else { - checked_delete(entry); - throw parse_error("Entry does not balance"); - } - } else { - throw parse_error("Failed to parse entry"); - } - end_pos = pos; - TRACE_STOP(entries, 1); - break; - } - } - } - catch (const std::exception& err) { - for (std::list<std::pair<path, int> >::reverse_iterator i = - include_stack.rbegin(); - i != include_stack.rend(); - i++) { - add_error_context("In file included from "); -#if 0 - add_error_context(include_context((*i).first, (*i).second)); -#endif - } - add_error_context(file_context(pathname, linenum - 1)); - - std::cout.flush(); - std::cerr << "Error: " << error_context() << err.what() - << std::endl; - errors++; - } - beg_pos = end_pos; - } - - if (! time_entries.empty()) { - std::list<account_t *> accounts; - - foreach (time_entry_t& time_entry, time_entries) - accounts.push_back(time_entry.account); - - foreach (account_t * account, accounts) - clock_out_from_timelog(time_entries, current_time, account, NULL, - journal); - - assert(time_entries.empty()); - } - - if (added_auto_entry_hook) - journal.remove_entry_finalizer(&auto_entry_finalizer); - - if (errors > 0) - throw static_cast<int>(errors); - - TRACE_STOP(parsing_total, 1); - - return count; -} - -void write_textual_journal(journal_t& journal, - const path& pathname, - xact_handler_ptr formatter, - const string& write_hdr_format, - std::ostream& out) -{ - unsigned long index = 0; - path found; - - if (pathname.empty()) { - if (! journal.sources.empty()) - found = *journal.sources.begin(); - } else { -#ifdef HAVE_REALPATH - char buf1[PATH_MAX]; - char buf2[PATH_MAX]; - - ::realpath(pathname.string().c_str(), buf1); - - foreach (const path& path, journal.sources) { - ::realpath(path.string().c_str(), buf2); - if (std::strcmp(buf1, buf2) == 0) { - found = path; - break; - } - index++; - } -#else - foreach (const path& path, journal.sources) { - if (pathname == path) { - found = path; - break; - } - index++; - } -#endif - } - - if (found.empty()) - throw_(std::runtime_error, - "Journal does not refer to file '" << pathname << "'"); - - entries_list::iterator el = journal.entries.begin(); - auto_entries_list::iterator al = journal.auto_entries.begin(); - period_entries_list::iterator pl = journal.period_entries.begin(); - - unsigned long pos = 0; - - format_t hdr_fmt(write_hdr_format); - boost::filesystem::ifstream in(found); - - while (! in.eof()) { - entry_base_t * base = NULL; - if (el != journal.entries.end() && pos == (*el)->beg_pos) { - hdr_fmt.format(out, **el); - base = *el++; - } - else if (al != journal.auto_entries.end() && pos == (*al)->beg_pos) { - out << "= " << (*al)->predicate.predicate.text() << '\n'; - base = *al++; - } - else if (pl != journal.period_entries.end() && pos == (*pl)->beg_pos) { - out << "~ " << (*pl)->period_string << '\n'; - base = *pl++; - } - - char c; - if (base) { - foreach (xact_t * xact, base->xacts) { - if (! xact->has_flags(XACT_AUTO)) { - xact->xdata().add_flags(XACT_EXT_TO_DISPLAY); - (*formatter)(*xact); - } - } - formatter->flush(); - - while (pos < base->end_pos) { - in.get(c); - pos = static_cast<unsigned long>(in.tellg()); // pos++; - } - } else { - in.get(c); - pos = static_cast<unsigned long>(in.tellg()); // pos++; - out.put(c); - } - } -} - -} // namespace ledger |