Inspired by Omari Norman, I've rewritten main.cc so it's easy to approach.

This commit is contained in:
John Wiegley 2009-01-31 18:52:34 -04:00
parent 75daee6a5d
commit 9d267fa133
12 changed files with 555 additions and 375 deletions

View file

@ -151,6 +151,7 @@ pkginclude_HEADERS = \
src/reconcile.h \
src/quotes.h \
\
src/work.h \
src/ledger.h \
\
python/pyledger.h \
@ -194,7 +195,7 @@ ledger_CPPFLAGS = $(lib_cppflags)
if HAVE_BOOST_PYTHON
ledger_CPPFLAGS += -I$(srcdir)/python
endif
ledger_SOURCES = src/main.cc
ledger_SOURCES = src/main.cc src/work.cc
ledger_LDADD = $(LIBOBJS) $(lib_LTLIBRARIES)
if HAVE_BOOST_PYTHON
ledger_LDADD += libledger_python.la

View file

@ -197,7 +197,7 @@ expr_t::ptr_op_t account_t::lookup(const string& name)
break;
}
return session_t::current->current_report->lookup(name);
return session_t::current->report->lookup(name);
}
bool account_t::valid() const
@ -240,7 +240,7 @@ void account_t::calculate_sums()
}
call_scope_t args(*this);
value_t amount(session_t::current->current_report->get_amount_expr(args));
value_t amount(session_t::current->report->get_amount_expr(args));
if (! amount.is_null()) {
add_or_set_value(xd.total, amount);
xd.total_count += xd.count;

View file

@ -128,7 +128,7 @@ expr_t::ptr_op_t item_t::lookup(const string& name)
break;
}
return session_t::current->current_report->lookup(name);
return session_t::current->report->lookup(name);
}
bool item_t::valid() const

View file

@ -41,6 +41,7 @@
#define _LEDGER_H
#include <utils.h>
#include <option.h>
#include <value.h>
@ -67,4 +68,11 @@
#include <reconcile.h>
#include <quotes.h>
#if defined(HAVE_BOOST_PYTHON)
#include <pyinterp.h>
#define LEDGER_SESSION_T python_interpreter_t
#else
#define LEDGER_SESSION_T session_t
#endif
#endif // _LEDGER_H

View file

@ -29,335 +29,129 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "session.h"
#include "report.h"
#include "option.h"
#include "help.h"
#include "pyinterp.h"
#include <ledger.h>
#include "textual.h"
#include "qif.h"
#include "xml.h"
#include "gnucash.h"
#ifdef HAVE_LIBOFX
#include "ofx.h"
#endif
namespace ledger {
int read_and_report(ledger::report_t& report,
int argc, char * argv[], char * envp[])
{
using namespace ledger;
session_t& session(report.session);
// Setup global defaults
optional<path> home;
if (const char * home_var = std::getenv("HOME"))
home = home_var;
session.init_file = home ? *home / ".ledgerrc" : "./.ledgerrc";
session.price_db = home ? *home / ".pricedb" : "./.pricedb";
session.cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
// Process the environment settings
TRACE_START(environment, 1, "Processed environment variables");
process_environment(const_cast<const char **>(envp), "LEDGER_", report);
#if 1
// These are here for backwards compatability, but are deprecated.
if (const char * p = std::getenv("LEDGER"))
process_option("file", report, p);
if (const char * p = std::getenv("LEDGER_INIT"))
process_option("init-file", report, p);
if (const char * p = std::getenv("PRICE_HIST"))
process_option("price-db", report, p);
if (const char * p = std::getenv("PRICE_EXP"))
process_option("price-exp", report, p);
#endif
TRACE_FINISH(environment, 1);
// Read the initialization file
TRACE_START(init, 1, "Read initialization file");
session.read_init();
TRACE_FINISH(init, 1);
// Handle the command-line arguments
TRACE_START(arguments, 1, "Processed command-line arguments");
strings_list args;
process_arguments(argc - 1, argv + 1, report, args);
if (args.empty()) {
ledger::help(std::cout);
return 1;
}
strings_list::iterator arg = args.begin();
if (! session.cache_file)
session.use_cache = false;
else if (session.use_cache)
session.use_cache = ! session.data_file.empty() && session.price_db;
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
if (session.data_file == *session.cache_file)
session.use_cache = false;
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
INFO("Initialization file is " << session.init_file->string());
INFO("Price database is " << session.price_db->string());
INFO("Binary cache is " << session.cache_file->string());
INFO("Journal file is " << session.data_file.string());
if (! session.use_cache)
INFO("Binary cache mechanism will not be used");
TRACE_FINISH(arguments, 1);
// Read the command word and see if it's any of the debugging commands
// that Ledger supports.
string verb = *arg++;
function_t command;
if (expr_t::ptr_op_t def = report.lookup(string("ledger_precmd_") + verb))
command = def->as_function();
// Parse the initialization file, which can only be textual; then
// parse the journal data. But only do this if there was no
// "pre-command", which are always executed without doing any
// parsing.
if (! command) {
INFO_START(journal, "Read journal file");
journal_t& journal(*session.create_journal());
std::size_t count = session.read_data(journal, report.account);
if (count == 0)
throw_(parse_error, "Failed to locate any journal entries; "
"did you specify a valid file with -f?");
INFO_FINISH(journal);
INFO("Found " << count << " entries");
TRACE_FINISH(entry_text, 1);
TRACE_FINISH(entry_details, 1);
TRACE_FINISH(entry_xacts, 1);
TRACE_FINISH(entries, 1);
TRACE_FINISH(session_parser, 1);
TRACE_FINISH(parsing_total, 1);
// Lookup the command object corresponding to the command verb.
if (expr_t::ptr_op_t def = report.lookup(string("ledger_cmd_") + verb))
command = def->as_function();
}
if (! command)
throw_(std::logic_error, string("Unrecognized command '") + verb + "'");
#if 1
// Patch up some of the reporting options based on what kind of
// command it was.
// jww (2008-08-14): This code really needs to be rationalized away
// for 3.0.
if (verb == "print" || verb == "entry" || verb == "dump") {
report.show_related = true;
report.show_all_related = true;
}
else if (verb == "equity") {
report.show_subtotal = true;
}
else if (report.show_related) {
if (verb[0] == 'r') {
report.show_inverted = true;
} else {
report.show_subtotal = true;
report.show_all_related = true;
}
}
if (verb[0] != 'b' && verb[0] != 'r')
amount_t::keep_base = true;
// Setup the default value for the display predicate
if (report.display_predicate.empty()) {
if (verb[0] == 'b') {
if (! report.show_empty)
report.display_predicate = "total";
if (! report.show_subtotal) {
if (! report.display_predicate.empty())
report.display_predicate += "&";
report.display_predicate += "depth<=1";
}
}
else if (verb == "equity") {
report.display_predicate = "amount_expr"; // jww (2008-08-14): ???
}
else if (verb[0] == 'r' && ! report.show_empty) {
report.display_predicate = "amount";
}
}
#endif
// Now setup the various formatting strings
// jww (2008-08-14): I hear a song, and it's sound is "HaAaaCcK"
#if 0
if (! date_output_format.empty())
date_t::output_format = date_output_format;
#endif
amount_t::keep_price = report.keep_price;
amount_t::keep_date = report.keep_date;
amount_t::keep_tag = report.keep_tag;
if (! report.report_period.empty() && ! report.sort_all)
report.entry_sort = true;
// Setup the output stream, which might involve invoking the pager
report.output_stream.initialize(report.output_file, report.pager_path);
// Create an argument scope containing the report command's
// arguments, and then invoke the command.
call_scope_t command_args(report);
for (strings_list::iterator i = arg; i != args.end(); i++)
command_args.push_back(string_value(*i));
INFO_START(command, "Did user command '" << verb << "'");
command(command_args);
INFO_FINISH(command);
#if 0
// Write out the binary cache, if need be
if (session.use_cache && session.cache_dirty && session.cache_file) {
TRACE_START(binary_cache, 1, "Wrote binary journal file");
ofstream stream(*session.cache_file);
journal.write(stream);
TRACE_FINISH(binary_cache, 1);
}
#endif
return 0;
}
}
#include "work.h" // this is where the top-level code is
int main(int argc, char * argv[], char * envp[])
{
int status = 1;
using namespace ledger;
for (int i = 1; i < argc; i++)
if (argv[i][0] == '-') {
if (std::strcmp(argv[i], "--verify") == 0) {
#if defined(VERIFY_ON)
ledger::verify_enabled = true;
#endif
}
else if (std::strcmp(argv[i], "--verbose") == 0 ||
std::strcmp(argv[i], "-v") == 0) {
#if defined(LOGGING_ON)
ledger::_log_level = ledger::LOG_INFO;
#endif
}
else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
#if defined(DEBUG_ON)
ledger::_log_level = ledger::LOG_DEBUG;
ledger::_log_category = argv[i + 1];
i++;
#endif
}
else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
#if defined(TRACING_ON)
ledger::_log_level = ledger::LOG_TRACE;
try {
ledger::_trace_level = boost::lexical_cast<int>(argv[i + 1]);
}
catch (const boost::bad_lexical_cast& e) {
std::cerr << "Argument to --trace must be an integer."
<< std::endl;
return 1;
}
i++;
#endif
}
}
// The very first thing we do is handle some very special command-line
// options, since they affect how the whole environment is setup:
//
// --verify ; turns on memory tracing
// --verbose ; turns on logging
// --debug CATEGORY ; turns on debug logging
// --trace LEVEL ; turns on trace logging
handle_debug_options(argc, argv);
IF_VERIFY()
ledger::initialize_memory_tracing();
initialize_memory_tracing();
// Initialize the global C++ environment
std::ios::sync_with_stdio(false);
filesystem::path::default_name_check(filesystem::portable_posix_name);
// Initialization of Ledger can now begin. The whole series of operations
// is contained in a try block, so we can report errors in a nicer fashion.
INFO("Ledger starting");
session_t * session = NULL;
int status = 1;
try {
std::ios::sync_with_stdio(false);
// Create the session object, which maintains nearly all state relating to
// this invocation of Ledger.
session = new LEDGER_SESSION_T;
set_session_context(session);
boost::filesystem::path::default_name_check
(boost::filesystem::portable_posix_name);
// Register all known journal parsers. The order of these is important.
register_journal_parsers(*session);
INFO("Ledger starting");
// Create the report object, which maintains state relating to each
// command invocation. Because we're running this from main() the
// distinction between session and report doesn't matter, but if a GUI
// were calling into Ledger, it would have one session object, with a
// separate report object for each report it generated.
session->report.reset(new report_t(*session));
report_t& report(*session->report.get());
#if defined(HAVE_BOOST_PYTHON)
std::auto_ptr<ledger::session_t> session(new ledger::python_interpreter_t);
#else
std::auto_ptr<ledger::session_t> session(new ledger::session_t);
#endif
// Read user option settings, first in the environment, then from the
// user's initialization file and then from the command-line. The first
// non-option argument thereafter is the "command verb".
read_environment_settings(report, envp);
session->read_init();
ledger::set_session_context(session.get());
// Notify the session object that all option handlers invoked beyond this
// point came from the command-line
session->now_at_command_line(true);
#if 0
session->register_parser(new ledger::journal_t::binary_parser_t);
#endif
session->register_parser(new ledger::xml_parser_t);
session->register_parser(new ledger::gnucash_parser_t);
#ifdef HAVE_LIBOFX
session->register_parser(new ledger::ofx_parser_t);
#endif
session->register_parser(new ledger::qif_parser_t);
session->register_parser(new ledger::textual_parser_t);
strings_list args = read_command_line_arguments(report, argc, argv);
string_iterator arg = args.begin();
string verb = *arg++;
session->current_report.reset(new ledger::report_t(*session.get()));
// Look for a precommand first, which is defined as any defined function
// whose name starts with "ledger_precmd_". The difference between a
// precommand and a regular command is that precommands ignore the journal
// data file completely, nor is the user's init file read.
//
// Here are some examples:
//
// parse STRING ; show how a value expression is parsed
// eval STRING ; simply evaluate a value expression
// format STRING ; show how a format string is parsed
//
// If such a command is found, create the output stream for the result and
// then invoke the command.
if (function_t command = look_for_precommand(report, verb)) {
create_output_stream(report);
invoke_command_verb(report, command, arg, args.end());
}
else if (function_t command = look_for_command(report, verb)) {
// Parse the user's journal files.
if (journal_t * journal = read_journal_files(*session)) {
normalize_report_options(report, verb); // this is a total hack
status = read_and_report(*session->current_report.get(), argc, argv, envp);
// Create the output stream (it might be a file, the console or a
// PAGER subprocess) and invoke the report command.
create_output_stream(report);
invoke_command_verb(report, command, arg, args.end());
if (! DO_VERIFY())
session.release(); // don't free anything! just let it leak
// Write out a binary cache of the journal data, if needed and
// appropriate to do so
write_binary_cache(*session, journal);
}
}
else {
throw_(std::logic_error, "Unrecognized command '" << verb << "'");
}
// If we got here, everything succeeded just fine. Ledger uses exceptions
// to notify of any error conditions, so if you're using gdb, type "catch
// throw" to find the source of any errors.
status = 0;
}
catch (const std::exception& err) {
std::cout.flush();
std::cerr << ledger::error_context()
<< "Error: " << err.what() << std::endl;
std::cerr << error_context() << "Error: " << err.what() << std::endl;
}
catch (int _status) {
status = _status;
}
// If memory verification is being performed (which can be very slow), clean
// up everything by closing the session and deleting the session object, and
// then shutting down the memory tracing subsystem. Otherwise, let it all
// leak because we're about to exit anyway.
IF_VERIFY() {
set_session_context(NULL);
if (session != NULL)
checked_delete(session);
INFO("Ledger ended (Boost/libstdc++ may still hold memory)");
ledger::set_session_context();
ledger::shutdown_memory_tracing();
shutdown_memory_tracing();
} else {
// Don't free anything, just let it all leak.
INFO("Ledger ended");
}

View file

@ -34,6 +34,8 @@
#include "account.h"
#include "session.h"
#if defined(HAVE_LIBOFX)
#include <libofx.h>
namespace ledger {
@ -261,3 +263,5 @@ std::size_t ofx_parser_t::parse(std::istream& in,
}
} // namespace ledger
#endif HAVE_LIBOFX

View file

@ -488,38 +488,7 @@ namespace {
out << std::endl << "--- Expression tree ---" << std::endl;
expr.dump(out);
out << std::endl << "--- Calculated value ---" << std::endl;
expr.calc(args).print(out);
out << std::endl;
return 0L;
}
value_t compile_command(call_scope_t& args)
{
var_t<string> arg(args, 0);
if (! arg) {
throw std::logic_error("Usage: compile TEXT");
return 1L;
}
report_t& report(find_scope<report_t>(args));
std::ostream& out(report.output_stream);
out << "--- Input text ---" << std::endl;
out << *arg << std::endl;
out << std::endl << "--- Text as parsed ---" << std::endl;
expr_t expr(*arg);
expr.print(out);
out << std::endl;
out << std::endl << "--- Expression tree ---" << std::endl;
expr.dump(out);
expr.compile(args);
out << std::endl << "--- Compiled tree ---" << std::endl;
expr.dump(out);
@ -680,11 +649,6 @@ expr_t::ptr_op_t report_t::lookup(const string& name)
return WRAP_FUNCTOR(period_command);
break;
case 'c':
if (std::strcmp(p, "compile") == 0)
return WRAP_FUNCTOR(compile_command);
break;
case 'e':
if (std::strcmp(p, "eval") == 0)
return WRAP_FUNCTOR(eval_command);

View file

@ -735,6 +735,8 @@ public:
}
value_t option_price_db_(call_scope_t& args) { // :
// jww (2009-01-31): This, and several of the other option handlers,
// should be in the session object.
session.price_db = args[0].as_string();
return true;
}

View file

@ -70,7 +70,12 @@ void release_session_context()
}
session_t::session_t()
: register_format
: 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(date) %-.20(payee) %-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) "
"%!12(print_balance(display_total, 12, 80, true))\n%/"
"%31|%-.23(account(23)) %!12(print_balance(amount_expr, 12, 67)) "
@ -116,6 +121,14 @@ session_t::session_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";
cache_file = home ? *home / ".ledger-cache" : "./.ledger-cache";
}
session_t::~session_t()
@ -159,6 +172,8 @@ void session_t::read_init()
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;
@ -167,12 +182,14 @@ void session_t::read_init()
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_file.empty())
if (data_files.empty())
throw_(parse_error, "No journal file was specified (please use -f)");
TRACE_START(session_parser, 1, "Parsed journal file");
@ -210,33 +227,39 @@ std::size_t session_t::read_data(journal_t& journal,
}
}
DEBUG("ledger.cache", "rejected cache, parsing " << data_file.string());
if (data_file == "-") {
use_cache = false;
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;
foreach (const path& pathname, data_files) {
DEBUG("ledger.cache", "rejected cache, parsing " << pathname.string());
if (pathname == "-") {
use_cache = false;
journal.sources.push_back("/dev/stdin");
while (std::cin.good() && ! std::cin.eof()) {
static char line[8192];
std::cin.read(line, 8192);
std::streamsize count = std::cin.gcount();
buffer.write(line, count);
// 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()) {
static 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);
clean_accounts();
}
else {
throw_(parse_error, "Could not open journal file '" << pathname << "'");
}
buffer.flush();
std::istringstream buf_in(buffer.str());
entry_count += read_journal(journal, buf_in, "/dev/stdin", acct);
}
else if (exists(data_file)) {
entry_count += read_journal(journal, data_file, acct);
if (journal.price_db)
journal.sources.push_back(*journal.price_db);
clean_accounts();
}
}

View file

@ -71,12 +71,16 @@ class session_t : public noncopyable, public scope_t
public:
static session_t * current;
scoped_ptr<report_t> current_report;
scoped_ptr<report_t> report;
path data_file;
std::list<path> data_files;
bool next_data_file_from_command_line;
bool saw_data_file_from_command_line;
optional<path> init_file;
optional<path> cache_file;
optional<path> price_db;
optional<path> price_db;
bool next_price_db_from_command_line;
bool saw_price_db_from_command_line;
string register_format;
string wide_register_format;
@ -114,6 +118,11 @@ public:
session_t();
virtual ~session_t();
void now_at_command_line(const bool truth) {
next_data_file_from_command_line = truth;
next_price_db_from_command_line = truth;
}
journal_t * create_journal() {
journal_t * journal = new journal_t;
journals.push_back(journal);
@ -243,11 +252,13 @@ See LICENSE file included with the distribution for details and disclaimer.\n";
value_t option_file_(call_scope_t& args) { // f
assert(args.size() == 1);
// jww (2008-08-13): Add support for multiple files
data_file = args[0].as_string();
use_cache = false;
if (next_data_file_from_command_line &&
! saw_data_file_from_command_line) {
data_files.clear();
use_cache = false;
saw_data_file_from_command_line = true;
}
data_files.push_back(args[0].as_string());
return true;
}
};

308
src/work.cc Normal file
View file

@ -0,0 +1,308 @@
/*
* 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 <ledger.h>
#include "work.h"
namespace ledger {
void handle_debug_options(int argc, char * argv[])
{
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
if (std::strcmp(argv[i], "--verify") == 0) {
#if defined(VERIFY_ON)
verify_enabled = true; // global in utils.h
#endif
}
else if (std::strcmp(argv[i], "--verbose") == 0 ||
std::strcmp(argv[i], "-v") == 0) {
#if defined(LOGGING_ON)
_log_level = LOG_INFO; // global in utils.h
#endif
}
else if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
#if defined(DEBUG_ON)
_log_level = LOG_DEBUG; // global in utils.h
_log_category = argv[i + 1]; // global in utils.h
i++;
#endif
}
else if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
#if defined(TRACING_ON)
_log_level = LOG_TRACE; // global in utils.h
try {
// global in utils.h
_trace_level = boost::lexical_cast<int>(argv[i + 1]);
}
catch (const boost::bad_lexical_cast& e) {
std::cerr << "Argument to --trace must be an integer."
<< std::endl;
throw int(1);
}
i++;
#endif
}
}
}
}
void register_journal_parsers(session_t& session)
{
#if 0
session.register_parser(new journal_t::binary_parser_t);
#endif
session.register_parser(new xml_parser_t);
session.register_parser(new gnucash_parser_t);
#ifdef HAVE_LIBOFX
session.register_parser(new ofx_parser_t);
#endif
session.register_parser(new qif_parser_t);
session.register_parser(new textual_parser_t);
}
void read_environment_settings(report_t& report, char * envp[])
{
TRACE_START(environment, 1, "Processed environment variables");
process_environment(const_cast<const char **>(envp), "LEDGER_",
report);
#if 1
// These are here for backwards compatability, but are deprecated.
if (const char * p = std::getenv("LEDGER"))
process_option("file", report, p);
if (const char * p = std::getenv("LEDGER_INIT"))
process_option("init-file", report, p);
if (const char * p = std::getenv("PRICE_HIST"))
process_option("price-db", report, p);
if (const char * p = std::getenv("PRICE_EXP"))
process_option("price-exp", report, p);
#endif
TRACE_FINISH(environment, 1);
}
strings_list
read_command_line_arguments(report_t& report, int argc, char * argv[])
{
TRACE_START(arguments, 1, "Processed command-line arguments");
strings_list args;
process_arguments(argc - 1, argv + 1, report, args);
if (args.empty()) {
help(std::cout);
throw int(1);
}
TRACE_FINISH(arguments, 1);
return args;
}
void normalize_session_options(session_t& session)
{
if (! session.cache_file)
session.use_cache = false;
DEBUG("ledger.session.cache", "1. use_cache = " << session.use_cache);
if (std::find(session.data_files.begin(),
session.data_files.end(), *session.cache_file) !=
session.data_files.end())
session.use_cache = false;
DEBUG("ledger.session.cache", "2. use_cache = " << session.use_cache);
INFO("Initialization file is " << session.init_file->string());
INFO("Price database is " << session.price_db->string());
INFO("Binary cache is " << session.cache_file->string());
foreach (const path& pathname, session.data_files)
INFO("Journal file is " << pathname.string());
if (! session.use_cache)
INFO("Binary cache mechanism will not be used");
}
function_t look_for_precommand(report_t& report, const string& verb)
{
if (expr_t::ptr_op_t def = report.lookup(string("ledger_precmd_") + verb))
return def->as_function();
else
return function_t();
}
journal_t * read_journal_files(session_t& session)
{
INFO_START(journal, "Read journal file");
journal_t * journal(session.create_journal());
std::size_t count = session.read_data(*journal, session.report->account);
if (count == 0)
throw_(parse_error, "Failed to locate any journal entries; "
"did you specify a valid file with -f?");
INFO_FINISH(journal);
INFO("Found " << count << " entries");
TRACE_FINISH(entry_text, 1);
TRACE_FINISH(entry_details, 1);
TRACE_FINISH(entry_xacts, 1);
TRACE_FINISH(entries, 1);
TRACE_FINISH(session_parser, 1);
TRACE_FINISH(parsing_total, 1);
return journal;
}
function_t look_for_command(report_t& report, const string& verb)
{
if (expr_t::ptr_op_t def = report.lookup(string("ledger_cmd_") + verb))
return def->as_function();
else
return function_t();
}
void normalize_report_options(report_t& report, const string& verb)
{
#if 1
// Patch up some of the reporting options based on what kind of
// command it was.
// jww (2008-08-14): This code really needs to be rationalized away
// for 3.0.
if (verb == "print" || verb == "entry" || verb == "dump") {
report.show_related = true;
report.show_all_related = true;
}
else if (verb == "equity") {
report.show_subtotal = true;
}
else if (report.show_related) {
if (verb[0] == 'r') {
report.show_inverted = true;
} else {
report.show_subtotal = true;
report.show_all_related = true;
}
}
if (verb[0] != 'b' && verb[0] != 'r')
amount_t::keep_base = true;
// Setup the default value for the display predicate
if (report.display_predicate.empty()) {
if (verb[0] == 'b') {
if (! report.show_empty)
report.display_predicate = "total";
if (! report.show_subtotal) {
if (! report.display_predicate.empty())
report.display_predicate += "&";
report.display_predicate += "depth<=1";
}
}
else if (verb == "equity") {
report.display_predicate = "amount_expr"; // jww (2008-08-14): ???
}
else if (verb[0] == 'r' && ! report.show_empty) {
report.display_predicate = "amount";
}
}
#endif
// Now setup the various formatting strings
// jww (2008-08-14): I hear a song, and it's sound is "HaAaaCcK"
#if 0
if (! date_output_format.empty())
date_t::output_format = date_output_format;
#endif
amount_t::keep_price = report.keep_price;
amount_t::keep_date = report.keep_date;
amount_t::keep_tag = report.keep_tag;
if (! report.report_period.empty() && ! report.sort_all)
report.entry_sort = true;
}
void create_output_stream(report_t& report)
{
report.output_stream.initialize(report.output_file, report.pager_path);
}
void invoke_command_verb(report_t& report,
function_t& command,
string_iterator args_begin,
string_iterator args_end)
{
// Create an argument scope containing the report command's
// arguments, and then invoke the command.
call_scope_t command_args(report);
for (string_iterator i = args_begin; i != args_end; i++)
command_args.push_back(string_value(*i));
INFO_START(command, "Finished executing command");
command(command_args);
INFO_FINISH(command);
}
void write_binary_cache(session_t& session, journal_t * journal)
{
// Write out the binary cache, if need be
if (session.use_cache && session.cache_dirty && session.cache_file) {
TRACE_START(binary_cache, 1, "Wrote binary journal file");
ofstream stream(*session.cache_file);
#if 0
// jww (2009-01-31): NYI
journal->write(stream);
#endif
TRACE_FINISH(binary_cache, 1);
}
}
} // namespace ledger

65
src/work.h Normal file
View file

@ -0,0 +1,65 @@
/*
* 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.
*/
/**
* @file work.h
* @author John Wiegley
*
* @brief Contains the top-level functions used by main.cc
*/
#ifndef _WORK_H
#define _WORK_H
namespace ledger {
typedef strings_list::iterator string_iterator;
typedef std::pair<string_iterator, string_iterator> string_iterator_pair;
void handle_debug_options(int argc, char * argv[]);
void register_journal_parsers(session_t& session);
void read_environment_settings(report_t& report, char * envp[]);
strings_list read_command_line_arguments(report_t& report,
int argc, char * argv[]);
void normalize_session_options(session_t& session);
function_t look_for_precommand(report_t& report, const string& verb);
journal_t * read_journal_files(session_t& session);
function_t look_for_command(report_t& report, const string& verb);
void normalize_report_options(report_t& report, const string& verb);
void create_output_stream(report_t& report);
void invoke_command_verb(report_t& report,
function_t& command,
string_iterator args_begin,
string_iterator args_end);
void write_binary_cache(session_t& session, journal_t * journal);
} // namespace ledger
#endif // _WORK_H