/* * Copyright (c) 2003-2009, 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 "session.h" #include "report.h" #include "handler.h" #include "iterators.h" #include "filters.h" #include "textual.h" namespace ledger { #if 0 boost::mutex session_t::session_mutex; #endif void set_session_context(session_t * session) { if (session) { #if 0 session_t::session_mutex.lock(); #endif amount_t::initialize(session->commodity_pool); // jww (2009-02-04): Is amount_t the right place for parse_conversion to // happen? amount_t::parse_conversion("1.0m", "60s"); amount_t::parse_conversion("1.0h", "60m"); value_t::initialize(); } else if (! session) { value_t::shutdown(); amount_t::shutdown(); #if 0 session_t::session_mutex.unlock(); #endif } } session_t::session_t() : next_data_file_from_command_line(false), saw_data_file_from_command_line(false), next_price_db_from_command_line(false), saw_price_db_from_command_line(false), register_format ("%-.9(display_date) %-.20(payee) %-.23(truncate(account, 23, 2)) %!12(print_balance(strip(amount_expr), 12, 67)) " "%!12(print_balance(strip(display_total), 12, 80, true))\n%/" "%31|%-.23(truncate(account, 23, 2)) %!12(print_balance(strip(amount_expr), 12, 67)) " "%!12(print_balance(strip(display_total), 12, 80, true))\n"), wide_register_format ("%-.9D %-.35P %-.39A %22.108t %!22.132T\n%/" "%48|%-.38A %22.108t %!22.132T\n"), print_format ("%(display_date)%(cleared ? \" *\" : (uncleared ? \"\" : \" !\"))%(code ? \" (\" + code + \")\" : \"\") %(payee)%(entry.comment | \"\")\n %-34(account) %12(amount)%(comment | \"\")\n%/ %-34(account) %12(amount)%(comment | \"\")\n%/\n"), balance_format ("%20(strip(display_total)) %(depth_spacer)%-(partial_account)\n"), equity_format ("\n%D %Y%C%P\n%/ %-34W %12t\n"), plot_amount_format ("%D %(S(t))\n"), plot_total_format ("%D %(S(T))\n"), write_hdr_format ("%d %Y%C%P\n"), write_xact_format (" %-34W %12o%n\n"), prices_format ("%[%Y/%m/%d %H:%M:%S %Z] %-10A %12t %12T\n"), pricesdb_format ("P %[%Y/%m/%d %H:%M:%S] %A %t\n"), pricing_leeway(24 * 3600), current_year(CURRENT_DATE().year()), download_quotes(false), #if 0 elision_style(ABBREVIATE), #endif abbrev_length(2), ansi_codes(false), ansi_invert(false), commodity_pool(new commodity_pool_t), master(new account_t(NULL, "")) { TRACE_CTOR(session_t, ""); optional home; if (const char * home_var = std::getenv("HOME")) home = home_var; init_file = home ? *home / ".ledgerrc" : "./.ledgerrc"; price_db = home ? *home / ".pricedb" : "./.pricedb"; register_parser(new textual_parser_t); // Add time commodity conversions, so that timelog's may be parsed // in terms of seconds, but reported as minutes or hours. if (commodity_t * commodity = commodity_pool->create("s")) { commodity->add_flags(COMMODITY_BUILTIN | COMMODITY_NOMARKET); } else { assert(false); } } session_t::~session_t() { TRACE_DTOR(session_t); } std::size_t session_t::read_journal(journal_t& journal, std::istream& in, const path& pathname, account_t * master) { if (! master) master = journal.master; foreach (journal_t::parser_t& parser, parsers) #if defined(TEST_FOR_PARSER) if (parser.test(in)) #endif return parser.parse(in, *this, journal, master, &pathname); return 0; } std::size_t session_t::read_journal(journal_t& journal, const path& pathname, account_t * master) { journal.sources.push_back(pathname); if (! exists(pathname)) throw_(std::logic_error, "Cannot read file" << pathname); ifstream stream(pathname); return read_journal(journal, stream, pathname, master); } void session_t::read_init() { if (init_file && exists(*init_file)) { TRACE_START(init, 1, "Read initialization file"); ifstream init(*init_file); journal_t temp; if (read_journal(temp, *init_file) > 0 || temp.auto_entries.size() > 0 || temp.period_entries.size() > 0) { throw_(parse_error, "Entries found in initialization file '" << init_file << "'"); } TRACE_FINISH(init, 1); } } std::size_t session_t::read_data(journal_t& journal, const string& master_account) { if (data_files.empty()) throw_(parse_error, "No journal file was specified (please use -f)"); TRACE_START(session_parser, 1, "Parsed journal file"); std::size_t entry_count = 0; if (entry_count == 0) { account_t * acct = journal.master; if (! master_account.empty()) acct = journal.find_account(master_account); journal.price_db = price_db; if (journal.price_db && exists(*journal.price_db)) { if (read_journal(journal, *journal.price_db)) { throw_(parse_error, "Entries not allowed in price history file"); } else { journal.sources.pop_back(); } } foreach (const path& pathname, data_files) { if (pathname == "-") { journal.sources.push_back("/dev/stdin"); // To avoid problems with stdin and pipes, etc., we read the entire // file in beforehand into a memory buffer, and then parcel it out // from there. std::ostringstream buffer; while (std::cin.good() && ! std::cin.eof()) { char line[8192]; std::cin.read(line, 8192); std::streamsize count = std::cin.gcount(); buffer.write(line, count); } buffer.flush(); std::istringstream buf_in(buffer.str()); entry_count += read_journal(journal, buf_in, "/dev/stdin", acct); } else if (exists(pathname)) { entry_count += read_journal(journal, pathname, acct); if (journal.price_db) journal.sources.push_back(*journal.price_db); } else { throw_(parse_error, "Could not open journal file '" << pathname << "'"); } } } VERIFY(journal.valid()); TRACE_STOP(session_parser, 1); return entry_count; } void session_t::clean_xacts() { session_xacts_iterator walker(*this); pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker); } void session_t::clean_xacts(entry_t& entry) { entry_xacts_iterator walker(entry); pass_down_xacts(xact_handler_ptr(new clear_xact_xdata), walker); } void session_t::clean_accounts() { basic_accounts_iterator acct_walker(*master); pass_down_accounts(acct_handler_ptr(new clear_account_xdata), acct_walker); master->clear_xdata(); } #if 0 value_t session_t::resolve(const string& name, expr_t::scope_t& locals) { const char * p = name.c_str(); switch (*p) { case 'd': #if 0 if (name == "date_format") { // jww (2007-04-18): What to do here? return string_value(moment_t::output_format); } #endif break; case 'n': switch (*++p) { case 'o': if (name == "now") return value_t(now); break; } break; case 'r': if (name == "register_format") return string_value(register_format); break; } return expr_t::scope_t::resolve(name, locals); } #endif expr_t::ptr_op_t session_t::lookup(const string& name) { const char * p = name.c_str(); switch (*p) { case 'b': if (std::strcmp(p, "balance_format") == 0) return expr_t::op_t::wrap_value(string_value(balance_format)); break; case 'e': if (std::strcmp(p, "equity_format") == 0) return expr_t::op_t::wrap_value(string_value(equity_format)); break; case 'p': if (std::strcmp(p, "plot_amount_format") == 0) return expr_t::op_t::wrap_value(string_value(plot_amount_format)); else if (std::strcmp(p, "plot_total_format") == 0) return expr_t::op_t::wrap_value(string_value(plot_total_format)); else if (std::strcmp(p, "prices_format") == 0) return expr_t::op_t::wrap_value(string_value(prices_format)); else if (std::strcmp(p, "pricesdb_format") == 0) return expr_t::op_t::wrap_value(string_value(pricesdb_format)); else if (std::strcmp(p, "print_format") == 0) return expr_t::op_t::wrap_value(string_value(print_format)); break; case 'r': if (std::strcmp(p, "register_format") == 0) return expr_t::op_t::wrap_value(string_value(register_format)); break; case 'w': if (std::strcmp(p, "wide_register_format") == 0) return expr_t::op_t::wrap_value(string_value(wide_register_format)); else if (std::strcmp(p, "write_hdr_format") == 0) return expr_t::op_t::wrap_value(string_value(write_hdr_format)); else if (std::strcmp(p, "write_xact_format") == 0) return expr_t::op_t::wrap_value(string_value(write_xact_format)); break; case 'o': if (std::strncmp(p, "opt_", 4) == 0) { p = p + 4; switch (*p) { case 'd': if (std::strcmp(p, "debug_") == 0) return MAKE_FUNCTOR(session_t::option_debug_); break; case 'f': if ((*(p + 1) == '_' && ! *(p + 2)) || std::strcmp(p, "file_") == 0) return MAKE_FUNCTOR(session_t::option_file_); break; case 'p': if (std::strcmp(p, "pager_") == 0) return MAKE_FUNCTOR(session_t::option_pager_); break; case 't': if (std::strcmp(p, "trace_") == 0) return MAKE_FUNCTOR(session_t::option_trace_); break; case 'v': if (! *(p + 1) || std::strcmp(p, "verbose") == 0) return MAKE_FUNCTOR(session_t::option_verbose); else if (std::strcmp(p, "version") == 0) return MAKE_FUNCTOR(session_t::option_version); else if (std::strcmp(p, "verify") == 0) return MAKE_FUNCTOR(session_t::option_verify); break; } } break; } return expr_t::ptr_op_t(); } } // namespace ledger