diff --git a/appveyor.yml b/appveyor.yml index 4b220318..ef8dbbd8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: - C:\%cygwin%\%cygsetup% -qnNdOX -R C:/%cygwin% -l C:/%cygwin%/var/cache/setup -P libpcre2-devel -P libncurses-devel -P libreadline-devel -P zlib-devel -P libbz2-devel -P libsqlite3-devel -P libcurl-devel -P libarchive-devel build_script: - - C:\%cygwin%\bin\sh -lc "uname -a && gcc --version && cd /cygdrive/c/projects/lnav && ./autogen.sh && ./configure && make && strip src/lnav.exe && ldd src/lnav.exe" + - C:\%cygwin%\bin\sh -lc "uname -a && gcc --version && cd /cygdrive/c/projects/lnav && ./autogen.sh && ./configure && make -j4 && strip src/lnav.exe && ldd src/lnav.exe" artifacts: - path: src\lnav.exe diff --git a/src/Makefile.am b/src/Makefile.am index 58107223..24eaaf1d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -310,6 +310,7 @@ noinst_HEADERS = \ time_T.hh \ timer.hh \ top_status_source.hh \ + top_status_source.cfg.hh \ unique_path.hh \ url_loader.hh \ view_curses.hh \ diff --git a/src/base/func_util.hh b/src/base/func_util.hh index 760deb4d..01a2328c 100644 --- a/src/base/func_util.hh +++ b/src/base/func_util.hh @@ -68,6 +68,54 @@ struct noop_func { namespace lnav { namespace func { +class scoped_cb { +public: + class guard { + public: + explicit guard(scoped_cb* owner) : g_owner(owner) {} + + guard(const guard&) = delete; + guard& operator=(const guard&) = delete; + + guard(guard&& gu) noexcept : g_owner(std::exchange(gu.g_owner, nullptr)) + { + } + + guard& operator=(guard&& gu) noexcept + { + this->g_owner = std::exchange(gu.g_owner, nullptr); + return *this; + } + + ~guard() + { + if (this->g_owner != nullptr) { + this->g_owner->s_callback = {}; + } + } + + private: + scoped_cb* g_owner; + }; + + guard install(std::function cb) + { + this->s_callback = std::move(cb); + + return guard{this}; + } + + void operator()() + { + if (s_callback) { + s_callback(); + } + } + +private: + std::function s_callback; +}; + template>{}, int> = 0> diff --git a/src/base/injector.bind.hh b/src/base/injector.bind.hh index 36ec5b9c..f240cd1d 100644 --- a/src/base/injector.bind.hh +++ b/src/base/injector.bind.hh @@ -90,6 +90,28 @@ struct bind : singleton_storage { return true; } + struct lifetime { + ~lifetime() + { + singleton_storage::ss_owner = nullptr; + singleton_storage::ss_data = nullptr; + } + }; + + template::value, bool> = true> + static lifetime to_scoped_singleton() noexcept + { + typename I::injectable* i = nullptr; + singleton_storage::ss_owner + = create_from_injectable(i)(); + singleton_storage::ss_data + = singleton_storage::ss_owner.get(); + singleton_storage::ss_scope = scope::singleton; + + return {}; + } + template static bool to_instance(T* (*f)(Args...)) noexcept { diff --git a/src/base/injector.hh b/src/base/injector.hh index 87e3adad..c6848a69 100644 --- a/src/base/injector.hh +++ b/src/base/injector.hh @@ -55,16 +55,21 @@ void force_linking(Annotation anno); template using void_t = void; -template -struct has_injectable : std::false_type { +template +struct with_annotations { + T value; }; +template +struct has_injectable : std::false_type {}; + template -struct has_injectable> : std::true_type { -}; +struct has_injectable> : std::true_type {}; template struct singleton_storage { + static scope get_scope() { return ss_scope; } + static T* get() { static int _[] = {0, (force_linking(Annotations{}), 0)...}; @@ -158,20 +163,16 @@ get() } template -struct is_shared_ptr : std::false_type { -}; +struct is_shared_ptr : std::false_type {}; template -struct is_shared_ptr> : std::true_type { -}; +struct is_shared_ptr> : std::true_type {}; template -struct is_vector : std::false_type { -}; +struct is_vector : std::false_type {}; template -struct is_vector> : std::true_type { -}; +struct is_vector> : std::true_type {}; template std::function()> create_from_injectable(R (*)(IArgs...), @@ -179,22 +180,28 @@ std::function()> create_from_injectable(R (*)(IArgs...), template::value, - bool> = true, + std::enable_if_t::value, bool> + = true, std::enable_if_t::value, bool> = true> T get(Args&... args) { typename T::element_type::injectable* i = nullptr; + if (singleton_storage::get_scope() + == scope::singleton) + { + return singleton_storage::get_owner(); + } return create_from_injectable(i, args...)(); } -template::value, - bool> = true, - std::enable_if_t::value, bool> = true> +template< + typename T, + typename... Annotations, + std::enable_if_t::value, bool> + = true, + std::enable_if_t::value, bool> = true> T get() { diff --git a/src/bound_tags.hh b/src/bound_tags.hh index 2facf4ed..a24f38ce 100644 --- a/src/bound_tags.hh +++ b/src/bound_tags.hh @@ -34,8 +34,6 @@ struct last_relative_time_tag {}; -struct sqlite_db_tag {}; - struct sql_cmd_map_tag {}; enum { diff --git a/src/command_executor.cc b/src/command_executor.cc index 0d03183b..e9a0296f 100644 --- a/src/command_executor.cc +++ b/src/command_executor.cc @@ -66,8 +66,6 @@ SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format int sql_progress(const struct log_cursor& lc) { - static sig_atomic_t sql_counter = 0; - ssize_t total = lnav_data.ld_log_source.text_line_count(); off_t off = lc.lc_curr_line; @@ -83,42 +81,11 @@ sql_progress(const struct log_cursor& lc) return 1; } + static sig_atomic_t sql_counter = 0; + if (ui_periodic_timer::singleton().time_to_update(sql_counter)) { - struct timeval current_time = {0, 0}; - int ch; - - while ((ch = getch()) != ERR) { - if (current_time.tv_sec == 0) { - gettimeofday(¤t_time, nullptr); - } - lnav_data.ld_user_message_source.clear(); - - alerter::singleton().new_input(ch); - - lnav_data.ld_input_dispatcher.new_input(current_time, ch); - - lnav_data.ld_view_stack.top() | [ch](auto tc) { - lnav_data.ld_key_repeat_history.update(ch, tc->get_top()); - }; - - if (!lnav_data.ld_looping) { - // No reason to keep processing input after the - // user has quit. The view stack will also be - // empty, which will cause issues. - break; - } - } - lnav_data.ld_bottom_source.update_loading(off, total); - lnav_data.ld_top_source.update_time(); - lnav_data.ld_status[LNS_TOP].do_update(); - lnav_data.ld_status[LNS_BOTTOM].do_update(); - lnav_data.ld_rl_view->do_update(); - if (handle_winch()) { - layout_views(); - lnav_data.ld_view_stack.do_update(); - } - refresh(); + lnav_data.ld_status_refresher(); } return 0; @@ -132,9 +99,7 @@ sql_progress_finished() } lnav_data.ld_bottom_source.update_loading(0, 0); - lnav_data.ld_top_source.update_time(); - lnav_data.ld_status[LNS_TOP].do_update(); - lnav_data.ld_status[LNS_BOTTOM].do_update(); + lnav_data.ld_status_refresher(); lnav_data.ld_views[LNV_DB].redo_search(); } diff --git a/src/lnav.cc b/src/lnav.cc index 9d253bdb..5cfd31f1 100644 --- a/src/lnav.cc +++ b/src/lnav.cc @@ -230,8 +230,7 @@ static auto bound_active_files = injector::bind::to_instance( +[]() { return &lnav_data.ld_active_files; }); static auto bound_sqlite_db - = injector::bind, - sqlite_db_tag>::to_instance(&lnav_data.ld_db); + = injector::bind::to_instance(&lnav_data.ld_db); static auto bound_lnav_flags = injector::bind::to_instance( @@ -264,12 +263,6 @@ force_linking(last_relative_time_tag anno) { } -template<> -void -force_linking(sqlite_db_tag anno) -{ -} - template<> void force_linking(lnav_flags_tag anno) @@ -1005,6 +998,55 @@ wait_for_pipers() } } +struct refresh_status_bars { + refresh_status_bars(std::shared_ptr top_source) + : rsb_top_source(std::move(top_source)) + { + } + + using injectable + = refresh_status_bars(std::shared_ptr top_source); + + void doit() const + { + struct timeval current_time {}; + int ch; + + gettimeofday(¤t_time, nullptr); + while ((ch = getch()) != ERR) { + lnav_data.ld_user_message_source.clear(); + + alerter::singleton().new_input(ch); + + lnav_data.ld_input_dispatcher.new_input(current_time, ch); + + lnav_data.ld_view_stack.top() | [ch](auto tc) { + lnav_data.ld_key_repeat_history.update(ch, tc->get_top()); + }; + + if (!lnav_data.ld_looping) { + // No reason to keep processing input after the + // user has quit. The view stack will also be + // empty, which will cause issues. + break; + } + } + + this->rsb_top_source->update_time(current_time); + for (auto& sc : lnav_data.ld_status) { + sc.do_update(); + } + lnav_data.ld_rl_view->do_update(); + if (handle_winch()) { + layout_views(); + lnav_data.ld_view_stack.do_update(); + } + refresh(); + } + + std::shared_ptr rsb_top_source; +}; + static void looper() { @@ -1340,10 +1382,15 @@ looper() lnav_data.ld_spectro_source->ss_exec_context = &lnav_data.ld_exec_context; + auto top_status_lifetime + = injector::bind::to_scoped_singleton(); + + auto top_source = injector::get>(); + lnav_data.ld_status[LNS_TOP].set_top(0); lnav_data.ld_status[LNS_TOP].set_default_role( role_t::VCR_INACTIVE_STATUS); - lnav_data.ld_status[LNS_TOP].set_data_source(&lnav_data.ld_top_source); + lnav_data.ld_status[LNS_TOP].set_data_source(top_source.get()); lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc->get_height() + 1)); for (auto& stat_bar : lnav_data.ld_status) { stat_bar.set_window(lnav_data.ld_window); @@ -1381,7 +1428,7 @@ looper() }; { - input_dispatcher& id = lnav_data.ld_input_dispatcher; + auto& id = lnav_data.ld_input_dispatcher; id.id_escape_matcher = match_escape_seq; id.id_escape_handler = handle_keyseq; @@ -1412,7 +1459,15 @@ looper() }; } - ui_periodic_timer& timer = ui_periodic_timer::singleton(); + auto refresher_lifetime + = injector::bind::to_scoped_singleton(); + + auto refresher = injector::get>(); + + auto refresh_guard = lnav_data.ld_status_refresher.install( + [refresher]() { refresher->doit(); }); + + auto& timer = ui_periodic_timer::singleton(); struct timeval current_time; static sig_atomic_t index_counter; @@ -1450,7 +1505,7 @@ looper() gettimeofday(¤t_time, nullptr); - lnav_data.ld_top_source.update_time(current_time); + top_source->update_time(current_time); lnav_data.ld_preview_view.set_needs_update(); layout_views(); @@ -1562,7 +1617,7 @@ looper() lnav_data.ld_user_message_view.do_update(); if (ui_clock::now() >= next_status_update_time) { echo_views_stmt.execute(); - lnav_data.ld_top_source.update_user_msg(); + top_source->update_user_msg(); for (auto& sc : lnav_data.ld_status) { sc.do_update(); } @@ -2150,6 +2205,26 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%' } while (!done); } + // XXX + lnav_data.ld_log_source.set_preview_sql_filter(nullptr); + lnav_data.ld_log_source.set_sql_filter("", nullptr); + lnav_data.ld_log_source.set_sql_marker("", nullptr); + lnav_config_listener::unload_all(); + + { + sqlite3_stmt* stmt_iter = nullptr; + + do { + stmt_iter = sqlite3_next_stmt(lnav_data.ld_db.in(), stmt_iter); + if (stmt_iter != nullptr) { + const auto* stmt_sql = sqlite3_sql(stmt_iter); + + log_warning("unfinalized SQL statement: %s", stmt_sql); + ensure(false); + } + } while (stmt_iter != nullptr); + } + for (auto& drop_stmt : tables_to_drop) { sqlite3_exec(lnav_data.ld_db.in(), drop_stmt.c_str(), diff --git a/src/lnav.hh b/src/lnav.hh index 8460cd78..f90766b4 100644 --- a/src/lnav.hh +++ b/src/lnav.hh @@ -71,7 +71,6 @@ #include "sql_util.hh" #include "statusview_curses.hh" #include "textfile_sub_source.hh" -#include "top_status_source.hh" #include "view_helpers.hh" class spectrogram_source; @@ -184,7 +183,6 @@ struct lnav_data_t { ln_mode_t ld_last_config_mode{ln_mode_t::FILTER}; statusview_curses ld_status[LNS__MAX]; - top_status_source ld_top_source; bottom_status_source ld_bottom_source; filter_status_source ld_filter_status_source; filter_help_status_source ld_filter_help_status_source; @@ -259,6 +257,8 @@ struct lnav_data_t { bool ld_initial_build{false}; bool ld_show_help_view{false}; + lnav::func::scoped_cb ld_status_refresher; + ghc::filesystem::file_time_type ld_last_dot_lnav_time; }; diff --git a/src/lnav.indexing.cc b/src/lnav.indexing.cc index af771f4c..d39c784a 100644 --- a/src/lnav.indexing.cc +++ b/src/lnav.indexing.cc @@ -85,10 +85,7 @@ do_observer_update(const std::shared_ptr& lf) if (isendwin()) { return; } - lnav_data.ld_top_source.update_time(); - for (auto& sc : lnav_data.ld_status) { - sc.do_update(); - } + lnav_data.ld_status_refresher(); if (lf && lnav_data.ld_mode == ln_mode_t::FILES && !lnav_data.ld_initial_build) { @@ -437,15 +434,7 @@ rescan_files(bool req) if (!done && !(lnav_data.ld_flags & LNF_HEADLESS)) { lnav_data.ld_files_view.set_needs_update(); lnav_data.ld_files_view.do_update(); - lnav_data.ld_top_source.update_time(); - lnav_data.ld_status[LNS_TOP].do_update(); - lnav_data.ld_status[LNS_BOTTOM].do_update(); - lnav_data.ld_rl_view->do_update(); - if (handle_winch()) { - layout_views(); - lnav_data.ld_view_stack.do_update(); - } - refresh(); + lnav_data.ld_status_refresher(); } } while (!done && lnav_data.ld_looping); return true; diff --git a/src/lnav_config.cc b/src/lnav_config.cc index 4d2f76a6..9ea617fa 100644 --- a/src/lnav_config.cc +++ b/src/lnav_config.cc @@ -96,6 +96,9 @@ static auto scc = injector::bind::to_instance( static auto lsc = injector::bind::to_instance( +[]() { return &lnav_config.lc_log_source; }); +static auto tssc = injector::bind::to_instance( + +[]() { return &lnav_config.lc_top_status_cfg; }); + bool check_experimental(const char* feature_name) { @@ -961,7 +964,8 @@ static const struct json_path_container ui_handlers = { .with_description("The format for the clock displayed in " "the top-left corner using strftime(3) conversions") .with_example("%a %b %d %H:%M:%S %Z") - .for_field(&_lnav_config::lc_ui_clock_format), + .for_field(&_lnav_config::lc_top_status_cfg, + &top_status_source_cfg::tssc_clock_format), yajlpp::property_handler("dim-text") .with_synopsis("bool") .with_description("Reduce the brightness of text (useful for xterms). " diff --git a/src/lnav_config.hh b/src/lnav_config.hh index dcce3931..f9df8609 100644 --- a/src/lnav_config.hh +++ b/src/lnav_config.hh @@ -52,6 +52,7 @@ #include "styling.hh" #include "sysclip.cfg.hh" #include "tailer/tailer.looper.cfg.hh" +#include "top_status_source.cfg.hh" /** * Check if an experimental feature should be enabled by @@ -85,7 +86,7 @@ struct key_map { }; struct _lnav_config { - std::string lc_ui_clock_format; + top_status_source_cfg lc_top_status_cfg; bool lc_ui_dim_text; bool lc_ui_default_colors{true}; std::string lc_ui_keymap; diff --git a/src/lnav_config_fwd.hh b/src/lnav_config_fwd.hh index 1de33897..0b371a33 100644 --- a/src/lnav_config_fwd.hh +++ b/src/lnav_config_fwd.hh @@ -52,6 +52,16 @@ public: virtual void reload_config(error_reporter& reporter) {} + virtual void unload_config() {} + + static void unload_all() { + auto* lcl = LISTENER_LIST; + while (lcl != nullptr) { + lcl->unload_config(); + lcl = lcl->lcl_next; + } + } + static lnav_config_listener* LISTENER_LIST; lnav_config_listener* lcl_next; diff --git a/src/log.watch.cc b/src/log.watch.cc index 5e4b4b94..e90b2eb5 100644 --- a/src/log.watch.cc +++ b/src/log.watch.cc @@ -52,8 +52,7 @@ struct compiled_watch_expr { struct expressions : public lnav_config_listener { void reload_config(error_reporter& reporter) override { - auto& lnav_db = injector::get&, - sqlite_db_tag>(); + auto& lnav_db = injector::get(); if (lnav_db.in() == nullptr) { log_warning("db not initialized yet!"); @@ -99,6 +98,10 @@ struct expressions : public lnav_config_listener { } } + void unload_config() override { + this->e_watch_exprs.clear(); + } + std::map e_watch_exprs; }; @@ -114,9 +117,7 @@ eval_with(logfile& lf, logfile::iterator ll) return; } - static auto& lnav_db - = injector::get&, - sqlite_db_tag>(); + static auto& lnav_db = injector::get(); char timestamp_buffer[64] = ""; shared_buffer_ref raw_sbr; diff --git a/src/logfile_sub_source.cc b/src/logfile_sub_source.cc index b4807389..db87ae8f 100644 --- a/src/logfile_sub_source.cc +++ b/src/logfile_sub_source.cc @@ -1334,12 +1334,17 @@ logfile_sub_source::set_sql_marker(std::string stmt_str, sqlite3_stmt* stmt) } } + this->lss_marker_stmt_text = std::move(stmt_str); + this->lss_marker_stmt = stmt; + + if (this->tss_view == nullptr) { + return Ok(); + } + auto& vis_bm = this->tss_view->get_bookmarks(); auto& expr_marks_bv = vis_bm[&textview_curses::BM_USER_EXPR]; expr_marks_bv.clear(); - this->lss_marker_stmt_text = std::move(stmt_str); - this->lss_marker_stmt = stmt; if (this->lss_index_delegate) { this->lss_index_delegate->index_start(*this); } diff --git a/src/session.export.cc b/src/session.export.cc index 6937ef8a..109627b4 100644 --- a/src/session.export.cc +++ b/src/session.export.cc @@ -175,9 +175,7 @@ replace_home_dir(std::string path) Result export_to(FILE* file) { - static auto& lnav_db - = injector::get&, - sqlite_db_tag>(); + static auto& lnav_db = injector::get(); static const char* BOOKMARK_QUERY = R"( SELECT log_time_msecs, log_format, log_mark, log_comment, log_tags, log_line_hash diff --git a/src/sql_commands.cc b/src/sql_commands.cc index ceaddd07..e1453316 100644 --- a/src/sql_commands.cc +++ b/src/sql_commands.cc @@ -45,9 +45,7 @@ sql_cmd_dump(exec_context& ec, std::string cmdline, std::vector& args) { - static auto& lnav_db - = injector::get&, - sqlite_db_tag>(); + static auto& lnav_db = injector::get(); static auto& lnav_flags = injector::get(); std::string retval; @@ -90,9 +88,7 @@ sql_cmd_read(exec_context& ec, std::string cmdline, std::vector& args) { - static auto& lnav_db - = injector::get&, - sqlite_db_tag>(); + static auto& lnav_db = injector::get(); static auto& lnav_flags = injector::get(); std::string retval; diff --git a/src/sqlitepp.hh b/src/sqlitepp.hh index aa0ef525..7845a232 100644 --- a/src/sqlitepp.hh +++ b/src/sqlitepp.hh @@ -43,6 +43,8 @@ /* XXX figure out how to do this with the template */ void sqlite_close_wrapper(void* mem); +using auto_sqlite3 = auto_mem; + namespace sqlitepp { inline auto_mem diff --git a/src/textfile_sub_source.cc b/src/textfile_sub_source.cc index 95586bff..ce3d084c 100644 --- a/src/textfile_sub_source.cc +++ b/src/textfile_sub_source.cc @@ -455,9 +455,7 @@ textfile_sub_source::rescan_files( textfile_sub_source::scan_callback& callback, nonstd::optional deadline) { - static auto& lnav_db - = injector::get&, - sqlite_db_tag>(); + static auto& lnav_db = injector::get(); file_iterator iter; bool retval = false; diff --git a/src/top_status_source.cc b/src/top_status_source.cc index 1c7c1d5e..59b95e57 100644 --- a/src/top_status_source.cc +++ b/src/top_status_source.cc @@ -29,22 +29,31 @@ #include "top_status_source.hh" -#include - #include "base/injector.hh" -#include "bound_tags.hh" #include "config.h" #include "lnav.hh" -#include "lnav_config.hh" -#include "logfile_sub_source.hh" #include "md2attr_line.hh" #include "md4cpp.hh" #include "shlex.hh" #include "shlex.resolver.hh" #include "sql_util.hh" #include "sqlitepp.client.hh" +#include "top_status_source.cfg.hh" -top_status_source::top_status_source() +static const char* MSG_QUERY = R"( +SELECT message FROM lnav_user_notifications + WHERE message IS NOT NULL AND + (expiration IS NULL OR expiration > datetime('now')) AND + (views IS NULL OR + json_contains(views, (SELECT name FROM lnav_top_view))) + ORDER BY priority DESC + LIMIT 1 +)"; + +top_status_source::top_status_source(auto_sqlite3& db, + const top_status_source_cfg& cfg) + : tss_config(cfg), + tss_user_msgs_stmt(prepare_stmt(db.in(), MSG_QUERY).unwrap()) { this->tss_fields[TSF_TIME].set_width(28); this->tss_fields[TSF_TIME].set_role(role_t::VCR_STATUS_INFO); @@ -62,7 +71,7 @@ top_status_source::update_time(const timeval& current_time) buffer[0] = ' '; strftime(&buffer[1], sizeof(buffer) - 1, - lnav_config.lc_ui_clock_format.c_str(), + this->tss_config.tssc_clock_format.c_str(), localtime(¤t_time.tv_sec)); sf.set_value(buffer); } @@ -76,40 +85,14 @@ top_status_source::update_time() this->update_time(tv); } -static const char* MSG_QUERY = R"( -SELECT message FROM lnav_user_notifications - WHERE message IS NOT NULL AND - (expiration IS NULL OR expiration > datetime('now')) AND - (views IS NULL OR - json_contains(views, (SELECT name FROM lnav_top_view))) - ORDER BY priority DESC - LIMIT 1 -)"; - -struct user_msg_stmt { - user_msg_stmt() - : ums_stmt( - prepare_stmt(injector::get&, - sqlite_db_tag>() - .in(), - MSG_QUERY) - .unwrap()) - { - } - - prepared_stmt ums_stmt; -}; - void top_status_source::update_user_msg() { - static thread_local user_msg_stmt um_stmt; - auto& al = this->tss_fields[TSF_USER_MSG].get_value(); al.clear(); - um_stmt.ums_stmt.reset(); - auto fetch_res = um_stmt.ums_stmt.fetch_row(); + this->tss_user_msgs_stmt.reset(); + auto fetch_res = this->tss_user_msgs_stmt.fetch_row(); fetch_res.match( [&al](const std::string& value) { shlex lexer(value); diff --git a/src/top_status_source.cfg.hh b/src/top_status_source.cfg.hh new file mode 100644 index 00000000..825f5e70 --- /dev/null +++ b/src/top_status_source.cfg.hh @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2022, Timothy Stack + * + * 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 Timothy Stack 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 REGENTS 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 REGENTS 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. + */ + +#ifndef lnav_top_status_source_cfg_hh +#define lnav_top_status_source_cfg_hh + +struct top_status_source_cfg { + std::string tssc_clock_format; +}; + +#endif diff --git a/src/top_status_source.hh b/src/top_status_source.hh index 58668ac6..5c76796b 100644 --- a/src/top_status_source.hh +++ b/src/top_status_source.hh @@ -32,19 +32,30 @@ #include +#include + +#include "base/injector.hh" +#include "bound_tags.hh" #include "listview_curses.hh" +#include "sql_util.hh" +#include "sqlitepp.client.hh" #include "statusview_curses.hh" +#include "top_status_source.cfg.hh" class top_status_source : public status_data_source { public: - typedef enum { + enum field_t { TSF_TIME, TSF_USER_MSG, TSF__MAX - } field_t; + }; - top_status_source(); + explicit top_status_source(auto_sqlite3& db, + const top_status_source_cfg& cfg); + + using injectable + = top_status_source(auto_sqlite3& db, const top_status_source_cfg& cfg); size_t statusview_fields() override { return TSF__MAX; } @@ -60,7 +71,9 @@ public: void update_user_msg(); private: + const top_status_source_cfg& tss_config; status_field tss_fields[TSF__MAX]; + prepared_stmt tss_user_msgs_stmt; }; #endif diff --git a/test/expected/expected.am b/test/expected/expected.am index c4f7d849..15f62438 100644 --- a/test/expected/expected.am +++ b/test/expected/expected.am @@ -294,6 +294,8 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_logfile.sh_09bd16e044302f6b121092534708594bdad11b5a.out \ $(srcdir)/%reldir%/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.err \ $(srcdir)/%reldir%/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.out \ + $(srcdir)/%reldir%/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.err \ + $(srcdir)/%reldir%/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out \ $(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.err \ $(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out \ $(srcdir)/%reldir%/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err \ diff --git a/test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.err b/test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out b/test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out new file mode 100644 index 00000000..1f8e9bf4 --- /dev/null +++ b/test/expected/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out @@ -0,0 +1,2 @@ +basename(filepath)  descriptor  mimetype  content  +logfile_syslog.1.gz net.zlib.gzip.header application/json {"name":"logfile_syslog.1","mtime":"2007-11-03T16:23:00.000","comment":""}  diff --git a/test/test_logfile.sh b/test/test_logfile.sh index 965bc1c5..12ab125a 100644 --- a/test/test_logfile.sh +++ b/test/test_logfile.sh @@ -697,5 +697,5 @@ run_cap_test ${lnav_test} -n \ ${test_dir}/logfile_ansi.1 run_cap_test ${lnav_test} -n \ - -c ';SELECT * FROM lnav_file_metadata' \ + -c ';SELECT basename(filepath),descriptor,mimetype,content FROM lnav_file_metadata' \ logfile_syslog.1.gz diff --git a/test/test_stubs.cc b/test/test_stubs.cc index 074d420d..7d273b39 100644 --- a/test/test_stubs.cc +++ b/test/test_stubs.cc @@ -72,11 +72,6 @@ rebuild_indexes_repeatedly() readline_context::command_map_t lnav_commands; namespace injector { -template<> -void -force_linking(sqlite_db_tag anno) -{ -} template<> void diff --git a/test/test_top_status.cc b/test/test_top_status.cc index 08b6548f..769c3461 100644 --- a/test/test_top_status.cc +++ b/test/test_top_status.cc @@ -51,7 +51,15 @@ main(int argc, char* argv[]) { int retval = EXIT_SUCCESS; - top_status_source tss; + auto_sqlite3 db; + + if (sqlite3_open(":memory:", db.out()) != SQLITE_OK) { + fprintf(stderr, "error: unable to create sqlite memory database\n"); + exit(EXIT_FAILURE); + } + + top_status_source_cfg cfg; + top_status_source tss(db, cfg); setenv("HOME", "/", 1); @@ -72,7 +80,7 @@ main(int argc, char* argv[]) tss.update_time(); assert(val.get_string() != sf.get_value().get_string()); - lnav_config.lc_ui_clock_format = "abc"; + cfg.tssc_clock_format = "abc"; tss.update_time(); val = sf.get_value(); assert(val.get_string() == " abc");