diff options
author | John Wiegley <johnw@newartisans.com> | 2009-02-04 18:23:18 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-02-04 19:55:08 -0400 |
commit | 73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe (patch) | |
tree | 695fde78e03351750210715ea76ec686ff04fbfc /src/main.cc | |
parent | b9603a1512acdfeb5d304e5ae910c1da553b3337 (diff) | |
download | fork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.tar.gz fork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.tar.bz2 fork-ledger-73cf3b01fbd50c3a8a4fd96ff69643c28394d8fe.zip |
Added structural support in main() for using a REPL.
Diffstat (limited to 'src/main.cc')
-rw-r--r-- | src/main.cc | 261 |
1 files changed, 187 insertions, 74 deletions
diff --git a/src/main.cc b/src/main.cc index b5b65da9..3cdc6cf4 100644 --- a/src/main.cc +++ b/src/main.cc @@ -33,43 +33,60 @@ #include "work.h" // This is where the meat of main() is, which // was moved there for the sake of clarity +using namespace ledger; -int main(int argc, char * argv[], char * envp[]) -{ - using namespace ledger; +namespace { + char * stripwhite (char * string) + { + if (! string) + return NULL; - session_t * session = NULL; - report_t * report = NULL; - int status = 1; - try { - // The very first thing we do is handle some very special command-line - // options, since they affect how the 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() initialize_memory_tracing(); + register char *s, *t; + + for (s = string; isspace (*s); s++) + ; + + if (*s == 0) + return (s); + + t = s + strlen (s) - 1; + while (t > s && isspace (*t)) + t--; + *++t = '\0'; - INFO("Ledger starting"); + return s; + } + + strings_list split_arguments(char * line) + { + strings_list args; - // Initialize global Boost/C++ environment - std::ios::sync_with_stdio(false); - filesystem::path::default_name_check(filesystem::portable_posix_name); + // jww (2009-02-04): This is too naive + for (char * p = std::strtok(line, " \t"); + p; + p = std::strtok(NULL, " \t")) + args.push_back(p); - // Create the session object, which maintains nearly all state relating to - // this invocation of Ledger; and register all known journal parsers. - session = new LEDGER_SESSION_T; - set_session_context(session); + return args; + } + /** + * @return \c true if a command was actually executed; otherwise, it probably + * just resulted in setting some options. + */ + bool execute_command(session_t& session, + ledger::strings_list args, + char ** envp = NULL) + { // Create the report object, which maintains state relating to each // command invocation. Because we're running from main(), the distinction // between session and report doesn't really matter, but if a GUI were // calling into Ledger it would have one session object per open document, // with a separate report_t object for each report it generated. - report = new report_t(*session); - session->global_scope = report; + std::auto_ptr<report_t> manager(new report_t(session)); + report_t& report(*manager.get()); + + session.global_scope = &report; // Read the user's options, in the following order: // @@ -80,10 +97,13 @@ int main(int argc, char * argv[], char * envp[]) // Before processing command-line options, we must notify the session // object that such options are beginning, since options like -f cause a // complete override of files found anywhere else. - read_environment_settings(*report, envp); - session->read_init(); - session->now_at_command_line(true); - strings_list args = read_command_line_arguments(*report, argc, argv); + if (envp) { + session.now_at_command_line(false); + read_environment_settings(report, envp); + session.read_init(); + } + session.now_at_command_line(true); + args = read_command_arguments(report, args); // Look for a precommand first, which is defined as any defined function // whose name starts with "ledger_precmd_". The difference between a @@ -98,56 +118,151 @@ int main(int argc, char * argv[], char * envp[]) // // If such a command is found, create the output stream for the result and // then invoke the command. - string_iterator arg = args.begin(); - string verb = *arg++; - - if (function_t command = look_for_precommand(*report, verb)) { - // 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()); - } - else if (function_t command = look_for_command(*report, verb)) { - // This is regular command verb, so parse the user's data. - if (journal_t * journal = read_journal_files(*session, report->account)) { - normalize_report_options(*report, verb); // jww (2009-02-02): a hack - - // 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()); - - // Write out a binary cache of the journal data, if needful and - // appropriate to do so. - write_binary_cache(*session, journal); + + if (args.empty()) { + read_journal_files(session, report.account); + return false; + } else { + string_iterator arg = args.begin(); + string verb = *arg++; + + if (function_t command = look_for_precommand(report, verb)) { + // 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()); } - } - else { - throw_(std::logic_error, "Unrecognized command '" << verb << "'"); - } + else if (function_t command = look_for_command(report, verb)) { + // This is regular command verb, so parse the user's data if we + // haven't already at the beginning of the REPL. + if (! envp || read_journal_files(session, report.account)) { + normalize_report_options(report, verb); // jww (2009-02-02): a hack + + // 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()); + } + } + else { + throw_(std::logic_error, "Unrecognized command '" << verb << "'"); + } + + session.global_scope = NULL; - // If we've reached this point, everything succeeded fine. Ledger uses - // exceptions to notify of error conditions, so if you're using gdb, just - // type "catch throw" to find the source point of any error. - status = 0; + return true; + } } - catch (const std::exception& err) { - std::cout.flush(); // first display anything that was pending - // Display any pending error context information - string context = error_context(); - if (! context.empty()) - std::cerr << context << std::endl; + int execute_command_wrapper(session_t& session, + ledger::strings_list args, + char ** envp = NULL) + { + int status = 1; + + try { + if (! execute_command(session, args, envp)) + return -1; + + // If we've reached this point, everything succeeded fine. Ledger uses + // exceptions to notify of error conditions, so if you're using gdb, + // just type "catch throw" to find the source point of any error. + status = 0; + } + catch (const std::exception& err) { + std::cout.flush(); // first display anything that was pending + + // Display any pending error context information + string context = error_context(); + if (! context.empty()) + std::cerr << context << std::endl; - std::cerr << "Error: " << err.what() << std::endl; - } - catch (int _status) { - status = _status; // used for a "quick" exit, and is used only + std::cerr << "Error: " << err.what() << std::endl; + } + catch (int _status) { + status = _status; // used for a "quick" exit, and is used only // if help text (such as --help) was displayed + } + return status; } +} + +int main(int argc, char * argv[], char * envp[]) +{ + session_t * session = NULL; + + // The very first thing we do is handle some very special command-line + // options, since they affect how the 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() initialize_memory_tracing(); + + INFO("Ledger starting"); + + // Initialize global Boost/C++ environment + std::ios::sync_with_stdio(false); + filesystem::path::default_name_check(filesystem::portable_posix_name); + + // Create the session object, which maintains nearly all state relating to + // this invocation of Ledger; and register all known journal parsers. + session = new LEDGER_SESSION_T; + set_session_context(session); + + strings_list cmd_args; + for (int i = 1; i < argc; i++) + cmd_args.push_back(argv[i]); + + int status = execute_command_wrapper(*session, cmd_args, envp); + if (status == -1) { // no command was given; enter the REPL + session->option_version(*session); + +#ifdef HAVE_LIBEDIT + + rl_readline_name = "Ledger"; +#if 0 + rl_attempted_completion_function = ledger_completion; +#endif - // Close the output stream, waiting on the pager process to exit if need be - report->output_stream.close(); + while (char * line = stripwhite(readline("==> "))) { + char * expansion; + int result; + + result = history_expand(line, &expansion); + + if (result < 0 || result == 2) { + throw_(std::logic_error, + "Failed to expand history reference '" << line << "'"); + } else { + add_history(expansion); + + strings_list line_argv = split_arguments(line); + execute_command_wrapper(*session, line_argv); + } + std::free(expansion); + + std::free(line); + } + +#else // HAVE_LIBEDIT + + while (! std::cin.eof()) { + std::cout << "--> "; + char line[1024]; + std::cin.getline(line, 1023); + + char * p = stripwhite(line); + if (*p) + execute_command_wrapper(*session, split_arguments(line)); + } + +#endif // HAVE_LIBEDIT + + status = 0; // report success + } // If memory verification is being performed (which can be very slow), clean // up everything by closing the session and deleting the session object, and @@ -157,8 +272,6 @@ int main(int argc, char * argv[], char * envp[]) set_session_context(NULL); if (session != NULL) checked_delete(session); - if (report != NULL) - checked_delete(report); INFO("Ledger ended (Boost/libstdc++ may still hold memory)"); shutdown_memory_tracing(); |