diff --git a/NEWS b/NEWS index 41b3a1a6..f0b02dbb 100644 --- a/NEWS +++ b/NEWS @@ -60,6 +60,10 @@ lnav v0.11.0: if none of the clipboard commands could be detected. Your terminal software will need to support the sequence and you may need to explicitly enable it in the terminal. + * Added the ":export-session-to " command that writes the + current session state to a file as a list of commands/SQL + statements. This script file can be executed to restore the + majority of the current state. Breaking Changes: * Added a 'language' column to the lnav_view_filters table that @@ -84,6 +88,10 @@ lnav v0.11.0: match log messages that are in that log format instead of all log messages. As a benefit, the search table now includes the columns that are defined as part of the format. + * The lnav_view_filters table will treats the tuple of + (view_name, type, language, pattern) as a UNIQUE index and + will raise a conflict error on an INSERT. Use "REPLACE INTO" + instead of "INSERT INTO" to ignore conflict error. Fixes: * Toggling enabled/disabled filters when there is a SQL expression diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56a5c065..8c1e9824 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -334,6 +334,7 @@ add_library( regex101.client.cc regex101.import.cc relative_time.cc + session.export.cc session_data.cc sequence_matcher.cc shlex.cc @@ -448,6 +449,7 @@ add_library( safe/defaulttypes.h safe/mutableref.h safe/safe.h + session.export.hh sequence_sink.hh shlex.hh shlex.resolver.hh diff --git a/src/Makefile.am b/src/Makefile.am index 7d660085..f457f9a3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -258,6 +258,7 @@ noinst_HEADERS = \ safe/mutableref.h \ safe/safe.h \ service_tags.hh \ + session.export.hh \ session_data.hh \ shared_buffer.hh \ shlex.hh \ @@ -418,6 +419,7 @@ libdiag_a_SOURCES = \ regex101.import.cc \ regexp_vtab.cc \ relative_time.cc \ + session.export.cc \ session_data.cc \ shared_buffer.cc \ shlex.cc \ diff --git a/src/base/auto_mem.hh b/src/base/auto_mem.hh index c7618e24..1f35e517 100644 --- a/src/base/auto_mem.hh +++ b/src/base/auto_mem.hh @@ -52,6 +52,17 @@ using free_func_t = void (*)(void*); template class auto_mem { public: + static void noop_free(void*) {} + + static auto_mem leak(T* ptr) + { + auto_mem retval(noop_free); + + retval = ptr; + + return retval; + } + explicit auto_mem(T* ptr = nullptr) : am_ptr(ptr), am_free_func(default_free) { @@ -156,6 +167,8 @@ private: class auto_buffer { public: + using value_type = char; + static auto_buffer alloc(size_t capacity) { return auto_buffer{capacity == 0 ? nullptr : (char*) malloc(capacity), @@ -223,6 +236,14 @@ public: const char* begin() const { return this->ab_buffer; } + void push_back(char ch) + { + this->ab_buffer[this->ab_size] = ch; + this->ab_size += 1; + } + + void pop_back() { this->ab_size -= 1; } + bool is_bit_set(size_t bit_offset) const { size_t byte_offset = bit_offset / 8; diff --git a/src/internals/cmd-ref.rst b/src/internals/cmd-ref.rst index 314d6a79..7dd521d8 100644 --- a/src/internals/cmd-ref.rst +++ b/src/internals/cmd-ref.rst @@ -44,7 +44,7 @@ :alt-msg Press t to switch to the text view **See Also** - :ref:`echo`, :ref:`eval`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` ---- @@ -67,7 +67,7 @@ :append-to /tmp/interesting-lines.txt **See Also** - :ref:`echo`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` ---- @@ -413,7 +413,7 @@ :echo Hello, World! **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -472,7 +472,23 @@ :eval ;SELECT * FROM ${table} **See Also** - :ref:`alt_msg`, :ref:`echo`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`echo`, :ref:`export_session_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + +---- + + +.. _export_session_to: + +:export-session-to *path* +^^^^^^^^^^^^^^^^^^^^^^^^^ + + Export the current lnav state to an lnav script file + + **Parameters** + * **path\*** --- The path to the file to write + + **See Also** + :ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -884,7 +900,7 @@ :pipe-line-to sed -e 's/foo/bar/g' **See Also** - :ref:`append_to`, :ref:`echo`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + :ref:`append_to`, :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` ---- @@ -907,7 +923,7 @@ :pipe-to sed -e s/foo/bar/g **See Also** - :ref:`append_to`, :ref:`echo`, :ref:`pipe_line_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` + :ref:`append_to`, :ref:`echo`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to` ---- @@ -1029,7 +1045,7 @@ :redirect-to /tmp/script-output.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1417,7 +1433,7 @@ :write-table-to /tmp/table.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1440,7 +1456,7 @@ :write-csv-to /tmp/table.csv **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1463,7 +1479,7 @@ :write-json-to /tmp/table.json **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1486,7 +1502,7 @@ :write-jsonlines-to /tmp/table.json **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1510,7 +1526,7 @@ :write-raw-to /tmp/table.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1533,7 +1549,7 @@ :write-screen-to /tmp/table.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1556,7 +1572,7 @@ :write-to /tmp/interesting-lines.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to` ---- @@ -1579,7 +1595,7 @@ :write-view-to /tmp/table.txt **See Also** - :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to` + :ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to` ---- diff --git a/src/lnav_commands.cc b/src/lnav_commands.cc index f865d1df..c3563097 100644 --- a/src/lnav_commands.cc +++ b/src/lnav_commands.cc @@ -58,6 +58,7 @@ #include "field_overlay_source.hh" #include "fmt/printf.h" #include "lnav.indexing.hh" +#include "session.export.hh" #include "lnav_commands.hh" #include "lnav_config.hh" #include "lnav_util.hh" @@ -3729,6 +3730,71 @@ com_save_session(exec_context& ec, return Ok(std::string()); } +static Result +com_export_session_to(exec_context& ec, + std::string cmdline, + std::vector& args) +{ + if (args.empty()) { + args.emplace_back("filename"); + } else if (!ec.ec_dry_run) { + auto_mem outfile(fclose); + auto fn = trim(remaining_args(cmdline, args)); + auto to_term = false; + + if (fn == "-" || fn == "/dev/stdout") { + auto ec_out = ec.get_output(); + + if (!ec_out) { + outfile = auto_mem::leak(stdout); + nodelay(lnav_data.ld_window, 0); + endwin(); + struct termios curr_termios; + tcgetattr(1, &curr_termios); + curr_termios.c_oflag |= ONLCR | OPOST; + tcsetattr(1, TCSANOW, &curr_termios); + setvbuf(stdout, nullptr, _IONBF, 0); + to_term = true; + fprintf(outfile, + "\n---------------- Press any key to exit lo-fi display " + "----------------\n\n"); + } else { + outfile = auto_mem::leak(ec_out.value()); + } + if (outfile.in() == stdout) { + lnav_data.ld_stdout_used = true; + } + } else if (fn == "/dev/clipboard") { + auto open_res = sysclip::open(sysclip::type_t::GENERAL); + if (open_res.isErr()) { + alerter::singleton().chime(); + return ec.make_error("Unable to copy to clipboard: {}", + open_res.unwrapErr()); + } + outfile = open_res.unwrap(); + } else if (lnav_data.ld_flags & LNF_SECURE_MODE) { + return ec.make_error("{} -- unavailable in secure mode", args[0]); + } else if ((outfile = fopen(fn.c_str(), "w")) == nullptr) { + return ec.make_error("unable to open file -- {}", fn); + } + + auto export_res = lnav::session::export_to(outfile.in()); + if (export_res.isErr()) { + return Err(export_res.unwrapErr()); + } + + fflush(outfile.in()); + if (to_term) { + cbreak(); + getch(); + refresh(); + nodelay(lnav_data.ld_window, 1); + } + } + + return Ok(std::string()); +} + static Result com_set_min_log_level(exec_context& ec, std::string cmdline, @@ -5521,6 +5587,13 @@ readline_context::command_t STD_COMMANDS[] = { help_text(":save-session") .with_summary("Save the current state as a session")}, + {"export-session-to", + com_export_session_to, + + help_text(":export-session-to") + .with_summary("Export the current lnav state to an lnav script file") + .with_parameter(help_text("path", "The path to the file to write")) + .with_tags({"io", "scripting"})}, {"set-min-log-level", com_set_min_log_level, diff --git a/src/lnav_util.hh b/src/lnav_util.hh index 781ce631..4a31f20d 100644 --- a/src/lnav_util.hh +++ b/src/lnav_util.hh @@ -35,6 +35,7 @@ #define lnav_util_hh #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include +#include "base/auto_mem.hh" #include "base/intern_string.hh" #include "base/lnav.console.hh" #include "base/result.h" @@ -67,10 +69,10 @@ class hasher { public: - hasher() - { - this->h_context.Init(0, 0); - } + using array_t = byte_array<2, uint64_t>; + static constexpr size_t STRING_SIZE = array_t::STRING_SIZE; + + hasher() { this->h_context.Init(0, 0); } hasher& update(const std::string& str) { @@ -102,9 +104,17 @@ public: return *this; } + void to_string(auto_buffer& buf) + { + array_t bits; + + this->h_context.Final(bits.out(0), bits.out(1)); + bits.to_string(std::back_inserter(buf)); + } + std::string to_string() { - byte_array<2, uint64> bits; + array_t bits; this->h_context.Final(bits.out(0), bits.out(1)); return bits.to_string(); diff --git a/src/log_vtab_impl.cc b/src/log_vtab_impl.cc index dcfcd3e7..3a86d66e 100644 --- a/src/log_vtab_impl.cc +++ b/src/log_vtab_impl.cc @@ -74,7 +74,8 @@ static const char* LOG_FOOTER_COLUMNS = R"( log_unique_path TEXT HIDDEN COLLATE naturalnocase, -- The unique portion of the path this message is from log_text TEXT HIDDEN, -- The full text of the log message log_body TEXT HIDDEN, -- The body of the log message - log_raw_text TEXT HIDDEN -- The raw text from the log file + log_raw_text TEXT HIDDEN, -- The raw text from the log file + log_line_hash TEXT HIDDEN -- A hash of the first line of the log message )"; enum class log_footer_columns : uint32_t { @@ -87,6 +88,7 @@ enum class log_footer_columns : uint32_t { text, body, raw_text, + line_hash, }; static const char* @@ -914,6 +916,29 @@ vt_column(sqlite3_vtab_cursor* cur, sqlite3_context* ctx, int col) } break; } + case log_footer_columns::line_hash: { + auto read_res = lf->read_line(ll); + + if (read_res.isErr()) { + auto msg = fmt::format( + FMT_STRING("unable to read line -- {}"), + read_res.unwrapErr()); + sqlite3_result_error( + ctx, msg.c_str(), msg.length()); + } else { + auto sbr = read_res.unwrap(); + hasher line_hasher; + + auto outbuf + = auto_buffer::alloc(hasher::STRING_SIZE); + line_hasher.update(sbr.get_data(), sbr.length()) + .update(cl) + .to_string(outbuf); + auto tab = text_auto_buffer{std::move(outbuf)}; + to_sqlite(ctx, tab); + } + break; + } } } else { if (vc->line_values.empty()) { @@ -1508,6 +1533,7 @@ vt_filter(sqlite3_vtab_cursor* p_vtc, case log_footer_columns::text: case log_footer_columns::body: case log_footer_columns::raw_text: + case log_footer_columns::line_hash: break; } } else { @@ -1789,6 +1815,7 @@ vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info) case log_footer_columns::text: case log_footer_columns::body: case log_footer_columns::raw_text: + case log_footer_columns::line_hash: break; } } else if (op == SQLITE_INDEX_CONSTRAINT_EQ) { diff --git a/src/session.export.cc b/src/session.export.cc new file mode 100644 index 00000000..436c7df0 --- /dev/null +++ b/src/session.export.cc @@ -0,0 +1,233 @@ +/** + * 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. + */ + +#include "session.export.hh" + +#include "base/injector.hh" +#include "bound_tags.hh" +#include "lnav.hh" +#include "sqlitepp.client.hh" +#include "sqlitepp.hh" +#include "textview_curses.hh" + +struct log_message_session_state { + int64_t lmss_time_msecs; + std::string lmss_format; + bool lmss_mark; + nonstd::optional lmss_comment; + nonstd::optional lmss_tags; + std::string lmss_hash; +}; + +template<> +struct from_sqlite { + inline log_message_session_state operator()(int argc, + sqlite3_value** argv, + int argi) + { + return { + from_sqlite()(argc, argv, argi + 0), + from_sqlite()(argc, argv, argi + 1), + from_sqlite()(argc, argv, argi + 2), + from_sqlite>()(argc, argv, argi + 3), + from_sqlite>()(argc, argv, argi + 4), + from_sqlite()(argc, argv, argi + 5), + }; + } +}; + +struct log_filter_session_state { + std::string lfss_name; + bool lfss_enabled; + std::string lfss_type; + std::string lfss_language; + std::string lfss_pattern; +}; + +template<> +struct from_sqlite { + inline log_filter_session_state operator()(int argc, + sqlite3_value** argv, + int argi) + { + return { + from_sqlite()(argc, argv, argi + 0), + from_sqlite()(argc, argv, argi + 1), + from_sqlite()(argc, argv, argi + 2), + from_sqlite()(argc, argv, argi + 3), + from_sqlite()(argc, argv, argi + 4), + }; + } +}; + +namespace lnav { +namespace session { + +Result +export_to(FILE* file) +{ + static auto& lnav_db + = injector::get&, + sqlite_db_tag>(); + + static const char* BOOKMARK_QUERY = R"( +SELECT log_time_msecs, log_format, log_mark, log_comment, log_tags, log_line_hash + FROM all_logs + WHERE log_mark = 1 OR log_comment IS NOT NULL OR log_tags IS NOT NULL +)"; + + static const char* FILTER_QUERY = R"( +SELECT view_name, enabled, type, language, pattern FROM lnav_view_filters +)"; + + console::user_message errmsg; + + auto prep_res = prepare_stmt(lnav_db.in(), BOOKMARK_QUERY); + if (prep_res.isErr()) { + return Err( + console::user_message::error("unable to export log bookmarks") + .with_reason(prep_res.unwrapErr())); + } + + auto bookmark_stmt = prep_res.unwrap(); + auto done = false; + while (!done) { + done = bookmark_stmt.fetch_row().match( + [file](const log_message_session_state& lmss) { + fmt::print(file, + FMT_STRING(";UPDATE all_logs " + "SET log_mark = {}, " + "log_comment = {}, " + "log_tags = {} " + "WHERE log_time_msecs = {} AND " + "log_format = {} AND " + "log_line_hash = {}\n"), + lmss.lmss_mark ? "1" : "0", + sqlitepp::quote(lmss.lmss_comment), + sqlitepp::quote(lmss.lmss_tags), + lmss.lmss_time_msecs, + sqlitepp::quote(lmss.lmss_format), + sqlitepp::quote(lmss.lmss_hash)); + return false; + }, + [](prepared_stmt::end_of_rows) { return true; }, + [&errmsg](const prepared_stmt::fetch_error& fe) { + errmsg + = console::user_message::error( + "failed to fetch bookmark metadata for log message") + .with_reason(fe.fe_msg); + return true; + }); + } + + if (!errmsg.um_message.empty()) { + return Err(errmsg); + } + + auto prep_filter_res = prepare_stmt(lnav_db.in(), FILTER_QUERY); + if (prep_filter_res.isErr()) { + return Err(console::user_message::error("unable to export filter state") + .with_reason(prep_filter_res.unwrapErr())); + } + + auto filter_stmt = prep_filter_res.unwrap(); + done = false; + while (!done) { + done = filter_stmt.fetch_row().match( + [file](const log_filter_session_state& lfss) { + fmt::print( + file, + FMT_STRING(";REPLACE INTO lnav_view_filters " + "(view_name, enabled, type, language, pattern) " + "VALUES ({}, {}, {}, {}, {})\n"), + sqlitepp::quote(lfss.lfss_name), + lfss.lfss_enabled ? 1 : 0, + sqlitepp::quote(lfss.lfss_type), + sqlitepp::quote(lfss.lfss_language), + sqlitepp::quote(lfss.lfss_pattern)); + return false; + }, + [](prepared_stmt::end_of_rows) { return true; }, + [&errmsg](const prepared_stmt::fetch_error& fe) { + errmsg = console::user_message::error( + "failed to fetch filter state for views") + .with_reason(fe.fe_msg); + return true; + }); + } + + if (!errmsg.um_message.empty()) { + return Err(errmsg); + } + + for (auto view_index : {LNV_LOG, LNV_TEXT}) { + auto& tc = lnav_data.ld_views[view_index]; + if (tc.get_inner_height() == 0_vl) { + continue; + } + + fmt::print(file, + FMT_STRING(":switch-to-view {}\n"), + lnav_view_strings[view_index]); + + auto* tss = tc.get_sub_source(); + auto* lss = dynamic_cast(tss); + if (lss != nullptr) { + auto min_level = lss->get_min_log_level(); + + if (min_level != LEVEL_UNKNOWN) { + fmt::print(file, + FMT_STRING(":set-min-log-level {}"), + level_names[min_level]); + } + + struct timeval min_time, max_time; + char tsbuf[128]; + if (lss->get_min_log_time(min_time)) { + sql_strftime(tsbuf, sizeof(tsbuf), min_time, 'T'); + fmt::print(file, FMT_STRING(":hide-lines-before {}"), tsbuf); + } + if (lss->get_max_log_time(max_time)) { + sql_strftime(tsbuf, sizeof(tsbuf), max_time, 'T'); + fmt::print(file, FMT_STRING(":hide-lines-after {}"), tsbuf); + } + } + + if (!tc.get_current_search().empty()) { + fmt::print(file, FMT_STRING("/{}\n"), tc.get_current_search()); + } + + fmt::print(file, FMT_STRING(":goto {}\n"), (int) tc.get_top()); + } + + return Ok(); +} + +} // namespace session +} // namespace lnav diff --git a/src/session.export.hh b/src/session.export.hh new file mode 100644 index 00000000..a913e675 --- /dev/null +++ b/src/session.export.hh @@ -0,0 +1,43 @@ +/** + * 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_session_export_hh +#define lnav_session_export_hh + +#include "base/lnav.console.hh" + +namespace lnav { +namespace session { + +Result export_to(FILE* file); + +} +} // namespace lnav + +#endif diff --git a/src/session_data.cc b/src/session_data.cc index 5c3e05c1..38b5abc8 100644 --- a/src/session_data.cc +++ b/src/session_data.cc @@ -396,30 +396,29 @@ load_time_bookmarks() } { - if (sqlite3_prepare_v2(db.in(), - "SELECT netloc FROM recent_netlocs", - -1, - stmt.out(), - nullptr) - != SQLITE_OK) - { + auto netloc_prep_res + = prepare_stmt(db.in(), "SELECT netloc FROM recent_netlocs"); + if (netloc_prep_res.isErr()) { + log_error("unable to get netlocs: %s", + netloc_prep_res.unwrapErr().c_str()); return; } + auto netloc_stmt = netloc_prep_res.unwrap(); bool done = false; while (!done) { - switch (sqlite3_step(stmt.in())) { - case SQLITE_ROW: { - const auto* netloc = sqlite3_column_text(stmt.in(), 0); - - session_data.sd_recent_netlocs.insert((const char*) netloc); - break; - } - default: - done = true; - break; - } + done = netloc_stmt.fetch_row().match( + [](const std::string& netloc) { + session_data.sd_recent_netlocs.insert(netloc); + return false; + }, + [](const prepared_stmt::fetch_error& fe) { + log_error("failed to fetch netloc row: %s", + fe.fe_msg.c_str()); + return true; + }, + [](prepared_stmt::end_of_rows) { return true; }); } } @@ -855,16 +854,18 @@ read_commands(yajlpp_parse_context* ypc, const unsigned char* str, size_t len) return 1; } -static struct json_path_container view_def_handlers - = {json_path_handler("top_line", read_top_line), - json_path_handler("search", read_current_search), - json_path_handler("word_wrap", read_word_wrap), - json_path_handler("filtering", read_filtering), - json_path_handler("commands#", read_commands)}; +static struct json_path_container view_def_handlers = { + json_path_handler("top_line", read_top_line), + json_path_handler("search", read_current_search), + json_path_handler("word_wrap", read_word_wrap), + json_path_handler("filtering", read_filtering), + json_path_handler("commands#", read_commands), +}; -static struct json_path_container view_handlers - = {yajlpp::pattern_property_handler("([^/]+)").with_children( - view_def_handlers)}; +static struct json_path_container view_handlers = { + yajlpp::pattern_property_handler("([^/]+)").with_children( + view_def_handlers), +}; static struct json_path_container file_state_handlers = { yajlpp::property_handler("visible") @@ -1313,10 +1314,9 @@ save_time_bookmarks() } for (auto& ls : lss) { - logfile::iterator line_iter; - - if (ls->get_file() == nullptr) + if (ls->get_file() == nullptr) { continue; + } auto lf = ls->get_file(); @@ -1324,7 +1324,7 @@ save_time_bookmarks() continue; } - line_iter = lf->begin() + lf->get_time_offset_line(); + auto line_iter = lf->begin() + lf->get_time_offset_line(); struct timeval offset = lf->get_time_offset(); auto read_result = lf->read_line(line_iter); diff --git a/src/spookyhash/SpookyV2.cpp b/src/spookyhash/SpookyV2.cpp index 735bd562..fa9793a7 100644 --- a/src/spookyhash/SpookyV2.cpp +++ b/src/spookyhash/SpookyV2.cpp @@ -300,19 +300,18 @@ void SpookyHash::Update(const void *message, size_t length) m_state[11] = h11; } - // report the hash for the concatenation of all message fragments so far -void SpookyHash::Final(uint64 *hash1, uint64 *hash2) +void +SpookyHash::Final(uint64* hash1, uint64* hash2) const { // init the variables - if (m_length < sc_bufSize) - { + if (m_length < sc_bufSize) { *hash1 = m_state[0]; *hash2 = m_state[1]; - Short( m_data, m_length, hash1, hash2); + Short(m_data, m_length, hash1, hash2); return; } - + const uint64 *data = (const uint64 *)m_data; uint8 remainder = m_remainder; diff --git a/src/spookyhash/SpookyV2.h b/src/spookyhash/SpookyV2.h index 4ddb37cc..caa2f296 100644 --- a/src/spookyhash/SpookyV2.h +++ b/src/spookyhash/SpookyV2.h @@ -98,7 +98,6 @@ public: const void *message, // message fragment size_t length); // length of message fragment in bytes - // // Final: compute the hash for the current SpookyHash state // @@ -107,9 +106,8 @@ public: // The result is the same as if SpookyHash() had been called with // all the pieces concatenated into one message. // - void Final( - uint64 *hash1, // out only: first 64 bits of hash value. - uint64 *hash2); // out only: second 64 bits of hash value. + void Final(uint64* hash1, // out only: first 64 bits of hash value. + uint64* hash2) const; // out only: second 64 bits of hash value. // // left rotate a 64-bit value by k bytes diff --git a/src/sqlitepp.hh b/src/sqlitepp.hh index 963c6eb8..489abef0 100644 --- a/src/sqlitepp.hh +++ b/src/sqlitepp.hh @@ -32,7 +32,29 @@ #ifndef lnav_sqlitepp_hh #define lnav_sqlitepp_hh +#include + +#include "base/auto_mem.hh" + /* XXX figure out how to do this with the template */ void sqlite_close_wrapper(void* mem); +namespace sqlitepp { + +inline auto_mem +quote(const nonstd::optional& str) +{ + auto_mem retval(sqlite3_free); + + if (str) { + retval = sqlite3_mprintf("%Q", str.value().c_str()); + } else { + retval = sqlite3_mprintf("NULL"); + } + + return retval; +} + +} // namespace sqlitepp + #endif diff --git a/src/textview_curses.cc b/src/textview_curses.cc index 66d46b69..f9ca3f23 100644 --- a/src/textview_curses.cc +++ b/src/textview_curses.cc @@ -981,6 +981,12 @@ filter_stack::get_enabled_mask(uint32_t& filter_in_mask, } } +void +filter_stack::add_filter(const std::shared_ptr& filter) +{ + this->fs_filters.push_back(filter); +} + void vis_location_history::loc_history_append(vis_line_t top) { diff --git a/src/textview_curses.hh b/src/textview_curses.hh index 70f4bf2e..c8e67e51 100644 --- a/src/textview_curses.hh +++ b/src/textview_curses.hh @@ -98,7 +98,9 @@ public: } type_t; text_filter(type_t type, filter_lang_t lang, std::string id, size_t index) - : lf_type(type), lf_lang(lang), lf_id(std::move(id)), lf_index(index){}; + : lf_type(type), lf_lang(lang), lf_id(std::move(id)), lf_index(index) + { + } virtual ~text_filter() = default; type_t get_type() const { return this->lf_type; } @@ -183,10 +185,7 @@ public: nonstd::optional next_index(); - void add_filter(const std::shared_ptr& filter) - { - this->fs_filters.push_back(filter); - } + void add_filter(const std::shared_ptr& filter); void clear_filters() { diff --git a/src/views_vtab.cc b/src/views_vtab.cc index 798c20e5..da9a0e23 100644 --- a/src/views_vtab.cc +++ b/src/views_vtab.cc @@ -635,9 +635,10 @@ CREATE TABLE lnav_view_filters ( nonstd::optional lang, sqlite3_value* pattern_str) { - textview_curses& tc = lnav_data.ld_views[view_index]; - text_sub_source* tss = tc.get_sub_source(); - filter_stack& fs = tss->get_filters(); + auto* mod_vt = (vtab_module::vtab*) tab; + auto& tc = lnav_data.ld_views[view_index]; + auto* tss = tc.get_sub_source(); + auto& fs = tss->get_filters(); auto filter_index = lang.value_or(filter_lang_t::REGEX) == filter_lang_t::REGEX ? fs.next_index() @@ -645,6 +646,7 @@ CREATE TABLE lnav_view_filters ( if (!filter_index) { throw sqlite_func_error("Too many filters"); } + auto conflict_mode = sqlite3_vtab_on_conflict(mod_vt->v_db); std::shared_ptr tf; switch (lang.value_or(filter_lang_t::REGEX)) { case filter_lang_t::REGEX: { @@ -656,6 +658,26 @@ CREATE TABLE lnav_view_filters ( pattern.first, *filter_index, pattern.second.release()); + auto new_cmd = pf->to_command(); + for (auto& filter : fs) { + if (filter->to_command() == new_cmd) { + switch (conflict_mode) { + case SQLITE_FAIL: + case SQLITE_ABORT: + tab->zErrMsg = sqlite3_mprintf( + "filter already exists -- %s", + new_cmd.c_str()); + return conflict_mode; + case SQLITE_IGNORE: + return SQLITE_OK; + case SQLITE_REPLACE: + filter->set_enabled(pf->is_enabled()); + return SQLITE_OK; + default: + break; + } + } + } fs.add_filter(pf); tf = pf; break; @@ -665,6 +687,19 @@ CREATE TABLE lnav_view_filters ( throw sqlite_func_error( "SQL filters are only supported in the log view"); } + if (lnav_data.ld_log_source.get_sql_filter_text() != "") { + switch (conflict_mode) { + case SQLITE_FAIL: + case SQLITE_ABORT: + tab->zErrMsg = sqlite3_mprintf( + "A SQL filter already exists"); + return conflict_mode; + case SQLITE_IGNORE: + return SQLITE_OK; + default: + break; + } + } auto clause = from_sqlite()(1, &pattern_str, 0); auto expr = fmt::format(FMT_STRING("SELECT 1 WHERE {}"), clause); diff --git a/src/vtab_module.hh b/src/vtab_module.hh index c4589ecd..00cc6c70 100644 --- a/src/vtab_module.hh +++ b/src/vtab_module.hh @@ -324,7 +324,7 @@ struct ToSqliteVisitor { template void operator()(T&& t) const { - to_sqlite(this->tsv_context, t); + to_sqlite(this->tsv_context, std::forward(t)); } sqlite3_context* tsv_context; @@ -612,11 +612,12 @@ struct vtab_module_base { template struct vtab_module : public vtab_module_base { struct vtab { - explicit vtab(T& impl) : v_impl(impl) {} + explicit vtab(sqlite3* db, T& impl) : v_db(db), v_impl(impl) {} explicit operator sqlite3_vtab*() { return &this->base; } sqlite3_vtab v_base{}; + sqlite3* v_db; T& v_impl; }; @@ -628,7 +629,7 @@ struct vtab_module : public vtab_module_base { char** pzErr) { auto* mod = static_cast*>(pAux); - auto vt = new vtab(mod->vm_impl); + auto vt = new vtab(db, mod->vm_impl); *pp_vt = (sqlite3_vtab*) &vt->v_base; @@ -802,7 +803,9 @@ struct vtab_module : public vtab_module_base { } template - void addUpdate(...){}; + void addUpdate(...) + { + } template vtab_module(Args&... args) noexcept : vm_impl(args...) diff --git a/test/expected/expected.am b/test/expected/expected.am index 0e70bb03..ba7c4445 100644 --- a/test/expected/expected.am +++ b/test/expected/expected.am @@ -340,6 +340,8 @@ EXPECTED_FILES = \ $(srcdir)/%reldir%/test_sessions.sh_68a89b56c5e7f7db620084cca1eb547cbb19a2c9.out \ $(srcdir)/%reldir%/test_sessions.sh_858fd0081ed9c46dd81e2f81f1090756f2463558.err \ $(srcdir)/%reldir%/test_sessions.sh_858fd0081ed9c46dd81e2f81f1090756f2463558.out \ + $(srcdir)/%reldir%/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.err \ + $(srcdir)/%reldir%/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out \ $(srcdir)/%reldir%/test_sessions.sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.err \ $(srcdir)/%reldir%/test_sessions.sh_903b41c950f5f90d7786d7a09bb6e2f217654b15.out \ $(srcdir)/%reldir%/test_sessions.sh_92a98a3e4e3a10bf1f2371d21a8282c5d3d4baa5.err \ diff --git a/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out b/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out index 637b3092..42653792 100644 --- a/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out +++ b/test/expected/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.out @@ -709,9 +709,9 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter msg The message to display See Also - :echo, :eval, :redirect-to, :write-csv-to, :write-json-to, - :write-jsonlines-to, :write-raw-to, :write-screen-to, :write-table-to, - :write-to, :write-view-to + :echo, :eval, :export-session-to, :redirect-to, :write-csv-to, + :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, + :write-table-to, :write-to, :write-view-to Example #1 To display 'Press t to switch to the text view' on the bottom right: :alt-msg Press t to switch to the text view  @@ -724,9 +724,9 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter path The path to the file to append to See Also - :echo, :pipe-line-to, :pipe-to, :redirect-to, :write-csv-to, - :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, - :write-table-to, :write-to, :write-view-to + :echo, :export-session-to, :pipe-line-to, :pipe-to, :redirect-to, + :write-csv-to, :write-json-to, :write-jsonlines-to, :write-raw-to, + :write-screen-to, :write-table-to, :write-to, :write-view-to Example #1 To append marked lines to the file /tmp/interesting-lines.txt: :append-to /tmp/interesting-lines.txt  @@ -938,12 +938,12 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter msg The message to display See Also - :alt-msg, :append-to, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-json-to, - :write-json-to, :write-jsonlines-to, :write-jsonlines-to, - :write-raw-to, :write-raw-to, :write-screen-to, :write-screen-to, - :write-table-to, :write-table-to, :write-to, :write-to, :write-view-to, - :write-view-to + :alt-msg, :append-to, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-json-to, :write-json-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-screen-to, + :write-screen-to, :write-table-to, :write-table-to, :write-to, + :write-to, :write-view-to, :write-view-to Example #1 To output 'Hello, World!': :echo Hello, World!  @@ -978,15 +978,28 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter command The command or query to perform substitution on. See Also - :alt-msg, :echo, :redirect-to, :write-csv-to, :write-json-to, - :write-jsonlines-to, :write-raw-to, :write-screen-to, :write-table-to, - :write-to, :write-view-to + :alt-msg, :echo, :export-session-to, :redirect-to, :write-csv-to, + :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, + :write-table-to, :write-to, :write-view-to Example #1 To substitute the table name from a variable: :eval ;SELECT * FROM ${table}  +:export-session-to path +══════════════════════════════════════════════════════════════════════ + Export the current lnav state to an lnav script file +Parameter + path The path to the file to write +See Also + :alt-msg, :append-to, :echo, :echo, :eval, :pipe-line-to, :pipe-to, + :redirect-to, :redirect-to, :write-csv-to, :write-csv-to, + :write-json-to, :write-json-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-screen-to, + :write-screen-to, :write-table-to, :write-table-to, :write-to, + :write-to, :write-view-to, :write-view-to + :filter-expr expr ══════════════════════════════════════════════════════════════════════ Set the filter expression @@ -1235,9 +1248,9 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter shell-cmd The shell command-line to execute See Also - :append-to, :echo, :pipe-to, :redirect-to, :write-csv-to, - :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, - :write-table-to, :write-to, :write-view-to + :append-to, :echo, :export-session-to, :pipe-to, :redirect-to, + :write-csv-to, :write-json-to, :write-jsonlines-to, :write-raw-to, + :write-screen-to, :write-table-to, :write-to, :write-view-to Example #1 To write the top line to 'sed' for processing: :pipe-line-to sed -e 's/foo/bar/g'  @@ -1250,9 +1263,9 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter shell-cmd The shell command-line to execute See Also - :append-to, :echo, :pipe-line-to, :redirect-to, :write-csv-to, - :write-json-to, :write-jsonlines-to, :write-raw-to, :write-screen-to, - :write-table-to, :write-to, :write-view-to + :append-to, :echo, :export-session-to, :pipe-line-to, :redirect-to, + :write-csv-to, :write-json-to, :write-jsonlines-to, :write-raw-to, + :write-screen-to, :write-table-to, :write-to, :write-view-to Example #1 To write marked lines to 'sed' for processing: :pipe-to sed -e s/foo/bar/g  @@ -1325,11 +1338,12 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write. If not specified, the current redirect will be cleared See Also - :alt-msg, :append-to, :echo, :echo, :eval, :pipe-line-to, :pipe-to, - :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, - :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, - :write-screen-to, :write-screen-to, :write-table-to, :write-table-to, - :write-to, :write-to, :write-view-to, :write-view-to + :alt-msg, :append-to, :echo, :echo, :eval, :export-session-to, + :export-session-to, :pipe-line-to, :pipe-to, :write-csv-to, + :write-csv-to, :write-json-to, :write-json-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-screen-to, + :write-screen-to, :write-table-to, :write-table-to, :write-to, + :write-to, :write-view-to, :write-view-to Example #1 To write the output of lnav commands to the file /tmp/script-output.txt: :redirect-to /tmp/script-output.txt  @@ -1553,12 +1567,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, - :write-raw-to, :write-screen-to, :write-screen-to, :write-screen-to, - :write-to, :write-to, :write-view-to, :write-view-to, :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-screen-to, :write-screen-to, :write-screen-to, :write-to, + :write-to, :write-view-to, :write-view-to, :write-view-to Example #1 To write SQL results as text to /tmp/table.txt: :write-table-to /tmp/table.txt  @@ -1572,13 +1587,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-json-to, :write-json-to, :write-json-to, - :write-jsonlines-to, :write-jsonlines-to, :write-jsonlines-to, - :write-raw-to, :write-raw-to, :write-raw-to, :write-screen-to, - :write-screen-to, :write-screen-to, :write-table-to, :write-table-to, - :write-table-to, :write-to, :write-to, :write-view-to, :write-view-to, - :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-json-to, + :write-json-to, :write-json-to, :write-jsonlines-to, + :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, + :write-raw-to, :write-screen-to, :write-screen-to, :write-screen-to, + :write-table-to, :write-table-to, :write-table-to, :write-to, + :write-to, :write-view-to, :write-view-to, :write-view-to Example #1 To write SQL results as CSV to /tmp/table.csv: :write-csv-to /tmp/table.csv  @@ -1592,13 +1607,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-jsonlines-to, :write-jsonlines-to, :write-jsonlines-to, - :write-raw-to, :write-raw-to, :write-raw-to, :write-screen-to, - :write-screen-to, :write-screen-to, :write-table-to, :write-table-to, - :write-table-to, :write-to, :write-to, :write-view-to, :write-view-to, - :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-screen-to, :write-screen-to, :write-screen-to, :write-table-to, + :write-table-to, :write-table-to, :write-to, :write-to, :write-view-to, + :write-view-to, :write-view-to Example #1 To write SQL results as JSON to /tmp/table.json: :write-json-to /tmp/table.json  @@ -1612,12 +1627,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-raw-to, - :write-raw-to, :write-raw-to, :write-screen-to, :write-screen-to, - :write-screen-to, :write-table-to, :write-table-to, :write-table-to, - :write-to, :write-to, :write-view-to, :write-view-to, :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-screen-to, :write-screen-to, :write-screen-to, :write-table-to, + :write-table-to, :write-table-to, :write-to, :write-to, :write-view-to, + :write-view-to, :write-view-to Example #1 To write SQL results as JSON Lines to /tmp/table.json: :write-jsonlines-to /tmp/table.json  @@ -1635,13 +1651,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-jsonlines-to, :write-screen-to, - :write-screen-to, :write-screen-to, :write-table-to, :write-table-to, - :write-table-to, :write-to, :write-to, :write-view-to, :write-view-to, - :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-screen-to, :write-screen-to, + :write-screen-to, :write-table-to, :write-table-to, :write-table-to, + :write-to, :write-to, :write-view-to, :write-view-to, :write-view-to Example #1 To write the marked lines in the log view to /tmp/table.txt: :write-raw-to /tmp/table.txt  @@ -1656,12 +1672,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, - :write-raw-to, :write-table-to, :write-table-to, :write-table-to, - :write-to, :write-to, :write-view-to, :write-view-to, :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-table-to, :write-table-to, :write-table-to, :write-to, + :write-to, :write-view-to, :write-view-to, :write-view-to Example #1 To write only the displayed text to /tmp/table.txt: :write-screen-to /tmp/table.txt  @@ -1675,12 +1692,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, - :write-raw-to, :write-screen-to, :write-screen-to, :write-screen-to, - :write-to, :write-to, :write-view-to, :write-view-to, :write-view-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-screen-to, :write-screen-to, :write-screen-to, :write-to, + :write-to, :write-view-to, :write-view-to, :write-view-to Example #1 To write SQL results as text to /tmp/table.txt: :write-table-to /tmp/table.txt  @@ -1693,12 +1711,12 @@ lnav@googlegroups.com[1] support@lnav.org[2] Parameter path The path to the file to write See Also - :alt-msg, :append-to, :echo, :echo, :eval, :pipe-line-to, :pipe-to, - :redirect-to, :redirect-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-screen-to, - :write-screen-to, :write-table-to, :write-table-to, :write-view-to, - :write-view-to + :alt-msg, :append-to, :echo, :echo, :eval, :export-session-to, + :export-session-to, :pipe-line-to, :pipe-to, :redirect-to, + :redirect-to, :write-csv-to, :write-csv-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-raw-to, :write-raw-to, :write-screen-to, :write-screen-to, + :write-table-to, :write-table-to, :write-view-to, :write-view-to Example #1 To write marked lines to the file /tmp/interesting-lines.txt: :write-to /tmp/interesting-lines.txt  @@ -1713,13 +1731,13 @@ lnav@googlegroups.com[1] support@lnav.org[2] path The path to the file to write See Also :alt-msg, :append-to, :create-logline-table, :create-search-table, - :echo, :echo, :eval, :pipe-line-to, :pipe-to, :redirect-to, - :redirect-to, :write-csv-to, :write-csv-to, :write-csv-to, - :write-json-to, :write-json-to, :write-json-to, :write-jsonlines-to, - :write-jsonlines-to, :write-jsonlines-to, :write-raw-to, :write-raw-to, - :write-raw-to, :write-screen-to, :write-screen-to, :write-screen-to, - :write-table-to, :write-table-to, :write-table-to, :write-to, - :write-to + :echo, :echo, :eval, :export-session-to, :export-session-to, + :pipe-line-to, :pipe-to, :redirect-to, :redirect-to, :write-csv-to, + :write-csv-to, :write-csv-to, :write-json-to, :write-json-to, + :write-json-to, :write-jsonlines-to, :write-jsonlines-to, + :write-jsonlines-to, :write-raw-to, :write-raw-to, :write-raw-to, + :write-screen-to, :write-screen-to, :write-screen-to, :write-table-to, + :write-table-to, :write-table-to, :write-to, :write-to Example #1 To write the top view to /tmp/table.txt: :write-view-to /tmp/table.txt  diff --git a/test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.err b/test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.err new file mode 100644 index 00000000..e69de29b diff --git a/test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out b/test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out new file mode 100644 index 00000000..4369a804 --- /dev/null +++ b/test/expected/test_sessions.sh_8732dad5481be991ca7f291d9c5451c7b016cea7.out @@ -0,0 +1,3 @@ +;UPDATE all_logs SET log_mark = 1, log_comment = NULL, log_tags = NULL WHERE log_time_msecs = 1248130769000 AND log_format = 'access_log' AND log_line_hash = 'b05c1bdfe75cde41e151c89087e31951' +:switch-to-view log +:goto 1 diff --git a/test/lnav_doctests.cc b/test/lnav_doctests.cc index 873f85cb..29828ee2 100644 --- a/test/lnav_doctests.cc +++ b/test/lnav_doctests.cc @@ -81,6 +81,10 @@ TEST_CASE("byte_array") ba1.clear(); CHECK(ba1.to_string() == "0000000000000000"); CHECK(ba2.to_string() == "6162636431323334"); + + auto outbuf = auto_buffer::alloc(my_array_t::STRING_SIZE); + ba2.to_string(std::back_inserter(outbuf)); + CHECK(std::string(outbuf.in(), outbuf.size()) == "6162636431323334"); } TEST_CASE("ptime_fmt") diff --git a/test/test_sessions.sh b/test/test_sessions.sh index 03ebb5a1..25fe620c 100644 --- a/test/test_sessions.sh +++ b/test/test_sessions.sh @@ -26,6 +26,12 @@ run_cap_test ${lnav_test} -nq \ -c ":save-session" \ ${test_dir}/logfile_access_log.0 +run_cap_test ${lnav_test} -nq \ + -c ";update access_log set log_mark = 1 where sc_bytes > 60000" \ + -c ":goto 1" \ + -c ":export-session-to -" \ + ${test_dir}/logfile_access_log.0 + # log mark was not saved in session run_cap_test ${lnav_test} -n \ -c ":load-session" \