mirror of https://github.com/tstack/lnav.git
[status] some more user notification tweaks
This commit is contained in:
parent
f5cc4b298f
commit
45270505e3
|
@ -1,7 +1,7 @@
|
|||
|
||||
ACLOCAL_AMFLAGS = -I .
|
||||
|
||||
SUBDIRS = src test
|
||||
SUBDIRS = tools src test
|
||||
|
||||
noinst_SCRIPTS = TESTS_ENVIRONMENT
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
# aminclude_static.am generated automatically by Autoconf
|
||||
# from AX_AM_MACROS_STATIC on Tue Jul 12 09:53:25 PDT 2022
|
||||
# from AX_AM_MACROS_STATIC on Fri Jul 15 10:37:15 PDT 2022
|
||||
|
||||
|
||||
# Code coverage
|
||||
|
|
|
@ -314,6 +314,7 @@ AC_SUBST(USER_CXXFLAGS)
|
|||
AC_CONFIG_HEADERS([src/config.h])
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([TESTS_ENVIRONMENT])
|
||||
AC_CONFIG_FILES([tools/Makefile])
|
||||
AC_CONFIG_FILES([src/Makefile])
|
||||
AC_CONFIG_FILES([src/base/Makefile])
|
||||
AC_CONFIG_FILES([src/formats/logfmt/Makefile])
|
||||
|
@ -321,7 +322,6 @@ AC_CONFIG_FILES([src/fmtlib/Makefile])
|
|||
AC_CONFIG_FILES([src/pcrepp/Makefile])
|
||||
AC_CONFIG_FILES([src/pugixml/Makefile])
|
||||
AC_CONFIG_FILES([src/tailer/Makefile])
|
||||
AC_CONFIG_FILES([src/tools/Makefile])
|
||||
AC_CONFIG_FILES([src/yajl/Makefile])
|
||||
AC_CONFIG_FILES([src/yajlpp/Makefile])
|
||||
AC_CONFIG_FILES([src/third-party/base64/lib/Makefile])
|
||||
|
|
|
@ -22,7 +22,7 @@ add_subdirectory(formats/logfmt)
|
|||
add_subdirectory(yajl)
|
||||
add_subdirectory(yajlpp)
|
||||
|
||||
add_executable(bin2c bin2c.hh tools/bin2c.c)
|
||||
add_executable(bin2c bin2c.hh ../tools/bin2c.c)
|
||||
target_link_libraries(bin2c ZLIB::ZLIB)
|
||||
|
||||
add_executable(ptimec ptimec.hh ptimec.c)
|
||||
|
|
|
@ -3,7 +3,7 @@ include $(top_srcdir)/aminclude_static.am
|
|||
|
||||
CXXFLAGS =
|
||||
|
||||
SUBDIRS = tools fmtlib third-party/base64/lib pcrepp base tailer pugixml yajl yajlpp formats/logfmt .
|
||||
SUBDIRS = fmtlib third-party/base64/lib pcrepp base tailer pugixml yajl yajlpp formats/logfmt .
|
||||
|
||||
bin_PROGRAMS = lnav
|
||||
|
||||
|
@ -23,10 +23,12 @@ RE2C_V = $(RE2C_V_@AM_V@)
|
|||
RE2C_V_ = $(RE2C_V_@AM_DEFAULT_V@)
|
||||
RE2C_V_0 = @echo " RE2C " $@;
|
||||
|
||||
BIN2C_PATH = ../tools/bin2c$(BUILD_EXEEXT)
|
||||
|
||||
include formats/formats.am
|
||||
|
||||
default-formats.h default-formats.cc: tools/bin2c$(BUILD_EXEEXT) $(FORMAT_FILES)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_format_json default-formats $(FORMAT_FILES)
|
||||
default-formats.cc: $(BIN2C_PATH) $(FORMAT_FILES)
|
||||
$(BIN2C_V)$(BIN2C_PATH) -n lnav_format_json default-formats $(FORMAT_FILES)
|
||||
|
||||
include keymaps/keymaps.am
|
||||
include themes/themes.am
|
||||
|
@ -37,34 +39,34 @@ CONFIG_FILES = \
|
|||
$(THEME_FILES) \
|
||||
$()
|
||||
|
||||
default-config.h default-config.cc: tools/bin2c$(BUILD_EXEEXT) $(CONFIG_FILES)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_config_json default-config $(CONFIG_FILES)
|
||||
default-config.cc: $(BIN2C_PATH) $(CONFIG_FILES)
|
||||
$(BIN2C_V)$(BIN2C_PATH) -n lnav_config_json default-config $(CONFIG_FILES)
|
||||
|
||||
include scripts/scripts.am
|
||||
|
||||
builtin-scripts.h builtin-scripts.cc: tools/bin2c$(BUILD_EXEEXT) $(BUILTIN_LNAVSCRIPTS)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS)
|
||||
builtin-scripts.cc: $(BIN2C_PATH) $(BUILTIN_LNAVSCRIPTS)
|
||||
$(BIN2C_V)$(BIN2C_PATH) -n lnav_scripts builtin-scripts $(BUILTIN_LNAVSCRIPTS)
|
||||
|
||||
builtin-sh-scripts.h builtin-sh-scripts.cc: tools/bin2c$(BUILD_EXEEXT) $(BUILTIN_SHSCRIPTS)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS)
|
||||
builtin-sh-scripts.cc: $(BIN2C_PATH) $(BUILTIN_SHSCRIPTS)
|
||||
$(BIN2C_V)$(BIN2C_PATH) -n lnav_sh_scripts builtin-sh-scripts $(BUILTIN_SHSCRIPTS)
|
||||
|
||||
%-sh.cc: $(srcdir)/%.sh tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-sh $<
|
||||
%-sh.cc: $(srcdir)/%.sh $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-sh $<
|
||||
|
||||
%-txt.cc %-txt.h: $(srcdir)/%.txt tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-txt $<
|
||||
%-txt.cc: $(srcdir)/%.txt $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-txt $<
|
||||
|
||||
%-md.cc %-md.h: $(srcdir)/%.md tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-md $<
|
||||
%-md.cc: $(srcdir)/%.md $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-md $<
|
||||
|
||||
%-sql.cc %-sql.h: $(srcdir)/%.sql tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-sql $<
|
||||
%-sql.cc: $(srcdir)/%.sql $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-sql $<
|
||||
|
||||
%-lnav.cc %-lnav.h: $(srcdir)/%.lnav tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-lnav $<
|
||||
%-lnav.cc: $(srcdir)/%.lnav $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-lnav $<
|
||||
|
||||
%-json.cc %-json.h: $(srcdir)/%.json tools/bin2c$(BUILD_EXEEXT)
|
||||
$(BIN2C_V)tools/bin2c$(BUILD_EXEEXT) $(*)-json $<
|
||||
%-json.cc: $(srcdir)/%.json $(BIN2C_PATH)
|
||||
$(BIN2C_V)$(BIN2C_PATH) $(*)-json $<
|
||||
|
||||
include time_formats.am
|
||||
|
||||
|
@ -77,44 +79,21 @@ if HAVE_RE2C
|
|||
$(REC2_V)test $@ -ef $(srcdir)/$*.cc || cp $@ $(srcdir)/$*.cc
|
||||
endif
|
||||
|
||||
lnav_config.$(OBJEXT): default-config.h
|
||||
|
||||
log_format_loader.$(OBJEXT): \
|
||||
builtin-scripts.h \
|
||||
builtin-sh-scripts.h \
|
||||
default-formats.h
|
||||
|
||||
styling.$(OBJEXT): ansi-palette-json.h xterm-palette-json.h
|
||||
|
||||
view_helpers.$(OBJEXT): help-txt.h help-md.h
|
||||
|
||||
md4cpp.$(OBJEXT): xml-entities-json.h emojis-json.h
|
||||
|
||||
LNAV_BUILT_FILES = \
|
||||
ansi-palette-json.h \
|
||||
ansi-palette-json.cc \
|
||||
builtin-scripts.h \
|
||||
builtin-scripts.cc \
|
||||
builtin-sh-scripts.h \
|
||||
builtin-sh-scripts.cc \
|
||||
default-config.h \
|
||||
default-config.cc \
|
||||
default-formats.h \
|
||||
default-formats.cc \
|
||||
emojis-json.h \
|
||||
emojis-json.cc \
|
||||
help-txt.h \
|
||||
help-txt.cc \
|
||||
help-md.h \
|
||||
help-md.cc \
|
||||
init-sql.h \
|
||||
init-sql.cc \
|
||||
time_fmts.cc \
|
||||
xml-entities-json.h \
|
||||
xml-entities-json.cc \
|
||||
xterm-palette-json.h \
|
||||
xterm-palette-json.cc
|
||||
|
||||
BUILT_SOURCES = $(LNAV_BUILT_FILES)
|
||||
|
||||
AM_LIBS = $(CODE_COVERAGE_LIBS)
|
||||
AM_CFLAGS = $(CODE_COVERAGE_CFLAGS)
|
||||
AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS) $(USER_CXXFLAGS)
|
||||
|
@ -287,6 +266,7 @@ noinst_HEADERS = \
|
|||
spectro_impls.hh \
|
||||
spectro_source.hh \
|
||||
sqlitepp.hh \
|
||||
sqlitepp.client.hh \
|
||||
sql_help.hh \
|
||||
sql_util.hh \
|
||||
sqlite-extension-func.hh \
|
||||
|
@ -474,8 +454,6 @@ libdiag_a_SOURCES = \
|
|||
PLUGIN_SRCS = \
|
||||
file_vtab.cc
|
||||
|
||||
lnav.$(OBJEXT): help-txt.h init-sql.h
|
||||
|
||||
lnav_SOURCES = \
|
||||
lnav.cc \
|
||||
lnav.events.cc \
|
||||
|
@ -516,11 +494,13 @@ uncrusty:
|
|||
$(HEADERS))
|
||||
|
||||
if !DISABLE_DOCUMENTATION
|
||||
all-local: lnav
|
||||
all-local: $(LNAV_BUILT_FILES) lnav
|
||||
if test -w $(srcdir)/internals; then \
|
||||
env DUMP_INTERNALS_DIR=$(srcdir)/internals DUMP_CRASH=1 ./lnav Makefile; \
|
||||
mv $(srcdir)/internals/*.schema.json $(top_srcdir)/docs/schemas; \
|
||||
fi
|
||||
else
|
||||
all-local: $(LNAV_BUILT_FILES)
|
||||
endif
|
||||
|
||||
install-exec-hook:
|
||||
|
|
23
src/init.sql
23
src/init.sql
|
@ -109,13 +109,30 @@ VALUES (0, null, '2017-02-03T04:05:06.100', '2017-02-03T04:05:06.100', 0,
|
|||
|
||||
CREATE TABLE lnav_user_notifications
|
||||
(
|
||||
id TEXT NOT NULL DEFAULT 'org.lnav.unknown',
|
||||
-- A unique identifier for the notification.
|
||||
id TEXT NOT NULL DEFAULT 'org.lnav.user' PRIMARY KEY,
|
||||
-- The priority of this message relative to others, the highest priority
|
||||
-- message will be shown in the top-right corner.
|
||||
priority INTEGER NOT NULL DEFAULT 0,
|
||||
-- The time when this notification was created.
|
||||
created DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
-- The time when this notification is no longer applicable.
|
||||
expiration DATETIME DEFAULT NULL,
|
||||
message TEXT
|
||||
-- A JSON array with the names of the views where this notification is
|
||||
-- applicable. Use NULL to show it in all views.
|
||||
views JSON,
|
||||
-- The message to display, can be null to clear the message.
|
||||
message TEXT,
|
||||
|
||||
CHECK (views IS NULL OR json_type(views) = 'array')
|
||||
);
|
||||
|
||||
INSERT INTO lnav_user_notifications (id, priority, expiration, message)
|
||||
VALUES ('org.lnav.breadcrumb.help.focus', -1, datetime('now', '+1 minute'),
|
||||
VALUES ('org.lnav.breadcrumb.focus', -1, datetime('now', '+1 minute'),
|
||||
'Press ENTER to focus on the breadcrumb bar');
|
||||
|
||||
CREATE TABLE lnav_views_echo AS
|
||||
SELECT name, top, "left", height, inner_height, top_time, search
|
||||
FROM lnav_views;
|
||||
|
||||
CREATE UNIQUE INDEX lnav_views_echo_index ON lnav_views_echo (name);
|
||||
|
|
|
@ -67,7 +67,7 @@ null_or_default(sqlite3_context* context, int argc, sqlite3_value* argv[])
|
|||
}
|
||||
|
||||
struct contains_userdata {
|
||||
util::variant<const char*, sqlite3_int64, bool> cu_match_value{false};
|
||||
util::variant<string_fragment, sqlite3_int64, bool> cu_match_value{false};
|
||||
size_t cu_depth{0};
|
||||
bool cu_result{false};
|
||||
};
|
||||
|
@ -75,12 +75,10 @@ struct contains_userdata {
|
|||
static int
|
||||
contains_string(void* ctx, const unsigned char* str, size_t len)
|
||||
{
|
||||
auto sf = string_fragment{(const char*) str, 0, (int) len};
|
||||
auto& cu = *((contains_userdata*) ctx);
|
||||
|
||||
if (cu.cu_depth <= 1
|
||||
&& strncmp((const char*) str, cu.cu_match_value.get<const char*>(), len)
|
||||
== 0)
|
||||
{
|
||||
if (cu.cu_depth <= 1 && cu.cu_match_value.get<string_fragment>() == sf) {
|
||||
cu.cu_result = true;
|
||||
}
|
||||
|
||||
|
@ -158,7 +156,11 @@ json_contains(vtab_types::nullable<const char> nullable_json_in,
|
|||
switch (sqlite3_value_type(value)) {
|
||||
case SQLITE3_TEXT:
|
||||
cb.yajl_string = contains_string;
|
||||
cu.cu_match_value = (const char*) sqlite3_value_text(value);
|
||||
cu.cu_match_value = string_fragment{
|
||||
(const char*) sqlite3_value_text(value),
|
||||
0,
|
||||
sqlite3_value_bytes(value),
|
||||
};
|
||||
break;
|
||||
case SQLITE_INTEGER:
|
||||
cb.yajl_integer = contains_integer;
|
||||
|
|
16
src/lnav.cc
16
src/lnav.cc
|
@ -96,7 +96,6 @@
|
|||
#include "filter_sub_source.hh"
|
||||
#include "fstat_vtab.hh"
|
||||
#include "grep_proc.hh"
|
||||
#include "help-txt.h"
|
||||
#include "hist_source.hh"
|
||||
#include "init-sql.h"
|
||||
#include "listview_curses.hh"
|
||||
|
@ -124,6 +123,7 @@
|
|||
#include "sql_help.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
#include "sqlitepp.client.hh"
|
||||
#include "tailer/tailer.looper.hh"
|
||||
#include "term_extra.hh"
|
||||
#include "termios_guard.hh"
|
||||
|
@ -1329,6 +1329,19 @@ looper()
|
|||
auto next_status_update_time = next_rebuild_time;
|
||||
auto next_rescan_time = next_rebuild_time;
|
||||
|
||||
auto echo_views_stmt = prepare_stmt(lnav_data.ld_db, R"(
|
||||
UPDATE lnav_views_echo
|
||||
SET top = orig.top,
|
||||
left = orig.left,
|
||||
height = orig.height,
|
||||
inner_height = orig.inner_height,
|
||||
top_time = orig.top_time,
|
||||
search = orig.search
|
||||
FROM (SELECT * FROM lnav_views) AS orig
|
||||
WHERE orig.name = lnav_views_echo.name
|
||||
)")
|
||||
.unwrap();
|
||||
|
||||
while (lnav_data.ld_looping) {
|
||||
auto loop_deadline
|
||||
= ui_clock::now() + (session_stage == 0 ? 3s : 50ms);
|
||||
|
@ -1447,6 +1460,7 @@ looper()
|
|||
lnav_data.ld_spectro_details_view.do_update();
|
||||
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();
|
||||
for (auto& sc : lnav_data.ld_status) {
|
||||
sc.do_update();
|
||||
|
|
|
@ -2214,8 +2214,8 @@ com_create_search_table(exec_context& ec,
|
|||
}
|
||||
|
||||
auto re = re_res.unwrap();
|
||||
auto lst = std::make_shared<log_search_table>(
|
||||
re, intern_string::lookup(args[1]));
|
||||
auto tab_name = intern_string::lookup(args[1]);
|
||||
auto lst = std::make_shared<log_search_table>(re, tab_name);
|
||||
if (ec.ec_dry_run) {
|
||||
textview_curses* tc = &lnav_data.ld_views[LNV_LOG];
|
||||
auto& hm = tc->get_highlights();
|
||||
|
@ -2238,6 +2238,11 @@ com_create_search_table(exec_context& ec,
|
|||
return Ok(std::string());
|
||||
}
|
||||
|
||||
auto tab_iter = custom_search_tables.find(args[1]);
|
||||
if (tab_iter != custom_search_tables.end()) {
|
||||
lnav_data.ld_vtab_manager->unregister_vtab(tab_name);
|
||||
}
|
||||
|
||||
std::string errmsg;
|
||||
|
||||
errmsg = lnav_data.ld_vtab_manager->register_vtab(lst);
|
||||
|
@ -2268,7 +2273,8 @@ com_delete_search_table(exec_context& ec,
|
|||
if (args.empty()) {
|
||||
args.emplace_back("search-table");
|
||||
} else if (args.size() == 2) {
|
||||
if (custom_search_tables.find(args[1]) == custom_search_tables.end()) {
|
||||
auto tab_iter = custom_search_tables.find(args[1]);
|
||||
if (tab_iter == custom_search_tables.end()) {
|
||||
return ec.make_error("unknown search table -- {}", args[1]);
|
||||
}
|
||||
|
||||
|
@ -2276,7 +2282,8 @@ com_delete_search_table(exec_context& ec,
|
|||
return Ok(std::string());
|
||||
}
|
||||
|
||||
std::string rc = lnav_data.ld_vtab_manager->unregister_vtab(
|
||||
custom_search_tables.erase(tab_iter);
|
||||
auto rc = lnav_data.ld_vtab_manager->unregister_vtab(
|
||||
intern_string::lookup(args[1]));
|
||||
|
||||
if (rc.empty()) {
|
||||
|
|
|
@ -127,21 +127,20 @@ log_search_table::next(log_cursor& lc, logfile_sub_source& lss)
|
|||
return true;
|
||||
}
|
||||
|
||||
lc.lc_curr_line += 1_vl;
|
||||
lc.lc_sub_index = 0;
|
||||
this->lst_match_index = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
this->lst_match_index = -1;
|
||||
|
||||
while (!lc.is_eof() && !this->is_valid(lc, lss)) {
|
||||
lc.lc_curr_line += 1_vl;
|
||||
lc.lc_sub_index = 0;
|
||||
}
|
||||
|
||||
if (lc.is_eof()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this->is_valid(lc, lss)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cl = lss.at(lc.lc_curr_line);
|
||||
auto* lf = lss.find_file_ptr(cl);
|
||||
|
||||
|
@ -177,7 +176,9 @@ log_search_table::extract(logfile* lf,
|
|||
shared_buffer_ref& line,
|
||||
std::vector<logline_value>& values)
|
||||
{
|
||||
values = this->lst_line_values_cache;
|
||||
if (this->lst_format != nullptr) {
|
||||
values = this->lst_line_values_cache;
|
||||
}
|
||||
values.emplace_back(this->lst_column_metas[this->lst_format_column_count],
|
||||
this->lst_match_index);
|
||||
for (int lpc = 0; lpc < this->lst_regex.get_capture_count(); lpc++) {
|
||||
|
|
|
@ -2023,7 +2023,7 @@ log_vtab_manager::unregister_vtab(intern_string_t name)
|
|||
std::string retval;
|
||||
|
||||
if (this->vm_impls.find(name) == this->vm_impls.end()) {
|
||||
retval = fmt::format(FMT_STRING("unknown log line table -- {}"), name);
|
||||
retval = fmt::format(FMT_STRING("unknown table -- {}"), name);
|
||||
} else {
|
||||
auto_mem<char, sqlite3_free> sql;
|
||||
__attribute((unused)) int rc;
|
||||
|
|
|
@ -520,7 +520,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
|
|||
void
|
||||
rl_search(readline_curses* rc)
|
||||
{
|
||||
textview_curses* tc = get_textview_for_mode(lnav_data.ld_mode);
|
||||
auto* tc = get_textview_for_mode(lnav_data.ld_mode);
|
||||
|
||||
rl_search_internal(rc, lnav_data.ld_mode);
|
||||
tc->set_follow_search_for(0, {});
|
||||
|
@ -878,6 +878,8 @@ rl_focus(readline_curses* rc)
|
|||
.get_overlay_source();
|
||||
|
||||
fos->fos_contexts.emplace("", false, true);
|
||||
|
||||
get_textview_for_mode(lnav_data.ld_mode)->save_current_search();
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -67,6 +67,7 @@
|
|||
#include "spookyhash/SpookyV2.h"
|
||||
|
||||
static int got_line = 0;
|
||||
static int got_abort = 0;
|
||||
static bool alt_done = 0;
|
||||
static sig_atomic_t got_timeout = 0;
|
||||
static sig_atomic_t got_winch = 0;
|
||||
|
@ -499,6 +500,8 @@ rubout_char_or_abort(int count, int key)
|
|||
{
|
||||
if (rl_line_buffer[0] == '\0') {
|
||||
rl_done = true;
|
||||
got_abort = 1;
|
||||
got_line = 0;
|
||||
return 0;
|
||||
} else {
|
||||
return rl_rubout(count, '\b');
|
||||
|
@ -885,6 +888,7 @@ readline_curses::start()
|
|||
= this->rc_contexts.find(context))
|
||||
!= this->rc_contexts.end())
|
||||
{
|
||||
got_abort = 0;
|
||||
current_context->second->load();
|
||||
rl_callback_handler_install(&msg[prompt_start],
|
||||
line_ready_tramp);
|
||||
|
@ -1027,7 +1031,7 @@ readline_curses::line_ready(const char* line)
|
|||
const char* cmd_ch = alt_done ? "D" : "d";
|
||||
|
||||
alt_done = false;
|
||||
if (line == nullptr) {
|
||||
if (got_abort || line == nullptr) {
|
||||
snprintf(msg, sizeof(msg), "a");
|
||||
|
||||
if (sendstring(this->rc_command_pipe[RCF_SLAVE], msg, strlen(msg))
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "logfile.hh"
|
||||
#include "service_tags.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "sqlitepp.client.hh"
|
||||
#include "tailer/tailer.looper.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
|
@ -131,143 +132,6 @@ static const size_t MAX_SESSION_FILE_COUNT = 256;
|
|||
static std::vector<content_line_t> marked_session_lines;
|
||||
static std::vector<content_line_t> offset_session_lines;
|
||||
|
||||
int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const struct timeval& tv)
|
||||
{
|
||||
char timestamp[64];
|
||||
|
||||
sql_strftime(timestamp, sizeof(timestamp), tv, 'T');
|
||||
|
||||
return sqlite3_bind_text(stmt, index, timestamp, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const char* str)
|
||||
{
|
||||
return sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, intern_string_t ist)
|
||||
{
|
||||
return sqlite3_bind_text(
|
||||
stmt, index, ist.get(), ist.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const std::string& str)
|
||||
{
|
||||
return sqlite3_bind_text(
|
||||
stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, int64_t i)
|
||||
{
|
||||
return sqlite3_bind_int64(stmt, index, i);
|
||||
}
|
||||
|
||||
template<typename... Args, std::size_t... Idx>
|
||||
int
|
||||
bind_values_helper(sqlite3_stmt* stmt,
|
||||
std::index_sequence<Idx...> idxs,
|
||||
Args... args)
|
||||
{
|
||||
int rcs[] = {bind_to_sqlite(stmt, Idx + 1, args)...};
|
||||
|
||||
for (size_t lpc = 0; lpc < idxs.size(); lpc++) {
|
||||
if (rcs[lpc] != SQLITE_OK) {
|
||||
log_error("Failed to bind column %d in statement: %s",
|
||||
lpc,
|
||||
sqlite3_sql(stmt));
|
||||
return rcs[lpc];
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
int
|
||||
bind_values(sqlite3_stmt* stmt, Args... args)
|
||||
{
|
||||
return bind_values_helper(
|
||||
stmt, std::make_index_sequence<sizeof...(Args)>(), args...);
|
||||
}
|
||||
|
||||
struct prepared_stmt {
|
||||
prepared_stmt(auto_mem<sqlite3_stmt> stmt) : ps_stmt(std::move(stmt)) {}
|
||||
|
||||
Result<void, std::string> execute()
|
||||
{
|
||||
auto rc = sqlite3_step(this->ps_stmt.in());
|
||||
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
auto msg = std::string(
|
||||
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())));
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
struct end_of_rows {};
|
||||
struct fetch_error {
|
||||
std::string fe_msg;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using fetch_result = mapbox::util::variant<T, end_of_rows, fetch_error>;
|
||||
|
||||
template<typename T>
|
||||
fetch_result<T> fetch_row()
|
||||
{
|
||||
auto rc = sqlite3_step(this->ps_stmt.in());
|
||||
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
|
||||
return end_of_rows{};
|
||||
}
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
const auto argc = sqlite3_column_count(this->ps_stmt.in());
|
||||
sqlite3_value* argv[argc];
|
||||
|
||||
for (int lpc = 0; lpc < argc; lpc++) {
|
||||
argv[lpc] = sqlite3_column_value(this->ps_stmt.in(), lpc);
|
||||
}
|
||||
|
||||
return from_sqlite<T>()(argc, argv, 0);
|
||||
}
|
||||
|
||||
return fetch_error{
|
||||
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())),
|
||||
};
|
||||
}
|
||||
|
||||
auto_mem<sqlite3_stmt> ps_stmt;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
static Result<prepared_stmt, std::string>
|
||||
prepare_stmt(sqlite3* db, const char* sql, Args... args)
|
||||
{
|
||||
auto_mem<sqlite3_stmt> retval(sqlite3_finalize);
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, retval.out(), nullptr) != SQLITE_OK) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
|
||||
sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
if (bind_values(retval.in(), args...) != SQLITE_OK) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
|
||||
sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
return Ok(prepared_stmt{
|
||||
std::move(retval),
|
||||
});
|
||||
}
|
||||
|
||||
static bool
|
||||
bind_line(sqlite3* db,
|
||||
sqlite3_stmt* stmt,
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/**
|
||||
* 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_sqlitepp_client_hh
|
||||
#define lnav_sqlitepp_client_hh
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include "base/auto_mem.hh"
|
||||
#include "base/intern_string.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "vtab_module.hh"
|
||||
|
||||
inline int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const struct timeval& tv)
|
||||
{
|
||||
char timestamp[64];
|
||||
|
||||
sql_strftime(timestamp, sizeof(timestamp), tv, 'T');
|
||||
|
||||
return sqlite3_bind_text(stmt, index, timestamp, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
inline int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const char* str)
|
||||
{
|
||||
return sqlite3_bind_text(stmt, index, str, -1, SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
inline int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, intern_string_t ist)
|
||||
{
|
||||
return sqlite3_bind_text(
|
||||
stmt, index, ist.get(), ist.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
inline int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, const std::string& str)
|
||||
{
|
||||
return sqlite3_bind_text(
|
||||
stmt, index, str.c_str(), str.size(), SQLITE_TRANSIENT);
|
||||
}
|
||||
|
||||
inline int
|
||||
bind_to_sqlite(sqlite3_stmt* stmt, int index, int64_t i)
|
||||
{
|
||||
return sqlite3_bind_int64(stmt, index, i);
|
||||
}
|
||||
|
||||
template<typename... Args, std::size_t... Idx>
|
||||
int
|
||||
bind_values_helper(sqlite3_stmt* stmt,
|
||||
std::index_sequence<Idx...> idxs,
|
||||
Args... args)
|
||||
{
|
||||
int rcs[] = {bind_to_sqlite(stmt, Idx + 1, args)...};
|
||||
|
||||
for (size_t lpc = 0; lpc < idxs.size(); lpc++) {
|
||||
if (rcs[lpc] != SQLITE_OK) {
|
||||
log_error("Failed to bind column %d in statement: %s",
|
||||
lpc,
|
||||
sqlite3_sql(stmt));
|
||||
return rcs[lpc];
|
||||
}
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
int
|
||||
bind_values(sqlite3_stmt* stmt, Args... args)
|
||||
{
|
||||
return bind_values_helper(
|
||||
stmt, std::make_index_sequence<sizeof...(Args)>(), args...);
|
||||
}
|
||||
|
||||
struct prepared_stmt {
|
||||
prepared_stmt(auto_mem<sqlite3_stmt> stmt) : ps_stmt(std::move(stmt)) {}
|
||||
|
||||
Result<void, std::string> execute()
|
||||
{
|
||||
auto rc = sqlite3_reset(this->ps_stmt.in());
|
||||
if (rc != SQLITE_OK) {
|
||||
return Err(std::string(
|
||||
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in()))));
|
||||
}
|
||||
|
||||
rc = sqlite3_step(this->ps_stmt.in());
|
||||
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
|
||||
return Ok();
|
||||
}
|
||||
|
||||
auto msg = std::string(
|
||||
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())));
|
||||
return Err(msg);
|
||||
}
|
||||
|
||||
struct end_of_rows {};
|
||||
struct fetch_error {
|
||||
std::string fe_msg;
|
||||
};
|
||||
|
||||
void reset() { sqlite3_reset(this->ps_stmt.in()); }
|
||||
|
||||
template<typename T>
|
||||
using fetch_result = mapbox::util::variant<T, end_of_rows, fetch_error>;
|
||||
|
||||
template<typename T>
|
||||
fetch_result<T> fetch_row()
|
||||
{
|
||||
auto rc = sqlite3_step(this->ps_stmt.in());
|
||||
if (rc == SQLITE_OK || rc == SQLITE_DONE) {
|
||||
return end_of_rows{};
|
||||
}
|
||||
|
||||
if (rc == SQLITE_ROW) {
|
||||
const auto argc = sqlite3_column_count(this->ps_stmt.in());
|
||||
sqlite3_value* argv[argc];
|
||||
|
||||
for (int lpc = 0; lpc < argc; lpc++) {
|
||||
argv[lpc] = sqlite3_column_value(this->ps_stmt.in(), lpc);
|
||||
}
|
||||
|
||||
return from_sqlite<T>()(argc, argv, 0);
|
||||
}
|
||||
|
||||
return fetch_error{
|
||||
sqlite3_errmsg(sqlite3_db_handle(this->ps_stmt.in())),
|
||||
};
|
||||
}
|
||||
|
||||
auto_mem<sqlite3_stmt> ps_stmt;
|
||||
};
|
||||
|
||||
template<typename... Args>
|
||||
static Result<prepared_stmt, std::string>
|
||||
prepare_stmt(sqlite3* db, const char* sql, Args... args)
|
||||
{
|
||||
auto_mem<sqlite3_stmt> retval(sqlite3_finalize);
|
||||
|
||||
if (sqlite3_prepare_v2(db, sql, -1, retval.out(), nullptr) != SQLITE_OK) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
|
||||
sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
if (bind_values(retval.in(), args...) != SQLITE_OK) {
|
||||
return Err(
|
||||
fmt::format(FMT_STRING("unable to prepare SQL statement: {}"),
|
||||
sqlite3_errmsg(db)));
|
||||
}
|
||||
|
||||
return Ok(prepared_stmt{
|
||||
std::move(retval),
|
||||
});
|
||||
}
|
||||
|
||||
#endif
|
|
@ -46,7 +46,7 @@ libtailerpp_a_CPPFLAGS = \
|
|||
libtailerpp_a_SOURCES = \
|
||||
tailerpp.cc
|
||||
|
||||
tailerbin.h tailerbin.cc: tailer tailer.ape ../tools/bin2c$(BUILD_EXEEXT)
|
||||
tailerbin.cc: tailer tailer.ape ../tools/bin2c$(BUILD_EXEEXT)
|
||||
../tools/bin2c$(BUILD_EXEEXT) -n tailer_bin tailerbin $(srcdir)/tailer.ape
|
||||
|
||||
libtailerservice_a_CPPFLAGS = \
|
||||
|
|
|
@ -570,7 +570,6 @@ textview_curses::execute_search(const std::string& regex_orig)
|
|||
const char* errptr;
|
||||
int eoff;
|
||||
|
||||
this->tc_previous_search = this->tc_current_search;
|
||||
this->match_reset();
|
||||
|
||||
this->tc_search_child.reset();
|
||||
|
|
|
@ -676,15 +676,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
std::string get_current_search() const
|
||||
std::string get_current_search() const { return this->tc_current_search; }
|
||||
|
||||
void save_current_search()
|
||||
{
|
||||
return this->tc_current_search;
|
||||
this->tc_previous_search = this->tc_current_search;
|
||||
}
|
||||
|
||||
void revert_search()
|
||||
{
|
||||
this->execute_search(this->tc_previous_search);
|
||||
}
|
||||
void revert_search() { this->execute_search(this->tc_previous_search); }
|
||||
|
||||
void invoke_scroll()
|
||||
{
|
||||
|
|
|
@ -4,21 +4,9 @@
|
|||
#include <stddef.h> /* size_t */
|
||||
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define BASE64_SYMBOL_IMPORT __declspec(dllimport)
|
||||
#define BASE64_SYMBOL_EXPORT __declspec(dllexport)
|
||||
#define BASE64_SYMBOL_PRIVATE
|
||||
|
||||
#elif __GNUC__ >= 4
|
||||
#define BASE64_SYMBOL_IMPORT __attribute__ ((visibility ("default")))
|
||||
#define BASE64_SYMBOL_EXPORT __attribute__ ((visibility ("default")))
|
||||
#define BASE64_SYMBOL_PRIVATE __attribute__ ((visibility ("hidden")))
|
||||
|
||||
#else
|
||||
#define BASE64_SYMBOL_IMPORT
|
||||
#define BASE64_SYMBOL_EXPORT
|
||||
#define BASE64_SYMBOL_PRIVATE
|
||||
#endif
|
||||
|
||||
#if defined(BASE64_STATIC_DEFINE)
|
||||
#define BASE64_EXPORT
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "lnav_config.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "sqlitepp.client.hh"
|
||||
|
||||
top_status_source::top_status_source()
|
||||
{
|
||||
|
@ -70,76 +71,48 @@ top_status_source::update_time()
|
|||
this->update_time(tv);
|
||||
}
|
||||
|
||||
struct user_msg_stmt {
|
||||
user_msg_stmt()
|
||||
{
|
||||
static const char* MSG_QUERY = R"(
|
||||
static const char* MSG_QUERY = R"(
|
||||
SELECT message FROM lnav_user_notifications
|
||||
WHERE expiration IS NULL OR expiration > datetime('now')
|
||||
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
|
||||
)";
|
||||
|
||||
auto& lnav_db = injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
|
||||
sqlite_db_tag>();
|
||||
|
||||
auto retcode = sqlite3_prepare_v2(
|
||||
lnav_db, MSG_QUERY, -1, this->ums_stmt.out(), nullptr);
|
||||
|
||||
ensure(retcode == SQLITE_OK);
|
||||
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())
|
||||
{
|
||||
}
|
||||
|
||||
auto_mem<sqlite3_stmt> ums_stmt{sqlite3_finalize};
|
||||
prepared_stmt ums_stmt;
|
||||
};
|
||||
|
||||
void
|
||||
top_status_source::update_user_msg()
|
||||
{
|
||||
static user_msg_stmt um_stmt;
|
||||
static auto& lnav_db
|
||||
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
|
||||
sqlite_db_tag>();
|
||||
|
||||
auto& al = this->tss_fields[TSF_USER_MSG].get_value();
|
||||
al.clear();
|
||||
|
||||
auto* stmt = um_stmt.ums_stmt.in();
|
||||
sqlite3_reset(stmt);
|
||||
|
||||
auto count = sqlite3_bind_parameter_count(stmt);
|
||||
for (int lpc = 0; lpc < count; lpc++) {
|
||||
const auto* name = sqlite3_bind_parameter_name(stmt, lpc + 1);
|
||||
|
||||
if (name[0] == '$') {
|
||||
const char* env_value;
|
||||
|
||||
if ((env_value = getenv(&name[1])) != nullptr) {
|
||||
sqlite3_bind_text(stmt, lpc + 1, env_value, -1, SQLITE_STATIC);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto step_res = sqlite3_step(stmt);
|
||||
|
||||
switch (step_res) {
|
||||
case SQLITE_OK:
|
||||
case SQLITE_DONE:
|
||||
break;
|
||||
case SQLITE_ROW: {
|
||||
int ncols = sqlite3_column_count(stmt);
|
||||
for (int lpc = 0; lpc < ncols; lpc++) {
|
||||
const auto* text = (const char*) sqlite3_column_text(stmt, lpc);
|
||||
|
||||
al.with_ansi_string(text);
|
||||
al.append(" ");
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
um_stmt.ums_stmt.reset();
|
||||
auto fetch_res = um_stmt.ums_stmt.fetch_row<std::string>();
|
||||
fetch_res.match(
|
||||
[&al](const std::string& value) {
|
||||
al.with_ansi_string(value);
|
||||
al.append(" ");
|
||||
},
|
||||
[](const prepared_stmt::end_of_rows&) {},
|
||||
[](const prepared_stmt::fetch_error& fe) {
|
||||
log_error("failed to execute user-message expression: %s",
|
||||
sqlite3_errmsg(lnav_db));
|
||||
break;
|
||||
}
|
||||
}
|
||||
fe.fe_msg.c_str());
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "document.sections.hh"
|
||||
#include "environ_vtab.hh"
|
||||
#include "help-md.h"
|
||||
#include "help-txt.h"
|
||||
#include "intervaltree/IntervalTree.h"
|
||||
#include "lnav.hh"
|
||||
#include "lnav.indexing.hh"
|
||||
|
|
|
@ -243,7 +243,7 @@ CREATE TABLE lnav_views (
|
|||
sql_strftime(timestamp,
|
||||
sizeof(timestamp),
|
||||
top_time_opt.value(),
|
||||
'T');
|
||||
' ');
|
||||
sqlite3_result_text(
|
||||
ctx, timestamp, -1, SQLITE_TRANSIENT);
|
||||
} else {
|
||||
|
@ -304,7 +304,7 @@ CREATE TABLE lnav_views (
|
|||
sql_strftime(timestamp,
|
||||
sizeof(timestamp),
|
||||
top_time_opt.value(),
|
||||
'T');
|
||||
' ');
|
||||
tlm.tlm_time = timestamp;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,6 @@ TEXT2C_OBJS = \
|
|||
../src/builtin-scripts.$(OBJEXT) \
|
||||
../src/builtin-sh-scripts.$(OBJEXT) \
|
||||
../src/default-formats.$(OBJEXT) \
|
||||
../src/help-txt.$(OBJEXT) \
|
||||
../src/time_fmts.$(OBJEXT)
|
||||
|
||||
LDADD = \
|
||||
|
|
|
@ -662,6 +662,8 @@ EXPECTED_FILES = \
|
|||
$(srcdir)/%reldir%/test_sql_json_func.sh_7c01aaf09078aaa3f23d127f9e03a317dca066de.out \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.err \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_80c97b22084a06fd765ad22c935616c578968d07.out \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.err \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_83d8615c9ce5dfab5e4373570c1b68b8608155f5.out \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.err \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_8cae9740ddfd6ba4c865fca0117b7bea3bb556e5.out \
|
||||
$(srcdir)/%reldir%/test_sql_json_func.sh_8e229f1b5fa3d3803e9db2f295a8d1a490e1b3db.err \
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
Row 0:
|
||||
Column json_contains('"hi"', 'hi there'): 0
|
|
@ -24,6 +24,10 @@ run_cap_test env TEST_COMMENT='contains1' ./drive_sql <<EOF
|
|||
select json_contains('"hi"', 'hi')
|
||||
EOF
|
||||
|
||||
run_cap_test env TEST_COMMENT='contains1.5' ./drive_sql <<EOF
|
||||
select json_contains('"hi"', 'hi there')
|
||||
EOF
|
||||
|
||||
run_cap_test env TEST_COMMENT='contains2' ./drive_sql <<EOF
|
||||
select json_contains('["hi", "bye"]', 'hola') as res
|
||||
EOF
|
||||
|
|
|
@ -136,16 +136,17 @@ main(int argc, char** argv)
|
|||
}
|
||||
|
||||
const char* out_base_name = argv[0];
|
||||
char hname[PATH_MAX], cname[PATH_MAX];
|
||||
char hname[PATH_MAX], hname_tmp[PATH_MAX], cname[PATH_MAX];
|
||||
|
||||
argc -= 1;
|
||||
argv += 1;
|
||||
|
||||
snprintf(hname, sizeof(hname), "%s.h", out_base_name);
|
||||
snprintf(hname_tmp, sizeof(hname_tmp), "%s.tmp", hname);
|
||||
|
||||
FILE* hfile = fopen(hname, "wb");
|
||||
FILE* hfile = fopen(hname_tmp, "w+b");
|
||||
if (hfile == NULL) {
|
||||
fprintf(stderr, "cannot open %s for writing\n", hname);
|
||||
fprintf(stderr, "cannot open %s for writing\n", hname_tmp);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
@ -174,7 +175,44 @@ main(int argc, char** argv)
|
|||
trailer[0] = '\0';
|
||||
}
|
||||
fprintf(hfile, HEADER_FMT, sym, sym, sym, trailer);
|
||||
fflush(hfile);
|
||||
rewind(hfile);
|
||||
|
||||
int same = 1;
|
||||
{
|
||||
FILE* orig_hfile = fopen(hname, "rb");
|
||||
if (orig_hfile == NULL) {
|
||||
same = 0;
|
||||
} else {
|
||||
while (1) {
|
||||
char orig_line[1024], new_line[1024];
|
||||
|
||||
char* orig_res
|
||||
= fgets(orig_line, sizeof(orig_line), orig_hfile);
|
||||
char* new_res = fgets(new_line, sizeof(new_line), hfile);
|
||||
|
||||
if (orig_res == NULL && new_res == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (orig_res == NULL || new_res == NULL) {
|
||||
same = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (strcmp(orig_line, new_line) != 0) {
|
||||
same = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(hfile);
|
||||
if (!same) {
|
||||
rename(hname_tmp, hname);
|
||||
} else {
|
||||
remove(hname_tmp);
|
||||
}
|
||||
|
||||
fprintf(cfile, "#include \"bin2c.hh\"\n");
|
||||
fprintf(cfile, "\n");
|
Loading…
Reference in New Issue