From 8d6d15109062ff1cbe9047e349bc7c9beae5584f Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Fri, 21 Feb 2020 01:13:29 +0200 Subject: [PATCH] server: support readline history / persistence When using readline, remember the last 50 commands; persist. --- lib/mu-runtime.cc | 3 ++ lib/mu-runtime.h | 3 +- mu/mu-cmd-server.cc | 88 ++++++++++++++++++++++++++++++++++----------- 3 files changed, 72 insertions(+), 22 deletions(-) diff --git a/lib/mu-runtime.cc b/lib/mu-runtime.cc index ff351640..b8fb6065 100644 --- a/lib/mu-runtime.cc +++ b/lib/mu-runtime.cc @@ -39,6 +39,8 @@ init_paths_xdg () { RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, g_get_user_cache_dir() + Sepa + Mu + Sepa + XapianDir); + RuntimePaths.emplace(MU_RUNTIME_PATH_CACHE, g_get_user_cache_dir() + + Sepa + Mu); RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, g_get_user_cache_dir() + Sepa + Mu + Sepa + PartsDir); RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, g_get_user_cache_dir() + @@ -51,6 +53,7 @@ static void init_paths_muhome (const char *muhome) { RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, muhome + Sepa + XapianDir); + RuntimePaths.emplace(MU_RUNTIME_PATH_CACHE, muhome); RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, muhome + Sepa + PartsDir); RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, muhome + Sepa + LogDir); RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, muhome + Sepa + Bookmarks); diff --git a/lib/mu-runtime.h b/lib/mu-runtime.h index 2440f71a..43269cc7 100644 --- a/lib/mu-runtime.h +++ b/lib/mu-runtime.h @@ -47,6 +47,7 @@ void mu_runtime_uninit (void); typedef enum { MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */ MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */ + MU_RUNTIME_PATH_CACHE, /* mu cache path for attachments etc. */ MU_RUNTIME_PATH_MIMECACHE, /* mu cache path for attachments etc. */ MU_RUNTIME_PATH_LOGDIR, /* mu path for log files */ @@ -59,7 +60,7 @@ typedef enum { * @return ma string which should be not be modified/freed, or NULL in * case of error. */ -const char* mu_runtime_path (MuRuntimePath path); +const char* mu_runtime_path (MuRuntimePath path); G_END_DECLS diff --git a/mu/mu-cmd-server.cc b/mu/mu-cmd-server.cc index 5916f33c..38d6c937 100644 --- a/mu/mu-cmd-server.cc +++ b/mu/mu-cmd-server.cc @@ -1279,35 +1279,68 @@ make_command_map (Context& context) } -static std::string -read_line (Context& context) -{ -// if we don't have readline, use the simple version. -#ifndef HAVE_LIBREADLINE - std::cout << ";; mu> "; - std::string line; - if (!std::getline(std::cin, line)) - context.do_quit = true; - return line; -#else /*!HAVE_LIBREADLINE*/ +struct Readline { + Readline (const std::string& histpath, size_t max_lines); + ~Readline(); + std::string read_line(bool& do_quit); + void save_line(const std::string& line); + std::string histpath_; + size_t max_lines_{0}; +}; +/// Wrapper around readline (if available) or nothing otherwise. +#if defined(HAVE_LIBREADLINE) && defined(HAVE_READLINE_HISTORY) && defined(bla) +Readline::Readline (const std::string& histpath, size_t max_lines): + histpath_{histpath}, max_lines_{max_lines} +{ + rl_bind_key('\t', rl_insert); // default (filenames) is not useful + using_history(); + read_history (histpath_.c_str()); + + if (max_lines_ > 0) + stifle_history(max_lines_); +} + +Readline::~Readline () { + write_history(histpath_.c_str()); + if (max_lines_ > 0) + history_truncate_file (histpath_.c_str(), max_lines_); +} + +std::string +Readline::read_line(bool& do_quit) +{ auto buf = readline(";; mu% "); if (!buf) { - context.do_quit = true; + do_quit = true; return {}; } -#ifdef HAVE_READLINE_HISTORY - else if (buf[0]) - add_history(buf); -#endif /*HAVE_LIBREADLINE_HISTORY*/ - std::string line{buf}; ::free (buf); - return line; -#endif /*HAVE_LIBREADLINE*/ } +void +Readline::save_line(const std::string& line) +{ + add_history(line.c_str()); +} +#else +Readline::Readline (const std::string& histpath, size_t max_lines) {} +Readline::~Readline() {} +void Readline::save_line(const std::string& line) {} + +std::string +Readline::read_line(bool& do_quit) +{ + std::string line; + std::cout << ";; mu> "; + if (!std::getline(std::cin, line)) + do_quit = true; + return line; +} +#endif // ! defined(HAVE_LIBREADLINE) && defined(HAVE_READLINE_HISTORY) + MuError mu_cmd_server (MuConfig *opts, GError **err) try @@ -1322,6 +1355,9 @@ mu_cmd_server (MuConfig *opts, GError **err) try Context context{opts}; context.command_map = make_command_map (context); + const auto histpath{std::string{mu_runtime_path(MU_RUNTIME_PATH_CACHE)} + "/history"}; + Readline readline(histpath, 50); + install_sig_handler(); std::cout << ";; Welcome to the " << PACKAGE_STRING << " command-server\n" << ";; Use (help) to get a list of commands, (quit) to quit.\n"; @@ -1330,11 +1366,14 @@ mu_cmd_server (MuConfig *opts, GError **err) try std::string line; try { - line = read_line(context); + line = readline.read_line(context.do_quit); if (line.find_first_not_of(" \t") == std::string::npos) continue; // skip whitespace-only lines - invoke(context.command_map, Sexp::parse(line)); + auto call{Sexp::parse(line)}; + readline.save_line(line); + + invoke(context.command_map, call); } catch (const Error& er) { std::cerr << ";; error: " << er.what() << "\n"; @@ -1343,7 +1382,14 @@ mu_cmd_server (MuConfig *opts, GError **err) try } } +#ifdef HAVE_READLINE_HISTORY + const auto history{std::string{mu_runtime_path(MU_RUNTIME_PATH_CACHE)} + "/history"}; + read_history (history.c_str()); + stifle_history(20); // remember last 20. +#endif /*HAVE_READLINE_HISTORY*/ + return MU_OK; + } catch (const Error& er) { g_set_error(err, MU_ERROR_DOMAIN, MU_ERROR, "%s", er.what()); return MU_ERROR;