[fini] fix a destruction ordering issue

This commit is contained in:
Tim Stack 2022-09-21 18:54:45 -07:00
parent 59ec0b4794
commit 93a53c4224
28 changed files with 316 additions and 156 deletions

View File

@ -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 - 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: 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: artifacts:
- path: src\lnav.exe - path: src\lnav.exe

View File

@ -310,6 +310,7 @@ noinst_HEADERS = \
time_T.hh \ time_T.hh \
timer.hh \ timer.hh \
top_status_source.hh \ top_status_source.hh \
top_status_source.cfg.hh \
unique_path.hh \ unique_path.hh \
url_loader.hh \ url_loader.hh \
view_curses.hh \ view_curses.hh \

View File

@ -68,6 +68,54 @@ struct noop_func {
namespace lnav { namespace lnav {
namespace func { 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<void()> cb)
{
this->s_callback = std::move(cb);
return guard{this};
}
void operator()()
{
if (s_callback) {
s_callback();
}
}
private:
std::function<void()> s_callback;
};
template<typename Fn, template<typename Fn,
typename... Args, typename... Args,
std::enable_if_t<std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0> std::enable_if_t<std::is_member_pointer<std::decay_t<Fn>>{}, int> = 0>

View File

@ -90,6 +90,28 @@ struct bind : singleton_storage<T, Annotations...> {
return true; return true;
} }
struct lifetime {
~lifetime()
{
singleton_storage<T, Annotations...>::ss_owner = nullptr;
singleton_storage<T, Annotations...>::ss_data = nullptr;
}
};
template<typename I = T,
std::enable_if_t<has_injectable<I>::value, bool> = true>
static lifetime to_scoped_singleton() noexcept
{
typename I::injectable* i = nullptr;
singleton_storage<T, Annotations...>::ss_owner
= create_from_injectable<I>(i)();
singleton_storage<T, Annotations...>::ss_data
= singleton_storage<T, Annotations...>::ss_owner.get();
singleton_storage<T, Annotations...>::ss_scope = scope::singleton;
return {};
}
template<typename... Args> template<typename... Args>
static bool to_instance(T* (*f)(Args...)) noexcept static bool to_instance(T* (*f)(Args...)) noexcept
{ {

View File

@ -55,16 +55,21 @@ void force_linking(Annotation anno);
template<class...> template<class...>
using void_t = void; using void_t = void;
template<class, class = void> template<typename T, typename... Annotations>
struct has_injectable : std::false_type { struct with_annotations {
T value;
}; };
template<class, class = void>
struct has_injectable : std::false_type {};
template<class T> template<class T>
struct has_injectable<T, void_t<typename T::injectable>> : std::true_type { struct has_injectable<T, void_t<typename T::injectable>> : std::true_type {};
};
template<typename T, typename... Annotations> template<typename T, typename... Annotations>
struct singleton_storage { struct singleton_storage {
static scope get_scope() { return ss_scope; }
static T* get() static T* get()
{ {
static int _[] = {0, (force_linking(Annotations{}), 0)...}; static int _[] = {0, (force_linking(Annotations{}), 0)...};
@ -158,20 +163,16 @@ get()
} }
template<class T> template<class T>
struct is_shared_ptr : std::false_type { struct is_shared_ptr : std::false_type {};
};
template<class T> template<class T>
struct is_shared_ptr<std::shared_ptr<T>> : std::true_type { struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
};
template<class T> template<class T>
struct is_vector : std::false_type { struct is_vector : std::false_type {};
};
template<class T> template<class T>
struct is_vector<std::vector<T>> : std::true_type { struct is_vector<std::vector<T>> : std::true_type {};
};
template<typename I, typename R, typename... IArgs, typename... Args> template<typename I, typename R, typename... IArgs, typename... Args>
std::function<std::shared_ptr<I>()> create_from_injectable(R (*)(IArgs...), std::function<std::shared_ptr<I>()> create_from_injectable(R (*)(IArgs...),
@ -179,22 +180,28 @@ std::function<std::shared_ptr<I>()> create_from_injectable(R (*)(IArgs...),
template<typename T, template<typename T,
typename... Args, typename... Args,
std::enable_if_t<has_injectable<typename T::element_type>::value, std::enable_if_t<has_injectable<typename T::element_type>::value, bool>
bool> = true, = true,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true> std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T T
get(Args&... args) get(Args&... args)
{ {
typename T::element_type::injectable* i = nullptr; typename T::element_type::injectable* i = nullptr;
if (singleton_storage<typename T::element_type>::get_scope()
== scope::singleton)
{
return singleton_storage<typename T::element_type>::get_owner();
}
return create_from_injectable<typename T::element_type>(i, args...)(); return create_from_injectable<typename T::element_type>(i, args...)();
} }
template<typename T, template<
typename... Annotations, typename T,
std::enable_if_t<!has_injectable<typename T::element_type>::value, typename... Annotations,
bool> = true, std::enable_if_t<!has_injectable<typename T::element_type>::value, bool>
std::enable_if_t<is_shared_ptr<T>::value, bool> = true> = true,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T T
get() get()
{ {

View File

@ -34,8 +34,6 @@
struct last_relative_time_tag {}; struct last_relative_time_tag {};
struct sqlite_db_tag {};
struct sql_cmd_map_tag {}; struct sql_cmd_map_tag {};
enum { enum {

View File

@ -66,8 +66,6 @@ SELECT count(*) AS total, min(log_line) AS log_line, log_msg_format
int int
sql_progress(const struct log_cursor& lc) 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(); ssize_t total = lnav_data.ld_log_source.text_line_count();
off_t off = lc.lc_curr_line; off_t off = lc.lc_curr_line;
@ -83,42 +81,11 @@ sql_progress(const struct log_cursor& lc)
return 1; return 1;
} }
static sig_atomic_t sql_counter = 0;
if (ui_periodic_timer::singleton().time_to_update(sql_counter)) { 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(&current_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_bottom_source.update_loading(off, total);
lnav_data.ld_top_source.update_time(); lnav_data.ld_status_refresher();
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();
} }
return 0; return 0;
@ -132,9 +99,7 @@ sql_progress_finished()
} }
lnav_data.ld_bottom_source.update_loading(0, 0); lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_top_source.update_time(); lnav_data.ld_status_refresher();
lnav_data.ld_status[LNS_TOP].do_update();
lnav_data.ld_status[LNS_BOTTOM].do_update();
lnav_data.ld_views[LNV_DB].redo_search(); lnav_data.ld_views[LNV_DB].redo_search();
} }

View File

@ -230,8 +230,7 @@ static auto bound_active_files = injector::bind<file_collection>::to_instance(
+[]() { return &lnav_data.ld_active_files; }); +[]() { return &lnav_data.ld_active_files; });
static auto bound_sqlite_db static auto bound_sqlite_db
= injector::bind<auto_mem<sqlite3, sqlite_close_wrapper>, = injector::bind<auto_sqlite3>::to_instance(&lnav_data.ld_db);
sqlite_db_tag>::to_instance(&lnav_data.ld_db);
static auto bound_lnav_flags static auto bound_lnav_flags
= injector::bind<unsigned long, lnav_flags_tag>::to_instance( = injector::bind<unsigned long, lnav_flags_tag>::to_instance(
@ -264,12 +263,6 @@ force_linking(last_relative_time_tag anno)
{ {
} }
template<>
void
force_linking(sqlite_db_tag anno)
{
}
template<> template<>
void void
force_linking(lnav_flags_tag anno) force_linking(lnav_flags_tag anno)
@ -1005,6 +998,55 @@ wait_for_pipers()
} }
} }
struct refresh_status_bars {
refresh_status_bars(std::shared_ptr<top_status_source> top_source)
: rsb_top_source(std::move(top_source))
{
}
using injectable
= refresh_status_bars(std::shared_ptr<top_status_source> top_source);
void doit() const
{
struct timeval current_time {};
int ch;
gettimeofday(&current_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<top_status_source> rsb_top_source;
};
static void static void
looper() looper()
{ {
@ -1340,10 +1382,15 @@ looper()
lnav_data.ld_spectro_source->ss_exec_context lnav_data.ld_spectro_source->ss_exec_context
= &lnav_data.ld_exec_context; = &lnav_data.ld_exec_context;
auto top_status_lifetime
= injector::bind<top_status_source>::to_scoped_singleton();
auto top_source = injector::get<std::shared_ptr<top_status_source>>();
lnav_data.ld_status[LNS_TOP].set_top(0); lnav_data.ld_status[LNS_TOP].set_top(0);
lnav_data.ld_status[LNS_TOP].set_default_role( lnav_data.ld_status[LNS_TOP].set_default_role(
role_t::VCR_INACTIVE_STATUS); 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)); lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc->get_height() + 1));
for (auto& stat_bar : lnav_data.ld_status) { for (auto& stat_bar : lnav_data.ld_status) {
stat_bar.set_window(lnav_data.ld_window); 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_matcher = match_escape_seq;
id.id_escape_handler = handle_keyseq; id.id_escape_handler = handle_keyseq;
@ -1412,7 +1459,15 @@ looper()
}; };
} }
ui_periodic_timer& timer = ui_periodic_timer::singleton(); auto refresher_lifetime
= injector::bind<refresh_status_bars>::to_scoped_singleton();
auto refresher = injector::get<std::shared_ptr<refresh_status_bars>>();
auto refresh_guard = lnav_data.ld_status_refresher.install(
[refresher]() { refresher->doit(); });
auto& timer = ui_periodic_timer::singleton();
struct timeval current_time; struct timeval current_time;
static sig_atomic_t index_counter; static sig_atomic_t index_counter;
@ -1450,7 +1505,7 @@ looper()
gettimeofday(&current_time, nullptr); gettimeofday(&current_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(); lnav_data.ld_preview_view.set_needs_update();
layout_views(); layout_views();
@ -1562,7 +1617,7 @@ looper()
lnav_data.ld_user_message_view.do_update(); lnav_data.ld_user_message_view.do_update();
if (ui_clock::now() >= next_status_update_time) { if (ui_clock::now() >= next_status_update_time) {
echo_views_stmt.execute(); echo_views_stmt.execute();
lnav_data.ld_top_source.update_user_msg(); top_source->update_user_msg();
for (auto& sc : lnav_data.ld_status) { for (auto& sc : lnav_data.ld_status) {
sc.do_update(); sc.do_update();
} }
@ -2150,6 +2205,26 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
} while (!done); } 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) { for (auto& drop_stmt : tables_to_drop) {
sqlite3_exec(lnav_data.ld_db.in(), sqlite3_exec(lnav_data.ld_db.in(),
drop_stmt.c_str(), drop_stmt.c_str(),

View File

@ -71,7 +71,6 @@
#include "sql_util.hh" #include "sql_util.hh"
#include "statusview_curses.hh" #include "statusview_curses.hh"
#include "textfile_sub_source.hh" #include "textfile_sub_source.hh"
#include "top_status_source.hh"
#include "view_helpers.hh" #include "view_helpers.hh"
class spectrogram_source; class spectrogram_source;
@ -184,7 +183,6 @@ struct lnav_data_t {
ln_mode_t ld_last_config_mode{ln_mode_t::FILTER}; ln_mode_t ld_last_config_mode{ln_mode_t::FILTER};
statusview_curses ld_status[LNS__MAX]; statusview_curses ld_status[LNS__MAX];
top_status_source ld_top_source;
bottom_status_source ld_bottom_source; bottom_status_source ld_bottom_source;
filter_status_source ld_filter_status_source; filter_status_source ld_filter_status_source;
filter_help_status_source ld_filter_help_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_initial_build{false};
bool ld_show_help_view{false}; bool ld_show_help_view{false};
lnav::func::scoped_cb ld_status_refresher;
ghc::filesystem::file_time_type ld_last_dot_lnav_time; ghc::filesystem::file_time_type ld_last_dot_lnav_time;
}; };

View File

@ -85,10 +85,7 @@ do_observer_update(const std::shared_ptr<logfile>& lf)
if (isendwin()) { if (isendwin()) {
return; return;
} }
lnav_data.ld_top_source.update_time(); lnav_data.ld_status_refresher();
for (auto& sc : lnav_data.ld_status) {
sc.do_update();
}
if (lf && lnav_data.ld_mode == ln_mode_t::FILES if (lf && lnav_data.ld_mode == ln_mode_t::FILES
&& !lnav_data.ld_initial_build) && !lnav_data.ld_initial_build)
{ {
@ -437,15 +434,7 @@ rescan_files(bool req)
if (!done && !(lnav_data.ld_flags & LNF_HEADLESS)) { if (!done && !(lnav_data.ld_flags & LNF_HEADLESS)) {
lnav_data.ld_files_view.set_needs_update(); lnav_data.ld_files_view.set_needs_update();
lnav_data.ld_files_view.do_update(); lnav_data.ld_files_view.do_update();
lnav_data.ld_top_source.update_time(); lnav_data.ld_status_refresher();
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();
} }
} while (!done && lnav_data.ld_looping); } while (!done && lnav_data.ld_looping);
return true; return true;

View File

@ -96,6 +96,9 @@ static auto scc = injector::bind<sysclip::config>::to_instance(
static auto lsc = injector::bind<logfile_sub_source_ns::config>::to_instance( static auto lsc = injector::bind<logfile_sub_source_ns::config>::to_instance(
+[]() { return &lnav_config.lc_log_source; }); +[]() { return &lnav_config.lc_log_source; });
static auto tssc = injector::bind<top_status_source_cfg>::to_instance(
+[]() { return &lnav_config.lc_top_status_cfg; });
bool bool
check_experimental(const char* feature_name) 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 " .with_description("The format for the clock displayed in "
"the top-left corner using strftime(3) conversions") "the top-left corner using strftime(3) conversions")
.with_example("%a %b %d %H:%M:%S %Z") .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") yajlpp::property_handler("dim-text")
.with_synopsis("bool") .with_synopsis("bool")
.with_description("Reduce the brightness of text (useful for xterms). " .with_description("Reduce the brightness of text (useful for xterms). "

View File

@ -52,6 +52,7 @@
#include "styling.hh" #include "styling.hh"
#include "sysclip.cfg.hh" #include "sysclip.cfg.hh"
#include "tailer/tailer.looper.cfg.hh" #include "tailer/tailer.looper.cfg.hh"
#include "top_status_source.cfg.hh"
/** /**
* Check if an experimental feature should be enabled by * Check if an experimental feature should be enabled by
@ -85,7 +86,7 @@ struct key_map {
}; };
struct _lnav_config { 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_dim_text;
bool lc_ui_default_colors{true}; bool lc_ui_default_colors{true};
std::string lc_ui_keymap; std::string lc_ui_keymap;

View File

@ -52,6 +52,16 @@ public:
virtual void reload_config(error_reporter& reporter) {} 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; static lnav_config_listener* LISTENER_LIST;
lnav_config_listener* lcl_next; lnav_config_listener* lcl_next;

View File

@ -52,8 +52,7 @@ struct compiled_watch_expr {
struct expressions : public lnav_config_listener { struct expressions : public lnav_config_listener {
void reload_config(error_reporter& reporter) override void reload_config(error_reporter& reporter) override
{ {
auto& lnav_db = injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&, auto& lnav_db = injector::get<auto_sqlite3&>();
sqlite_db_tag>();
if (lnav_db.in() == nullptr) { if (lnav_db.in() == nullptr) {
log_warning("db not initialized yet!"); 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<std::string, compiled_watch_expr> e_watch_exprs; std::map<std::string, compiled_watch_expr> e_watch_exprs;
}; };
@ -114,9 +117,7 @@ eval_with(logfile& lf, logfile::iterator ll)
return; return;
} }
static auto& lnav_db static auto& lnav_db = injector::get<auto_sqlite3&>();
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
char timestamp_buffer[64] = ""; char timestamp_buffer[64] = "";
shared_buffer_ref raw_sbr; shared_buffer_ref raw_sbr;

View File

@ -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& vis_bm = this->tss_view->get_bookmarks();
auto& expr_marks_bv = vis_bm[&textview_curses::BM_USER_EXPR]; auto& expr_marks_bv = vis_bm[&textview_curses::BM_USER_EXPR];
expr_marks_bv.clear(); expr_marks_bv.clear();
this->lss_marker_stmt_text = std::move(stmt_str);
this->lss_marker_stmt = stmt;
if (this->lss_index_delegate) { if (this->lss_index_delegate) {
this->lss_index_delegate->index_start(*this); this->lss_index_delegate->index_start(*this);
} }

View File

@ -175,9 +175,7 @@ replace_home_dir(std::string path)
Result<void, lnav::console::user_message> Result<void, lnav::console::user_message>
export_to(FILE* file) export_to(FILE* file)
{ {
static auto& lnav_db static auto& lnav_db = injector::get<auto_sqlite3&>();
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
static const char* BOOKMARK_QUERY = R"( static const char* BOOKMARK_QUERY = R"(
SELECT log_time_msecs, log_format, log_mark, log_comment, log_tags, log_line_hash SELECT log_time_msecs, log_format, log_mark, log_comment, log_tags, log_line_hash

View File

@ -45,9 +45,7 @@ sql_cmd_dump(exec_context& ec,
std::string cmdline, std::string cmdline,
std::vector<std::string>& args) std::vector<std::string>& args)
{ {
static auto& lnav_db static auto& lnav_db = injector::get<auto_sqlite3&>();
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>(); static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
std::string retval; std::string retval;
@ -90,9 +88,7 @@ sql_cmd_read(exec_context& ec,
std::string cmdline, std::string cmdline,
std::vector<std::string>& args) std::vector<std::string>& args)
{ {
static auto& lnav_db static auto& lnav_db = injector::get<auto_sqlite3&>();
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>(); static auto& lnav_flags = injector::get<unsigned long&, lnav_flags_tag>();
std::string retval; std::string retval;

View File

@ -43,6 +43,8 @@
/* XXX figure out how to do this with the template */ /* XXX figure out how to do this with the template */
void sqlite_close_wrapper(void* mem); void sqlite_close_wrapper(void* mem);
using auto_sqlite3 = auto_mem<sqlite3, sqlite_close_wrapper>;
namespace sqlitepp { namespace sqlitepp {
inline auto_mem<char> inline auto_mem<char>

View File

@ -455,9 +455,7 @@ textfile_sub_source::rescan_files(
textfile_sub_source::scan_callback& callback, textfile_sub_source::scan_callback& callback,
nonstd::optional<ui_clock::time_point> deadline) nonstd::optional<ui_clock::time_point> deadline)
{ {
static auto& lnav_db static auto& lnav_db = injector::get<auto_sqlite3&>();
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>();
file_iterator iter; file_iterator iter;
bool retval = false; bool retval = false;

View File

@ -29,22 +29,31 @@
#include "top_status_source.hh" #include "top_status_source.hh"
#include <sqlite3.h>
#include "base/injector.hh" #include "base/injector.hh"
#include "bound_tags.hh"
#include "config.h" #include "config.h"
#include "lnav.hh" #include "lnav.hh"
#include "lnav_config.hh"
#include "logfile_sub_source.hh"
#include "md2attr_line.hh" #include "md2attr_line.hh"
#include "md4cpp.hh" #include "md4cpp.hh"
#include "shlex.hh" #include "shlex.hh"
#include "shlex.resolver.hh" #include "shlex.resolver.hh"
#include "sql_util.hh" #include "sql_util.hh"
#include "sqlitepp.client.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_width(28);
this->tss_fields[TSF_TIME].set_role(role_t::VCR_STATUS_INFO); 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] = ' '; buffer[0] = ' ';
strftime(&buffer[1], strftime(&buffer[1],
sizeof(buffer) - 1, sizeof(buffer) - 1,
lnav_config.lc_ui_clock_format.c_str(), this->tss_config.tssc_clock_format.c_str(),
localtime(&current_time.tv_sec)); localtime(&current_time.tv_sec));
sf.set_value(buffer); sf.set_value(buffer);
} }
@ -76,40 +85,14 @@ top_status_source::update_time()
this->update_time(tv); 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<auto_mem<sqlite3, sqlite_close_wrapper>&,
sqlite_db_tag>()
.in(),
MSG_QUERY)
.unwrap())
{
}
prepared_stmt ums_stmt;
};
void void
top_status_source::update_user_msg() top_status_source::update_user_msg()
{ {
static thread_local user_msg_stmt um_stmt;
auto& al = this->tss_fields[TSF_USER_MSG].get_value(); auto& al = this->tss_fields[TSF_USER_MSG].get_value();
al.clear(); al.clear();
um_stmt.ums_stmt.reset(); this->tss_user_msgs_stmt.reset();
auto fetch_res = um_stmt.ums_stmt.fetch_row<std::string>(); auto fetch_res = this->tss_user_msgs_stmt.fetch_row<std::string>();
fetch_res.match( fetch_res.match(
[&al](const std::string& value) { [&al](const std::string& value) {
shlex lexer(value); shlex lexer(value);

View File

@ -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

View File

@ -32,19 +32,30 @@
#include <string> #include <string>
#include <sqlite3.h>
#include "base/injector.hh"
#include "bound_tags.hh"
#include "listview_curses.hh" #include "listview_curses.hh"
#include "sql_util.hh"
#include "sqlitepp.client.hh"
#include "statusview_curses.hh" #include "statusview_curses.hh"
#include "top_status_source.cfg.hh"
class top_status_source : public status_data_source { class top_status_source : public status_data_source {
public: public:
typedef enum { enum field_t {
TSF_TIME, TSF_TIME,
TSF_USER_MSG, TSF_USER_MSG,
TSF__MAX 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; } size_t statusview_fields() override { return TSF__MAX; }
@ -60,7 +71,9 @@ public:
void update_user_msg(); void update_user_msg();
private: private:
const top_status_source_cfg& tss_config;
status_field tss_fields[TSF__MAX]; status_field tss_fields[TSF__MAX];
prepared_stmt tss_user_msgs_stmt;
}; };
#endif #endif

View File

@ -294,6 +294,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_logfile.sh_09bd16e044302f6b121092534708594bdad11b5a.out \ $(srcdir)/%reldir%/test_logfile.sh_09bd16e044302f6b121092534708594bdad11b5a.out \
$(srcdir)/%reldir%/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.err \ $(srcdir)/%reldir%/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.err \
$(srcdir)/%reldir%/test_logfile.sh_1c6eee38f66356fcd9a9f0faedaea6dbcc901060.out \ $(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.err \
$(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out \ $(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out \
$(srcdir)/%reldir%/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err \ $(srcdir)/%reldir%/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err \

View File

@ -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":""} 

View File

@ -697,5 +697,5 @@ run_cap_test ${lnav_test} -n \
${test_dir}/logfile_ansi.1 ${test_dir}/logfile_ansi.1
run_cap_test ${lnav_test} -n \ 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 logfile_syslog.1.gz

View File

@ -72,11 +72,6 @@ rebuild_indexes_repeatedly()
readline_context::command_map_t lnav_commands; readline_context::command_map_t lnav_commands;
namespace injector { namespace injector {
template<>
void
force_linking(sqlite_db_tag anno)
{
}
template<> template<>
void void

View File

@ -51,7 +51,15 @@ main(int argc, char* argv[])
{ {
int retval = EXIT_SUCCESS; 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); setenv("HOME", "/", 1);
@ -72,7 +80,7 @@ main(int argc, char* argv[])
tss.update_time(); tss.update_time();
assert(val.get_string() != sf.get_value().get_string()); assert(val.get_string() != sf.get_value().get_string());
lnav_config.lc_ui_clock_format = "abc"; cfg.tssc_clock_format = "abc";
tss.update_time(); tss.update_time();
val = sf.get_value(); val = sf.get_value();
assert(val.get_string() == " abc"); assert(val.get_string() == " abc");