[cmds] add :export-session command

pull/824/merge
Timothy Stack 2 years ago
parent 1abc77e129
commit 6712a33163

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

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

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

@ -52,6 +52,17 @@ using free_func_t = void (*)(void*);
template<class T, free_func_t default_free = free>
class auto_mem {
public:
static void noop_free(void*) {}
static auto_mem<T> leak(T* ptr)
{
auto_mem<T> 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;

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

@ -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<std::string, lnav::console::user_message>
com_export_session_to(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
if (args.empty()) {
args.emplace_back("filename");
} else if (!ec.ec_dry_run) {
auto_mem<FILE> 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<FILE>::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<FILE>::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<std::string, lnav::console::user_message>
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,

@ -35,6 +35,7 @@
#define lnav_util_hh
#include <future>
#include <iterator>
#include <numeric>
#include <string>
#include <type_traits>
@ -47,6 +48,7 @@
#include <sys/types.h>
#include <time.h>
#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();

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

@ -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<std::string> lmss_comment;
nonstd::optional<std::string> lmss_tags;
std::string lmss_hash;
};
template<>
struct from_sqlite<log_message_session_state> {
inline log_message_session_state operator()(int argc,
sqlite3_value** argv,
int argi)
{
return {
from_sqlite<int64_t>()(argc, argv, argi + 0),
from_sqlite<std::string>()(argc, argv, argi + 1),
from_sqlite<bool>()(argc, argv, argi + 2),
from_sqlite<nonstd::optional<std::string>>()(argc, argv, argi + 3),
from_sqlite<nonstd::optional<std::string>>()(argc, argv, argi + 4),
from_sqlite<std::string>()(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<log_filter_session_state> {
inline log_filter_session_state operator()(int argc,
sqlite3_value** argv,
int argi)
{
return {
from_sqlite<std::string>()(argc, argv, argi + 0),
from_sqlite<bool>()(argc, argv, argi + 1),
from_sqlite<std::string>()(argc, argv, argi + 2),
from_sqlite<std::string>()(argc, argv, argi + 3),
from_sqlite<std::string>()(argc, argv, argi + 4),
};
}
};
namespace lnav {
namespace session {
Result<void, lnav::console::user_message>
export_to(FILE* file)
{
static auto& lnav_db
= injector::get<auto_mem<sqlite3, sqlite_close_wrapper>&,
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<log_message_session_state>().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<log_filter_session_state>().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<logfile_sub_source*>(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

@ -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<void, lnav::console::user_message> export_to(FILE* file);
}
} // namespace lnav
#endif

@ -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<std::string>().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);

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

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

@ -32,7 +32,29 @@
#ifndef lnav_sqlitepp_hh
#define lnav_sqlitepp_hh
#include <string>
#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<char>
quote(const nonstd::optional<std::string>& str)
{
auto_mem<char> retval(sqlite3_free);
if (str) {
retval = sqlite3_mprintf("%Q", str.value().c_str());
} else {
retval = sqlite3_mprintf("NULL");
}
return retval;
}
} // namespace sqlitepp
#endif

@ -981,6 +981,12 @@ filter_stack::get_enabled_mask(uint32_t& filter_in_mask,
}
}
void
filter_stack::add_filter(const std::shared_ptr<text_filter>& filter)
{
this->fs_filters.push_back(filter);
}
void
vis_location_history::loc_history_append(vis_line_t top)
{

@ -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<size_t> next_index();
void add_filter(const std::shared_ptr<text_filter>& filter)
{
this->fs_filters.push_back(filter);
}
void add_filter(const std::shared_ptr<text_filter>& filter);
void clear_filters()
{

@ -635,9 +635,10 @@ CREATE TABLE lnav_view_filters (
nonstd::optional<filter_lang_t> 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<lnav_view_filters>::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<text_filter> 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<std::string>()(1, &pattern_str, 0);
auto expr
= fmt::format(FMT_STRING("SELECT 1 WHERE {}"), clause);

@ -324,7 +324,7 @@ struct ToSqliteVisitor {
template<typename T>
void operator()(T&& t) const
{
to_sqlite(this->tsv_context, t);
to_sqlite(this->tsv_context, std::forward<T>(t));
}
sqlite3_context* tsv_context;
@ -612,11 +612,12 @@ struct vtab_module_base {
template<typename T>
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<vtab_module<T>*>(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<typename U>
void addUpdate(...){};
void addUpdate(...)
{
}
template<typename... Args>
vtab_module(Args&... args) noexcept : vm_impl(args...)

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

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

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

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

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

Loading…
Cancel
Save