392 lines
11 KiB
C++
392 lines
11 KiB
C++
/*
|
|
* 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<path> 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)
|
|
return;
|
|
|
|
if (! exists(*init_file))
|
|
throw_(std::logic_error, "Cannot read init file" << *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
|