Skip to content

Commit

Permalink
guile repl access to full api
Browse files Browse the repository at this point in the history
- launch guile or python REPL
- optionally load datafile, RO (default) or RW
- exposes qof-session-save-quiet
- optionally runs "--script script.scm" before REPL
  • Loading branch information
christopherlam committed Oct 8, 2023
1 parent a068dfd commit be38331
Showing 1 changed file with 124 additions and 0 deletions.
124 changes: 124 additions & 0 deletions gnucash/gnucash-cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@
#include <glib/gi18n.h>
#include <gnc-engine.h>
#include <gnc-prefs.h>
#include "gnc-session.h"
#include "gnc-prefs-utils.h"
#include "libguile.h"

#include <boost/locale.hpp>
#include <boost/optional.hpp>
#ifdef __MINGW32__
#include <boost/nowide/args.hpp>
#endif
#include <iostream>
#include <filesystem>
#include <gnc-quotes.hpp>

namespace bl = boost::locale;
Expand All @@ -62,6 +66,11 @@ namespace Gnucash {
boost::optional <std::string> m_namespace;
bool m_verbose = false;

boost::optional <std::string> m_script;
boost::optional <std::string> m_language;
bool m_interactive;
bool m_open_readwrite;

boost::optional <std::string> m_report_cmd;
boost::optional <std::string> m_report_name;
boost::optional <std::string> m_export_type;
Expand Down Expand Up @@ -107,6 +116,15 @@ Gnucash::GnucashCli::configure_program_options (void)
m_opt_desc_display->add (quotes_options);
m_opt_desc_all.add (quotes_options);

bpo::options_description cli_options(_("Scripting and Interactive Session Options"));
cli_options.add_options()
("script,S", bpo::value (&m_script), "Script to run")
("interactive,I", bpo::bool_switch (&m_interactive), "Interactive session after possibly running script")
("language,L", bpo::value (&m_language), "Specify language for script or interactive session; guile (default) or python")
("readwrite,W", bpo::bool_switch (&m_open_readwrite), "Open datafile read-write for script and/or interactive session");
m_opt_desc_display->add (cli_options);
m_opt_desc_all.add (cli_options);

bpo::options_description report_options(_("Report Generation Options"));
report_options.add_options()
("report,R", bpo::value (&m_report_cmd),
Expand All @@ -126,11 +144,117 @@ may be specified to describe some saved options.\n"

}

struct cli_struct
{
boost::optional<std::string> script;
bool interactive;
bool verbose;
};

static void
run_guile_cli (void *data, [[maybe_unused]] int argc, [[maybe_unused]] char **argv)
{
auto args = static_cast<cli_struct*>(data);
scm_c_use_module ("system repl repl");
scm_c_use_module ("gnucash core-utils");
scm_c_use_module ("gnucash engine");
scm_c_use_module ("gnucash app-utils");
scm_c_use_module ("gnucash report");
scm_c_use_module ("ice-9 readline");
scm_c_eval_string ("(activate-readline)");
if (args->script)
{
if (args->verbose)
std::cout << "Running script from " << *args->script << "...";
scm_c_primitive_load (args->script->c_str());
if (args->verbose)
std::cout << "success!" << std::endl;
}
if (args->interactive)
{
if (args->verbose)
std::cout << "Starting CLI... " << std::endl;
scm_c_eval_string ("(start-repl)");
}
if (gnc_current_session_exist())
{
if (args->verbose)
std::cout << "Warning: session still open. Clearing session..." << std::endl;
gnc_clear_current_session ();
}
}

static void load_file (std::string m_file_to_load, bool m_open_readwrite, bool m_verbose)
{
if (m_verbose)
std::cout << "\n\nLoading " << m_file_to_load
<< (m_open_readwrite ? " (r/w)" : " (readonly)");
auto session = gnc_get_current_session();
auto mode = m_open_readwrite ? SESSION_READ_ONLY : SESSION_NORMAL_OPEN;
while (true)
{
qof_session_begin (session, m_file_to_load.c_str(), mode);
auto io_err = qof_session_get_error (session);
switch (io_err)
{
case ERR_BACKEND_NO_ERR:
qof_session_load (session, NULL);
if (m_verbose)
std::cout << "... done!\n\n";
return;
case ERR_BACKEND_LOCKED:
case ERR_BACKEND_READONLY:
if (m_verbose)
std::cout << " (forced readonly)";
mode = SESSION_READ_ONLY;
break;
default:
if (m_verbose)
std::cout << "... unknown error. Abort." << std::endl;
exit (1);
}
}
}

int
Gnucash::GnucashCli::start ([[maybe_unused]] int argc, [[maybe_unused]] char **argv)
{
Gnucash::CoreApp::start();

if (m_interactive || m_script)
{
if (m_open_readwrite && !m_file_to_load)
{
std::cerr << "missing datafile to be open read/write!" << std::endl;
return 1;
}
if (m_script && (!std::filesystem::exists (*m_script) ||
std::filesystem::is_directory (*m_script)))
{
std::cerr << "invalid script " << *m_script << std::endl;
return 1;
}
gnc_prefs_init ();
cli_struct args = { m_script, m_interactive, m_verbose };
if (!m_language || *m_language == "guile")
{
if (m_file_to_load)
load_file (*m_file_to_load, m_open_readwrite, m_verbose);
scm_boot_guile (0, nullptr, run_guile_cli, &args);
return 0;
}
else if (*m_language == "python")
{
std::cerr << "don't know how to launch python" << std::endl;
return 1;
}
else
{
std::cerr << "Valid languages are 'python' or 'guile'" << std::endl;
return 1;
}
}

if (!m_quotes_cmd.empty())
{
if (m_quotes_cmd.front() == "info")
Expand Down

0 comments on commit be38331

Please sign in to comment.