summaryrefslogtreecommitdiff
path: root/src/main.cc
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2007-04-30 06:26:38 +0000
committerJohn Wiegley <johnw@newartisans.com>2008-04-13 03:38:33 -0400
commitc8899addfd2deed3d84be2de234681db64987722 (patch)
tree07f9a5eb603ff4ec83fe18c83083575d2b7a439a /src/main.cc
parentaa9cc125796711afcaa459898e95527fdae8e912 (diff)
downloadfork-ledger-c8899addfd2deed3d84be2de234681db64987722.tar.gz
fork-ledger-c8899addfd2deed3d84be2de234681db64987722.tar.bz2
fork-ledger-c8899addfd2deed3d84be2de234681db64987722.zip
Rearranged the sources a bit.
Diffstat (limited to 'src/main.cc')
-rw-r--r--src/main.cc484
1 files changed, 484 insertions, 0 deletions
diff --git a/src/main.cc b/src/main.cc
new file mode 100644
index 00000000..0ca984c3
--- /dev/null
+++ b/src/main.cc
@@ -0,0 +1,484 @@
+#if defined(USE_BOOST_PYTHON)
+#include <pyledger.h>
+#else
+#include <ledger.h>
+#endif
+#include <option.h>
+
+#include "acconf.h"
+
+#ifdef HAVE_UNIX_PIPES
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "fdstream.hpp"
+#endif
+
+using namespace ledger;
+
+#if 0
+class print_addr : public repitem_t::select_callback_t {
+ virtual void operator()(repitem_t * item) {
+ std::cout << item << std::endl;
+ }
+};
+#endif
+
+static int read_and_report(report_t * report, int argc, char * argv[],
+ char * envp[])
+{
+ session_t& session(*report->session);
+
+ // Handle the command-line arguments
+
+ std::list<string> args;
+ process_arguments(argc - 1, argv + 1, false, report, args);
+
+ if (args.empty()) {
+#if 0
+ help(std::cerr);
+#endif
+ return 1;
+ }
+ strings_list::iterator arg = args.begin();
+
+ if (session.cache_file == "<none>")
+ session.use_cache = false;
+ else
+ session.use_cache = session.data_file.empty() && session.price_db.empty();
+
+ DEBUG_("ledger.session.cache", "1. use_cache = " << session.use_cache);
+
+ // Process the environment settings
+
+ TRACE_START(environment, 1, "Processed environment variables");
+ process_environment(const_cast<const char **>(envp), "LEDGER_", report);
+ TRACE_FINISH(environment, 1);
+
+ const char * p = std::getenv("HOME");
+ string home = p ? p : "";
+
+ if (session.init_file.empty())
+ session.init_file = home + "/.ledgerrc";
+ if (session.price_db.empty())
+ session.price_db = home + "/.pricedb";
+
+ if (session.cache_file.empty())
+ session.cache_file = home + "/.ledger-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);
+ INFO("Price database is " << session.price_db);
+ INFO("Binary cache is " << session.cache_file);
+ INFO("Journal file is " << session.data_file);
+
+ if (! session.use_cache)
+ INFO("Binary cache mechanism will not be used");
+
+ // Read the command word and create a command object based on it
+
+ string verb = *arg++;
+
+ std::auto_ptr<xml::xpath_t::functor_t> command;
+
+ if (verb == "register" || verb == "reg" || verb == "r") {
+#if 1
+ command.reset(new register_command);
+#else
+ command = new format_command
+ ("register", either_or(report->format_string,
+ report->session->register_format));
+#endif
+ }
+#if 0
+ else if (verb == "balance" || verb == "bal" || verb == "b") {
+ if (! report->raw_mode) {
+ report->transforms.push_back(new accounts_transform);
+ report->transforms.push_back(new clean_transform);
+ report->transforms.push_back(new compact_transform);
+ }
+ command = new format_command
+ ("balance", either_or(report->format_string,
+ report->session->balance_format));
+ }
+ else if (verb == "print" || verb == "p") {
+ if (! report->raw_mode)
+ report->transforms.push_back(new optimize_transform);
+ command = new format_command
+ ("print", either_or(report->format_string,
+ report->session->print_format));
+ }
+ else if (verb == "equity") {
+ if (! report->raw_mode)
+ report->transforms.push_back(new accounts_transform);
+ command = new format_command
+ ("equity", either_or(report->format_string,
+ report->session->equity_format));
+ }
+ else if (verb == "entry")
+ command = new entry_command;
+ else if (verb == "dump")
+ command = new dump_command;
+ else if (verb == "output")
+ command = new output_command;
+ else if (verb == "prices")
+ command = new prices_command;
+ else if (verb == "pricesdb")
+ command = new pricesdb_command;
+ else if (verb == "csv")
+ command = new csv_command;
+ else if (verb == "emacs" || verb == "lisp")
+ command = new emacs_command;
+#endif
+ else if (verb == "xml")
+ command.reset(new xml_command);
+ else if (verb == "expr")
+ ;
+ else if (verb == "xpath")
+ ;
+ else if (verb == "parse") {
+ xml::xpath_t expr(*arg);
+
+ IF_INFO() {
+ std::cout << "Value expression tree:" << std::endl;
+ expr.dump(std::cout);
+ std::cout << std::endl;
+ std::cout << "Value expression parsed was:" << std::endl;
+ expr.write(std::cout);
+ std::cout << std::endl << std::endl;
+ std::cout << "Result of calculation: ";
+ }
+
+ std::cout << expr.calc((xml::document_t *)NULL, report).
+ strip_annotations() << std::endl;
+
+ return 0;
+ }
+ else {
+ char buf[128];
+ std::strcpy(buf, "command_");
+ std::strcat(buf, verb.c_str());
+
+ // jww (2007-04-19): This is an error, since command is an
+ // auto_ptr!
+ if (xml::xpath_t::op_t * def = report->lookup(buf))
+ command.reset(def->functor_obj());
+
+ if (! command.get())
+ throw_(exception, string("Unrecognized command '") + verb + "'");
+ }
+
+ // Parse the initialization file, which can only be textual; then
+ // parse the journal data.
+
+ session.read_init();
+
+ INFO_START(journal, "Read journal file");
+ journal_t * journal = session.read_data(report->account);
+ INFO_FINISH(journal);
+
+ TRACE_FINISH(entry_text, 1);
+ TRACE_FINISH(entry_date, 1);
+ TRACE_FINISH(entry_details, 1);
+ TRACE_FINISH(entry_xacts, 1);
+ TRACE_FINISH(entries, 1);
+ TRACE_FINISH(parsing_total, 1);
+
+ // Configure the output stream
+
+#ifdef HAVE_UNIX_PIPES
+ int status, pfd[2]; // Pipe file descriptors
+#endif
+ std::ostream * out = &std::cout;
+
+ if (! report->output_file.empty()) {
+ out = new std::ofstream(report->output_file.c_str());
+ }
+#ifdef HAVE_UNIX_PIPES
+ else if (! report->pager.empty()) {
+ status = pipe(pfd);
+ if (status == -1)
+ throw_(exception, "Failed to create pipe");
+
+ status = fork();
+ if (status < 0) {
+ throw_(exception, "Failed to fork child process");
+ }
+ else if (status == 0) { // child
+ const char *arg0;
+
+ // Duplicate pipe's reading end into stdin
+ status = dup2(pfd[0], STDIN_FILENO);
+ if (status == -1)
+ perror("dup2");
+
+ // Close unuseful file descriptors: the pipe's writing and
+ // reading ends (the latter is not needed anymore, after the
+ // duplication).
+ close(pfd[1]);
+ close(pfd[0]);
+
+ // Find command name: its the substring starting right of the
+ // rightmost '/' character in the pager pathname. See manpage
+ // for strrchr.
+ arg0 = std::strrchr(report->pager.c_str(), '/');
+ if (arg0)
+ arg0++;
+ else
+ arg0 = report->pager.c_str(); // No slashes in pager.
+
+ execlp(report->pager.c_str(), arg0, (char *)0);
+ perror("execl");
+ exit(1);
+ }
+ else { // parent
+ close(pfd[0]);
+ out = new boost::fdostream(pfd[1]);
+ }
+ }
+#endif
+
+ // Are we handling the expr commands? Do so now.
+
+ if (verb == "expr") {
+ xml::xpath_t expr(*arg);
+
+ IF_INFO() {
+ *out << "Value expression tree:" << std::endl;
+ expr.dump(*out);
+ *out << std::endl;
+ *out << "Value expression parsed was:" << std::endl;
+ expr.write(*out);
+ *out << std::endl << std::endl;
+ *out << "Result of calculation: ";
+ }
+
+ *out << expr.calc((xml::document_t *)NULL, report).
+ strip_annotations() << std::endl;
+
+ return 0;
+ }
+ else if (verb == "xpath") {
+ std::cout << "XPath parsed:" << std::endl;
+ xml::xpath_t xpath(*arg);
+ xpath.write(*out);
+ *out << std::endl;
+
+#if 0
+ std::auto_ptr<repitem_t> items(repitem_t::wrap(&session, report, true));
+ print_addr cb;
+ items->select(path.get(), cb);
+#endif
+ return 0;
+ }
+
+ // Create the an argument scope containing the report command's
+ // arguments, and then invoke the command.
+
+ std::auto_ptr<xml::xpath_t::scope_t> locals
+ (new xml::xpath_t::scope_t(report, xml::xpath_t::scope_t::ARGUMENT));
+
+ locals->args = new value_t::sequence_t;
+ locals->args.push_back(out);
+ locals->args.push_back(journal->document);
+
+ if (command->wants_args) {
+ for (strings_list::iterator i = args.begin();
+ i != args.end();
+ i++)
+ locals->args.push_back(*i);
+ } else {
+ string regexps[4];
+
+ // Treat the remaining command-line arguments as regular
+ // expressions, used for refining report results.
+
+ int base = 0;
+ for (strings_list::iterator i = arg; i != args.end(); i++)
+ if ((*i)[0] == '-') {
+ if ((*i)[1] == '-') {
+ if (base == 0)
+ base += 2;
+ continue;
+ }
+ if (! regexps[base + 1].empty())
+ regexps[base + 1] += "|";
+ regexps[base + 1] += (*i).substr(1);
+ } else {
+ if (! regexps[base].empty())
+ regexps[base] += "|";
+ regexps[base] += *i;
+ }
+
+#if 0
+ // jww (2006-09-21): Escape the \ in these strings!
+
+ if (! regexps[3].empty())
+ report->transforms.push_front
+ (new remove_transform
+ (string("//entry[payee =~ /(") + regexps[3] + ")/]"));
+
+ if (! regexps[2].empty())
+ report->transforms.push_front
+ (new select_transform
+ (string("//entry[payee =~ /(") + regexps[2] + ")/]"));
+
+ if (! regexps[1].empty())
+ report->transforms.push_front
+ (new remove_transform
+ (string("//xact[account =~ /(") + regexps[1] + ")/]"));
+
+ if (! regexps[0].empty())
+ report->transforms.push_front
+ (new select_transform
+ (string("//xact[account =~ /(") + regexps[0] + ")/]"));
+#endif
+ }
+
+ INFO_START(transforms, "Applied transforms");
+ report->apply_transforms(journal->document);
+ INFO_FINISH(transforms);
+
+ INFO_START(command, "Did user command '" << verb << "'");
+ value_t temp;
+ (*command)(temp, locals.get());
+ INFO_FINISH(command);
+
+ // Write out the binary cache, if need be
+
+ if (session.use_cache && session.cache_dirty &&
+ ! session.cache_file.empty()) {
+ TRACE_START(binary_cache, 1, "Wrote binary journal file");
+
+ std::ofstream stream(session.cache_file.c_str());
+#if 0
+ write_binary_journal(stream, journal);
+#endif
+
+ TRACE_FINISH(binary_cache, 1);
+ }
+
+#if defined(FREE_MEMORY)
+ // Cleanup memory -- if this is a beta or development build.
+
+ if (! report->output_file.empty())
+ delete out;
+#endif
+
+ // If the user specified a pager, wait for it to exit now
+
+#ifdef HAVE_UNIX_PIPES
+ if (report->output_file.empty() && ! report->pager.empty()) {
+ delete out;
+ close(pfd[1]);
+
+ // Wait for child to finish
+ wait(&status);
+ if (status & 0xffff != 0)
+ throw_(exception, "Something went wrong in the pager");
+ }
+#endif
+
+ return 0;
+}
+
+int main(int argc, char * argv[], char * envp[])
+{
+ int status = 1;
+
+ for (int i = 1; i < argc; i++)
+ if (argv[i][0] == '-') {
+#if defined(VERIFY_ON)
+ if (std::strcmp(argv[i], "--verify") == 0)
+ ledger::verify_enabled = true;
+#endif
+#if defined(DEBUG_ON)
+ if (i + 1 < argc && std::strcmp(argv[i], "--debug") == 0) {
+ ledger::_log_level = LOG_DEBUG;
+ ledger::_log_category = argv[i + 1];
+ i++;
+ }
+#endif
+#if defined(TRACING_ON)
+ if (i + 1 < argc && std::strcmp(argv[i], "--trace") == 0) {
+ ledger::_log_level = LOG_TRACE;
+ ledger::_trace_level = std::atoi(argv[i + 1]);
+ i++;
+ }
+#endif
+ }
+
+ try {
+ std::ios::sync_with_stdio(false);
+
+ ledger::initialize();
+
+#if ! defined(FULL_DEBUG)
+ ledger::do_cleanup = false;
+#endif
+ INFO("Ledger starting");
+
+ std::auto_ptr<ledger::session_t> session(new ledger::session_t);
+
+#if 0
+ session->register_parser(new binary_parser_t);
+#endif
+#if defined(HAVE_EXPAT) || defined(HAVE_XMLPARSE)
+ session->register_parser(new xml::xml_parser_t);
+ session->register_parser(new gnucash_parser_t);
+#endif
+#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);
+
+ std::auto_ptr<ledger::report_t> report(new ledger::report_t(session.get()));
+
+ status = read_and_report(report.get(), argc, argv, envp);
+
+ if (! ledger::do_cleanup) {
+ report.release();
+ session.release();
+ }
+ }
+#if 0
+ catch (error * err) {
+ std::cout.flush();
+ // Push a null here since there's no file context
+ if (err->context.empty() ||
+ ! dynamic_cast<xact_context *>(err->context.front()))
+ err->context.push_front(new error_context(""));
+ err->reveal_context(std::cerr, "Error");
+ std::cerr << err->what() << std::endl;
+ delete err;
+ }
+ catch (fatal * err) {
+ std::cout.flush();
+ // Push a null here since there's no file context
+ if (err->context.empty() ||
+ ! dynamic_cast<xact_context *>(err->context.front()))
+ err->context.push_front(new error_context(""));
+ err->reveal_context(std::cerr, "Fatal");
+ std::cerr << err->what() << std::endl;
+ delete err;
+ }
+#endif
+ catch (const std::exception& err) {
+ std::cout.flush();
+ std::cerr << "Error: " << err.what() << std::endl;
+ }
+ catch (int _status) {
+ status = _status;
+ }
+
+ if (ledger::do_cleanup)
+ ledger::shutdown();
+
+ return status;
+}
+
+// main.cc ends here.