mirror of
https://github.com/tstack/lnav.git
synced 2024-08-17 00:40:30 +03:00
[open] improve file previews
The file preview for :open was not fully fleshed out. Related to #1254
This commit is contained in:
parent
e71ec01ac3
commit
e382e3ad84
3
NEWS.md
3
NEWS.md
@ -5,6 +5,8 @@ Features:
|
|||||||
from @flicus.
|
from @flicus.
|
||||||
* Added a `stats.hist` PRQL transform that produces a histogram
|
* Added a `stats.hist` PRQL transform that produces a histogram
|
||||||
of values over time.
|
of values over time.
|
||||||
|
* The preview for the `:open` command will now show a listing
|
||||||
|
of archive contents.
|
||||||
|
|
||||||
Bug Fixes:
|
Bug Fixes:
|
||||||
* With the recent xz backdoor shenanigans, it seems like a good
|
* With the recent xz backdoor shenanigans, it seems like a good
|
||||||
@ -17,6 +19,7 @@ Bug Fixes:
|
|||||||
displaying JSON-lines logs.
|
displaying JSON-lines logs.
|
||||||
* A crash during initialization on Apple Silicon and MacOS 12
|
* A crash during initialization on Apple Silicon and MacOS 12
|
||||||
has been fixed.
|
has been fixed.
|
||||||
|
* A crash when previewing non-text files.
|
||||||
|
|
||||||
## lnav v0.12.1
|
## lnav v0.12.1
|
||||||
|
|
||||||
|
@ -74,10 +74,13 @@ enable_desired_archive_formats(archive* arc)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool
|
Result<describe_result, std::string>
|
||||||
is_archive(const fs::path& filename)
|
describe(const fs::path& filename)
|
||||||
{
|
{
|
||||||
#if HAVE_ARCHIVE_H
|
#if HAVE_ARCHIVE_H
|
||||||
|
static const auto RAW_FORMAT_NAME = string_fragment::from_const("raw");
|
||||||
|
static const auto GZ_FILTER_NAME = string_fragment::from_const("gzip");
|
||||||
|
|
||||||
auto_mem<archive> arc(archive_read_free);
|
auto_mem<archive> arc(archive_read_free);
|
||||||
|
|
||||||
arc = archive_read_new();
|
arc = archive_read_new();
|
||||||
@ -96,39 +99,56 @@ is_archive(const fs::path& filename)
|
|||||||
if (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
|
if (archive_read_next_header(arc, &entry) == ARCHIVE_OK) {
|
||||||
log_debug("read next done %s", filename.c_str());
|
log_debug("read next done %s", filename.c_str());
|
||||||
|
|
||||||
static const auto RAW_FORMAT_NAME = string_fragment("raw");
|
|
||||||
static const auto GZ_FILTER_NAME = string_fragment("gzip");
|
|
||||||
|
|
||||||
format_name = archive_format_name(arc);
|
format_name = archive_format_name(arc);
|
||||||
|
|
||||||
if (RAW_FORMAT_NAME == format_name) {
|
if (RAW_FORMAT_NAME == format_name) {
|
||||||
auto filter_count = archive_filter_count(arc);
|
auto filter_count = archive_filter_count(arc);
|
||||||
|
|
||||||
if (filter_count == 1) {
|
if (filter_count == 1) {
|
||||||
return false;
|
return Ok(describe_result{unknown_file{}});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* first_filter_name = archive_filter_name(arc, 0);
|
const auto* first_filter_name = archive_filter_name(arc, 0);
|
||||||
if (filter_count == 2 && GZ_FILTER_NAME == first_filter_name) {
|
if (filter_count == 2 && GZ_FILTER_NAME == first_filter_name) {
|
||||||
return false;
|
return Ok(describe_result{unknown_file{}});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log_info(
|
log_info(
|
||||||
"detected archive: %s -- %s", filename.c_str(), format_name);
|
"detected archive: %s -- %s", filename.c_str(), format_name);
|
||||||
return true;
|
auto ai = archive_info{
|
||||||
|
format_name,
|
||||||
|
};
|
||||||
|
|
||||||
|
do {
|
||||||
|
ai.ai_entries.emplace_back(archive_info::entry{
|
||||||
|
archive_entry_pathname_utf8(entry),
|
||||||
|
archive_entry_strmode(entry),
|
||||||
|
archive_entry_mtime(entry),
|
||||||
|
archive_entry_size_is_set(entry)
|
||||||
|
? nonstd::make_optional(archive_entry_size(entry))
|
||||||
|
: nonstd::nullopt,
|
||||||
|
});
|
||||||
|
} while (archive_read_next_header(arc, &entry) == ARCHIVE_OK);
|
||||||
|
|
||||||
|
return Ok(describe_result{ai});
|
||||||
}
|
}
|
||||||
|
|
||||||
log_info("archive read header failed: %s -- %s",
|
log_info("archive read header failed: %s -- %s",
|
||||||
filename.c_str(),
|
filename.c_str(),
|
||||||
archive_error_string(arc));
|
archive_error_string(arc));
|
||||||
|
return Err(fmt::format(FMT_STRING("unable to read header: {} -- {}"),
|
||||||
|
filename,
|
||||||
|
archive_error_string(arc)));
|
||||||
} else {
|
} else {
|
||||||
log_info("archive open failed: %s -- %s",
|
log_info("archive open failed: %s -- %s",
|
||||||
filename.c_str(),
|
filename.c_str(),
|
||||||
archive_error_string(arc));
|
archive_error_string(arc));
|
||||||
|
return Err(fmt::format(FMT_STRING("unable to open file: {} -- {}"),
|
||||||
|
filename,
|
||||||
|
archive_error_string(arc)));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return false;
|
return Ok(describe_result{unknown_file{}});
|
||||||
}
|
}
|
||||||
|
|
||||||
static fs::path
|
static fs::path
|
||||||
|
@ -37,8 +37,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/file_range.hh"
|
||||||
#include "base/result.h"
|
#include "base/result.h"
|
||||||
#include "ghc/filesystem.hpp"
|
#include "ghc/filesystem.hpp"
|
||||||
|
#include "mapbox/variant.hpp"
|
||||||
|
#include "optional.hpp"
|
||||||
|
|
||||||
namespace archive_manager {
|
namespace archive_manager {
|
||||||
|
|
||||||
@ -56,7 +59,22 @@ struct extract_progress {
|
|||||||
using extract_cb
|
using extract_cb
|
||||||
= std::function<extract_progress*(const ghc::filesystem::path&, ssize_t)>;
|
= std::function<extract_progress*(const ghc::filesystem::path&, ssize_t)>;
|
||||||
|
|
||||||
bool is_archive(const ghc::filesystem::path& filename);
|
struct archive_info {
|
||||||
|
struct entry {
|
||||||
|
ghc::filesystem::path e_name;
|
||||||
|
const char* e_mode;
|
||||||
|
time_t e_mtime;
|
||||||
|
nonstd::optional<file_ssize_t> e_size;
|
||||||
|
};
|
||||||
|
const char* ai_format_name;
|
||||||
|
std::vector<entry> ai_entries;
|
||||||
|
};
|
||||||
|
struct unknown_file {};
|
||||||
|
|
||||||
|
using describe_result = mapbox::util::variant<archive_info, unknown_file>;
|
||||||
|
|
||||||
|
Result<describe_result, std::string> describe(
|
||||||
|
const ghc::filesystem::path& filename);
|
||||||
|
|
||||||
ghc::filesystem::path filename_to_tmp_path(const std::string& filename);
|
ghc::filesystem::path filename_to_tmp_path(const std::string& filename);
|
||||||
|
|
||||||
|
@ -28,3 +28,57 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "attr_line.builder.hh"
|
#include "attr_line.builder.hh"
|
||||||
|
|
||||||
|
attr_line_builder&
|
||||||
|
attr_line_builder::append_as_hexdump(const string_fragment& sf)
|
||||||
|
{
|
||||||
|
auto byte_off = size_t{0};
|
||||||
|
for (auto ch : sf) {
|
||||||
|
if (byte_off == 8) {
|
||||||
|
this->append(" ");
|
||||||
|
}
|
||||||
|
nonstd::optional<role_t> ro;
|
||||||
|
if (ch == '\0') {
|
||||||
|
ro = role_t::VCR_NULL;
|
||||||
|
} else if (isspace(ch) || iscntrl(ch)) {
|
||||||
|
ro = role_t::VCR_ASCII_CTRL;
|
||||||
|
} else if (!isprint(ch)) {
|
||||||
|
ro = role_t::VCR_NON_ASCII;
|
||||||
|
}
|
||||||
|
auto ag = ro.has_value() ? this->with_attr(VC_ROLE.value(ro.value()))
|
||||||
|
: this->with_default();
|
||||||
|
this->appendf(FMT_STRING(" {:0>2x}"), ch);
|
||||||
|
byte_off += 1;
|
||||||
|
}
|
||||||
|
for (; byte_off < 16; byte_off++) {
|
||||||
|
if (byte_off == 8) {
|
||||||
|
this->append(" ");
|
||||||
|
}
|
||||||
|
this->append(" ");
|
||||||
|
}
|
||||||
|
this->append(" ");
|
||||||
|
byte_off = 0;
|
||||||
|
for (auto ch : sf) {
|
||||||
|
if (byte_off == 8) {
|
||||||
|
this->append(" ");
|
||||||
|
}
|
||||||
|
if (ch == '\0') {
|
||||||
|
auto ag = this->with_attr(VC_ROLE.value(role_t::VCR_NULL));
|
||||||
|
this->append("\u22c4");
|
||||||
|
} else if (isspace(ch)) {
|
||||||
|
auto ag = this->with_attr(VC_ROLE.value(role_t::VCR_ASCII_CTRL));
|
||||||
|
this->append("_");
|
||||||
|
} else if (iscntrl(ch)) {
|
||||||
|
auto ag = this->with_attr(VC_ROLE.value(role_t::VCR_ASCII_CTRL));
|
||||||
|
this->append("\u2022");
|
||||||
|
} else if (isprint(ch)) {
|
||||||
|
this->alb_line.get_string().push_back(ch);
|
||||||
|
} else {
|
||||||
|
auto ag = this->with_attr(VC_ROLE.value(role_t::VCR_NON_ASCII));
|
||||||
|
this->append("\u00d7");
|
||||||
|
}
|
||||||
|
byte_off += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
@ -127,6 +127,8 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attr_line_builder& append_as_hexdump(const string_fragment& sf);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
attr_line_t& alb_line;
|
attr_line_t& alb_line;
|
||||||
};
|
};
|
||||||
|
@ -41,7 +41,10 @@
|
|||||||
file_format_t
|
file_format_t
|
||||||
detect_file_format(const ghc::filesystem::path& filename)
|
detect_file_format(const ghc::filesystem::path& filename)
|
||||||
{
|
{
|
||||||
if (archive_manager::is_archive(filename)) {
|
auto describe_res = archive_manager::describe(filename);
|
||||||
|
if (describe_res.isOk()
|
||||||
|
&& describe_res.unwrap().is<archive_manager::archive_info>())
|
||||||
|
{
|
||||||
return file_format_t::ARCHIVE;
|
return file_format_t::ARCHIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@
|
|||||||
#include "base/attr_line.builder.hh"
|
#include "base/attr_line.builder.hh"
|
||||||
#include "base/auto_mem.hh"
|
#include "base/auto_mem.hh"
|
||||||
#include "base/fs_util.hh"
|
#include "base/fs_util.hh"
|
||||||
|
#include "base/humanize.hh"
|
||||||
#include "base/humanize.network.hh"
|
#include "base/humanize.network.hh"
|
||||||
#include "base/injector.hh"
|
#include "base/injector.hh"
|
||||||
#include "base/isc.hh"
|
#include "base/isc.hh"
|
||||||
@ -3234,31 +3235,32 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
|||||||
lnav_data.ld_preview_source[0].clear();
|
lnav_data.ld_preview_source[0].clear();
|
||||||
if (!fc.fc_file_names.empty()) {
|
if (!fc.fc_file_names.empty()) {
|
||||||
auto iter = fc.fc_file_names.begin();
|
auto iter = fc.fc_file_names.begin();
|
||||||
std::string fn = iter->first;
|
std::string fn_str = iter->first;
|
||||||
auto_fd preview_fd;
|
|
||||||
|
|
||||||
if (fn.find(':') != std::string::npos) {
|
if (fn_str.find(':') != std::string::npos) {
|
||||||
auto id = lnav_data.ld_preview_generation;
|
auto id = lnav_data.ld_preview_generation;
|
||||||
lnav_data.ld_preview_status_source[0]
|
lnav_data.ld_preview_status_source[0]
|
||||||
.get_description()
|
.get_description()
|
||||||
.set_cylon(true)
|
.set_cylon(true)
|
||||||
.set_value("Loading %s...", fn.c_str());
|
.set_value("Loading %s...", fn_str.c_str());
|
||||||
lnav_data.ld_preview_view[0].set_sub_source(
|
lnav_data.ld_preview_view[0].set_sub_source(
|
||||||
&lnav_data.ld_preview_source[0]);
|
&lnav_data.ld_preview_source[0]);
|
||||||
lnav_data.ld_preview_source[0].clear();
|
lnav_data.ld_preview_source[0].clear();
|
||||||
|
|
||||||
isc::to<tailer::looper&, services::remote_tailer_t>().send(
|
isc::to<tailer::looper&, services::remote_tailer_t>().send(
|
||||||
[id, fn](auto& tlooper) {
|
[id, fn_str](auto& tlooper) {
|
||||||
auto rp_opt = humanize::network::path::from_str(fn);
|
auto rp_opt = humanize::network::path::from_str(fn_str);
|
||||||
if (rp_opt) {
|
if (rp_opt) {
|
||||||
tlooper.load_preview(id, *rp_opt);
|
tlooper.load_preview(id, *rp_opt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
lnav_data.ld_preview_view[0].set_needs_update();
|
lnav_data.ld_preview_view[0].set_needs_update();
|
||||||
} else if (lnav::filesystem::is_glob(fn.c_str())) {
|
} else if (lnav::filesystem::is_glob(fn_str)) {
|
||||||
static_root_mem<glob_t, globfree> gl;
|
static_root_mem<glob_t, globfree> gl;
|
||||||
|
|
||||||
if (glob(fn.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
|
if (glob(fn_str.c_str(), GLOB_NOCHECK, nullptr, gl.inout())
|
||||||
|
== 0)
|
||||||
|
{
|
||||||
attr_line_t al;
|
attr_line_t al;
|
||||||
|
|
||||||
for (size_t lpc = 0; lpc < gl->gl_pathc && lpc < 10; lpc++)
|
for (size_t lpc = 0; lpc < gl->gl_pathc && lpc < 10; lpc++)
|
||||||
@ -3278,42 +3280,124 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
|||||||
&lnav_data.ld_preview_source[0]);
|
&lnav_data.ld_preview_source[0]);
|
||||||
lnav_data.ld_preview_source[0].replace_with(al);
|
lnav_data.ld_preview_source[0].replace_with(al);
|
||||||
} else {
|
} else {
|
||||||
return ec.make_error("failed to evaluate glob -- {}", fn);
|
return ec.make_error("failed to evaluate glob -- {}",
|
||||||
|
fn_str);
|
||||||
}
|
}
|
||||||
} else if ((preview_fd = open(fn.c_str(), O_RDONLY)) == -1) {
|
|
||||||
return ec.make_error(
|
|
||||||
"unable to open file3: {} -- {}", fn, strerror(errno));
|
|
||||||
} else {
|
} else {
|
||||||
line_buffer lb;
|
auto fn = ghc::filesystem::path(fn_str);
|
||||||
|
auto detect_res = detect_file_format(fn);
|
||||||
attr_line_t al;
|
attr_line_t al;
|
||||||
file_range range;
|
attr_line_builder alb(al);
|
||||||
std::string lines;
|
|
||||||
|
|
||||||
lb.set_fd(preview_fd);
|
switch (detect_res) {
|
||||||
for (int lpc = 0; lpc < 10; lpc++) {
|
case file_format_t::ARCHIVE: {
|
||||||
auto load_result = lb.load_next_line(range);
|
auto describe_res = archive_manager::describe(fn);
|
||||||
|
|
||||||
if (load_result.isErr()) {
|
if (describe_res.isOk()) {
|
||||||
|
auto arc_res = describe_res.unwrap();
|
||||||
|
|
||||||
|
if (arc_res.is<archive_manager::archive_info>()) {
|
||||||
|
auto ai
|
||||||
|
= arc_res
|
||||||
|
.get<archive_manager::archive_info>();
|
||||||
|
auto lines_remaining = size_t{9};
|
||||||
|
|
||||||
|
al.append("Archive: ")
|
||||||
|
.append(
|
||||||
|
lnav::roles::symbol(ai.ai_format_name))
|
||||||
|
.append("\n");
|
||||||
|
for (const auto& entry : ai.ai_entries) {
|
||||||
|
if (lines_remaining == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lines_remaining -= 1;
|
||||||
|
|
||||||
|
char timebuf[64];
|
||||||
|
sql_strftime(timebuf,
|
||||||
|
sizeof(timebuf),
|
||||||
|
entry.e_mtime,
|
||||||
|
0,
|
||||||
|
'T');
|
||||||
|
al.append(" ")
|
||||||
|
.append(entry.e_mode)
|
||||||
|
.append(" ")
|
||||||
|
.appendf(
|
||||||
|
FMT_STRING("{:>8}"),
|
||||||
|
humanize::file_size(
|
||||||
|
entry.e_size.value(),
|
||||||
|
humanize::alignment::columnar))
|
||||||
|
.append(" ")
|
||||||
|
.append(timebuf)
|
||||||
|
.append(" ")
|
||||||
|
.append(lnav::roles::file(entry.e_name))
|
||||||
|
.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
al.append(describe_res.unwrapErr());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case file_format_t::UNKNOWN: {
|
||||||
|
auto open_res
|
||||||
|
= lnav::filesystem::open_file(fn, O_RDONLY);
|
||||||
|
|
||||||
auto li = load_result.unwrap();
|
if (open_res.isErr()) {
|
||||||
|
return ec.make_error("unable to open -- {}", fn);
|
||||||
|
}
|
||||||
|
auto preview_fd = open_res.unwrap();
|
||||||
|
line_buffer lb;
|
||||||
|
file_range range;
|
||||||
|
|
||||||
range = li.li_file_range;
|
lb.set_fd(preview_fd);
|
||||||
auto read_result = lb.read_range(range);
|
for (int lpc = 0; lpc < 10; lpc++) {
|
||||||
if (read_result.isErr()) {
|
auto load_result = lb.load_next_line(range);
|
||||||
|
|
||||||
|
if (load_result.isErr()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto li = load_result.unwrap();
|
||||||
|
|
||||||
|
range = li.li_file_range;
|
||||||
|
if (!li.li_utf8_scan_result.is_valid()) {
|
||||||
|
range.fr_size = 16;
|
||||||
|
}
|
||||||
|
auto read_result = lb.read_range(range);
|
||||||
|
if (read_result.isErr()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sbr = read_result.unwrap();
|
||||||
|
auto sf = sbr.to_string_fragment();
|
||||||
|
if (li.li_utf8_scan_result.is_valid()) {
|
||||||
|
alb.append(sf);
|
||||||
|
} else {
|
||||||
|
{
|
||||||
|
auto ag = alb.with_attr(
|
||||||
|
VC_ROLE.value(role_t::VCR_FILE_OFFSET));
|
||||||
|
alb.appendf(FMT_STRING("{: >16x} "),
|
||||||
|
range.fr_offset);
|
||||||
|
}
|
||||||
|
alb.append_as_hexdump(sf);
|
||||||
|
alb.append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case file_format_t::SQLITE_DB: {
|
||||||
|
alb.append(fmt::to_string(detect_res));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case file_format_t::REMOTE: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto sbr = read_result.unwrap();
|
|
||||||
lines.append(sbr.get_data(), sbr.length());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lnav_data.ld_preview_view[0].set_sub_source(
|
lnav_data.ld_preview_view[0].set_sub_source(
|
||||||
&lnav_data.ld_preview_source[0]);
|
&lnav_data.ld_preview_source[0]);
|
||||||
lnav_data.ld_preview_source[0]
|
lnav_data.ld_preview_source[0].replace_with(al).set_text_format(
|
||||||
.replace_with(al.with_string(lines))
|
detect_text_format(al.get_string()));
|
||||||
.set_text_format(detect_text_format(al.get_string()));
|
|
||||||
lnav_data.ld_preview_status_source[0]
|
lnav_data.ld_preview_status_source[0]
|
||||||
.get_description()
|
.get_description()
|
||||||
.set_value("For file: %s", fn.c_str());
|
.set_value("For file: %s", fn.c_str());
|
||||||
@ -3719,7 +3803,8 @@ com_clear_comment(exec_context& ec,
|
|||||||
|
|
||||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||||
return ec.make_error(
|
return ec.make_error(
|
||||||
"The :clear-comment command only works in the log view");
|
"The :clear-comment command only works in the log "
|
||||||
|
"view");
|
||||||
}
|
}
|
||||||
auto& lss = lnav_data.ld_log_source;
|
auto& lss = lnav_data.ld_log_source;
|
||||||
|
|
||||||
@ -3859,7 +3944,8 @@ com_delete_tags(exec_context& ec,
|
|||||||
|
|
||||||
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
if (tc != &lnav_data.ld_views[LNV_LOG]) {
|
||||||
return ec.make_error(
|
return ec.make_error(
|
||||||
"The :delete-tag command only works in the log view");
|
"The :delete-tag command only works in the log "
|
||||||
|
"view");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto& known_tags = bookmark_metadata::KNOWN_TAGS;
|
auto& known_tags = bookmark_metadata::KNOWN_TAGS;
|
||||||
@ -4133,7 +4219,8 @@ com_summarize(exec_context& ec,
|
|||||||
query += ",";
|
query += ",";
|
||||||
}
|
}
|
||||||
query_frag = sqlite3_mprintf(
|
query_frag = sqlite3_mprintf(
|
||||||
" \"count_%s\" desc, \"c_%s\" collate naturalnocase asc",
|
" \"count_%s\" desc, \"c_%s\" collate "
|
||||||
|
"naturalnocase asc",
|
||||||
iter->c_str(),
|
iter->c_str(),
|
||||||
iter->c_str());
|
iter->c_str());
|
||||||
query += query_frag;
|
query += query_frag;
|
||||||
@ -4437,7 +4524,8 @@ com_export_session_to(exec_context& ec,
|
|||||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||||
to_term = true;
|
to_term = true;
|
||||||
fprintf(outfile,
|
fprintf(outfile,
|
||||||
"\n---------------- Press any key to exit lo-fi "
|
"\n---------------- Press any key to exit "
|
||||||
|
"lo-fi "
|
||||||
"display "
|
"display "
|
||||||
"----------------\n\n");
|
"----------------\n\n");
|
||||||
} else {
|
} else {
|
||||||
@ -4562,7 +4650,9 @@ com_toggle_field(exec_context& ec,
|
|||||||
if (hide) {
|
if (hide) {
|
||||||
if (lnav_data.ld_rl_view != nullptr) {
|
if (lnav_data.ld_rl_view != nullptr) {
|
||||||
lnav_data.ld_rl_view->set_alt_value(
|
lnav_data.ld_rl_view->set_alt_value(
|
||||||
HELP_MSG_1(x, "to quickly show hidden fields"));
|
HELP_MSG_1(x,
|
||||||
|
"to quickly show hidden "
|
||||||
|
"fields"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tc->set_needs_update();
|
tc->set_needs_update();
|
||||||
@ -4613,10 +4703,11 @@ com_hide_line(exec_context& ec,
|
|||||||
max_time_str, sizeof(max_time_str), max_time_opt.value());
|
max_time_str, sizeof(max_time_str), max_time_opt.value());
|
||||||
}
|
}
|
||||||
if (min_time_opt && max_time_opt) {
|
if (min_time_opt && max_time_opt) {
|
||||||
retval = fmt::format(
|
retval
|
||||||
FMT_STRING("info: hiding lines before {} and after {}"),
|
= fmt::format(FMT_STRING("info: hiding lines before {} and "
|
||||||
min_time_str,
|
"after {}"),
|
||||||
max_time_str);
|
min_time_str,
|
||||||
|
max_time_str);
|
||||||
} else if (min_time_opt) {
|
} else if (min_time_opt) {
|
||||||
retval = fmt::format(FMT_STRING("info: hiding lines before {}"),
|
retval = fmt::format(FMT_STRING("info: hiding lines before {}"),
|
||||||
min_time_str);
|
min_time_str);
|
||||||
@ -4625,7 +4716,8 @@ com_hide_line(exec_context& ec,
|
|||||||
max_time_str);
|
max_time_str);
|
||||||
} else {
|
} else {
|
||||||
retval
|
retval
|
||||||
= "info: no lines hidden by time, pass an absolute or "
|
= "info: no lines hidden by time, pass an "
|
||||||
|
"absolute or "
|
||||||
"relative time";
|
"relative time";
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -4656,7 +4748,8 @@ com_hide_line(exec_context& ec,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return ec.make_error(
|
return ec.make_error(
|
||||||
"relative time values only work in a time-based view");
|
"relative time values only work in a "
|
||||||
|
"time-based view");
|
||||||
}
|
}
|
||||||
} else if (dts.convert_to_timeval(all_args, tv_abs)) {
|
} else if (dts.convert_to_timeval(all_args, tv_abs)) {
|
||||||
tv_opt = tv_abs;
|
tv_opt = tv_abs;
|
||||||
@ -5569,8 +5662,9 @@ command_prompt(std::vector<std::string>& args)
|
|||||||
|
|
||||||
rollback_lnav_config = lnav_config;
|
rollback_lnav_config = lnav_config;
|
||||||
lnav_data.ld_doc_status_source.set_title("Command Help");
|
lnav_data.ld_doc_status_source.set_title("Command Help");
|
||||||
lnav_data.ld_doc_status_source.set_description(" See " ANSI_BOLD(
|
lnav_data.ld_doc_status_source.set_description(
|
||||||
"https://docs.lnav.org/en/latest/commands.html") " for more details");
|
" See " ANSI_BOLD("https://docs.lnav.org/en/latest/"
|
||||||
|
"commands.html") " for more details");
|
||||||
add_view_text_possibilities(lnav_data.ld_rl_view,
|
add_view_text_possibilities(lnav_data.ld_rl_view,
|
||||||
ln_mode_t::COMMAND,
|
ln_mode_t::COMMAND,
|
||||||
"filter",
|
"filter",
|
||||||
@ -5723,8 +5817,9 @@ sql_prompt(std::vector<std::string>& args)
|
|||||||
cget(args, 3).value_or(""));
|
cget(args, 3).value_or(""));
|
||||||
|
|
||||||
lnav_data.ld_doc_status_source.set_title("Query Help");
|
lnav_data.ld_doc_status_source.set_title("Query Help");
|
||||||
lnav_data.ld_doc_status_source.set_description("See " ANSI_BOLD(
|
lnav_data.ld_doc_status_source.set_description(
|
||||||
"https://docs.lnav.org/en/latest/sqlext.html") " for more details");
|
"See " ANSI_BOLD("https://docs.lnav.org/en/latest/"
|
||||||
|
"sqlext.html") " for more details");
|
||||||
rl_set_help();
|
rl_set_help();
|
||||||
lnav_data.ld_bottom_source.update_loading(0, 0);
|
lnav_data.ld_bottom_source.update_loading(0, 0);
|
||||||
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
lnav_data.ld_status[LNS_BOTTOM].do_update();
|
||||||
@ -5819,14 +5914,13 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":prompt")
|
help_text(":prompt")
|
||||||
.with_summary("Open the given prompt")
|
.with_summary("Open the given prompt")
|
||||||
.with_parameter(
|
.with_parameter({"type",
|
||||||
{"type",
|
"The type of prompt -- command, script, "
|
||||||
"The type of prompt -- command, script, search, sql, user"})
|
"search, sql, user"})
|
||||||
.with_parameter(
|
.with_parameter(help_text("--alt",
|
||||||
help_text(
|
"Perform the alternate action "
|
||||||
"--alt",
|
"for this prompt by default")
|
||||||
"Perform the alternate action for this prompt by default")
|
.optional())
|
||||||
.optional())
|
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("prompt", "The prompt to display").optional())
|
help_text("prompt", "The prompt to display").optional())
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
@ -5834,7 +5928,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"The initial value to fill in for the prompt")
|
"The initial value to fill in for the prompt")
|
||||||
.optional())
|
.optional())
|
||||||
.with_example({
|
.with_example({
|
||||||
"To open the command prompt with 'filter-in' already filled "
|
"To open the command prompt with 'filter-in' already "
|
||||||
|
"filled "
|
||||||
"in",
|
"in",
|
||||||
"command : 'filter-in '",
|
"command : 'filter-in '",
|
||||||
})
|
})
|
||||||
@ -5869,7 +5964,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"convert-time-to",
|
"convert-time-to",
|
||||||
com_convert_time_to,
|
com_convert_time_to,
|
||||||
help_text(":convert-time-to")
|
help_text(":convert-time-to")
|
||||||
.with_summary("Convert the focused timestamp to the given timezone")
|
.with_summary("Convert the focused timestamp to the "
|
||||||
|
"given timezone")
|
||||||
.with_parameter(help_text("zone", "The timezone name")),
|
.with_parameter(help_text("zone", "The timezone name")),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -5877,7 +5973,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_set_file_timezone,
|
com_set_file_timezone,
|
||||||
help_text(":set-file-timezone")
|
help_text(":set-file-timezone")
|
||||||
.with_summary("Set the timezone to use for log messages that do "
|
.with_summary("Set the timezone to use for log messages that do "
|
||||||
"not include a timezone. The timezone is applied to "
|
"not include a timezone. The timezone is applied "
|
||||||
|
"to "
|
||||||
"the focused file or the given glob pattern.")
|
"the focused file or the given glob pattern.")
|
||||||
.with_parameter({"zone", "The timezone name"})
|
.with_parameter({"zone", "The timezone name"})
|
||||||
.with_parameter(help_text{"pattern",
|
.with_parameter(help_text{"pattern",
|
||||||
@ -5891,12 +5988,13 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"clear-file-timezone",
|
"clear-file-timezone",
|
||||||
com_clear_file_timezone,
|
com_clear_file_timezone,
|
||||||
help_text(":clear-file-timezone")
|
help_text(":clear-file-timezone")
|
||||||
.with_summary("Clear the timezone setting for the focused file or "
|
.with_summary("Clear the timezone setting for the "
|
||||||
|
"focused file or "
|
||||||
"the given glob pattern.")
|
"the given glob pattern.")
|
||||||
.with_parameter(
|
.with_parameter(help_text{"pattern",
|
||||||
help_text{"pattern",
|
"The glob pattern to match against files "
|
||||||
"The glob pattern to match against files that should "
|
"that should "
|
||||||
"no longer use this timezone"})
|
"no longer use this timezone"})
|
||||||
.with_tags({"file-options"}),
|
.with_tags({"file-options"}),
|
||||||
com_clear_file_timezone_prompt,
|
com_clear_file_timezone_prompt,
|
||||||
},
|
},
|
||||||
@ -5918,7 +6016,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
.with_examples(
|
.with_examples(
|
||||||
{{"To go to line 22", "22"},
|
{{"To go to line 22", "22"},
|
||||||
{"To go to the line 75% of the way into the view", "75%"},
|
{"To go to the line 75% of the way into the view", "75%"},
|
||||||
{"To go to the first message on the first day of 2017",
|
{"To go to the first message on the first day of "
|
||||||
|
"2017",
|
||||||
"2017-01-01"},
|
"2017-01-01"},
|
||||||
{"To go to the Screenshots section", "#screenshots"}})
|
{"To go to the Screenshots section", "#screenshots"}})
|
||||||
.with_tags({"navigation"})},
|
.with_tags({"navigation"})},
|
||||||
@ -5939,8 +6038,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_annotate,
|
com_annotate,
|
||||||
|
|
||||||
help_text(":annotate")
|
help_text(":annotate")
|
||||||
.with_summary(
|
.with_summary("Analyze the focused log message and "
|
||||||
"Analyze the focused log message and attach annotations")
|
"attach annotations")
|
||||||
.with_tags({"metadata"}),
|
.with_tags({"metadata"}),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -5957,16 +6056,18 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":mark-expr")
|
help_text(":mark-expr")
|
||||||
.with_summary("Set the bookmark expression")
|
.with_summary("Set the bookmark expression")
|
||||||
.with_parameter(help_text(
|
.with_parameter(help_text("expr",
|
||||||
"expr",
|
"The SQL expression to evaluate for each "
|
||||||
"The SQL expression to evaluate for each log message. "
|
"log message. "
|
||||||
"The message values can be accessed using column names "
|
"The message values can be accessed "
|
||||||
"prefixed with a colon"))
|
"using column names "
|
||||||
|
"prefixed with a colon"))
|
||||||
.with_opposites({"clear-mark-expr"})
|
.with_opposites({"clear-mark-expr"})
|
||||||
.with_tags({"bookmarks"})
|
.with_tags({"bookmarks"})
|
||||||
.with_example(
|
.with_example({"To mark lines from 'dhclient' that "
|
||||||
{"To mark lines from 'dhclient' that mention 'eth0'",
|
"mention 'eth0'",
|
||||||
":log_procname = 'dhclient' AND :log_body LIKE '%eth0%'"}),
|
":log_procname = 'dhclient' AND "
|
||||||
|
":log_body LIKE '%eth0%'"}),
|
||||||
|
|
||||||
com_mark_expr_prompt,
|
com_mark_expr_prompt,
|
||||||
},
|
},
|
||||||
@ -5993,7 +6094,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_goto_mark,
|
com_goto_mark,
|
||||||
|
|
||||||
help_text(":prev-mark")
|
help_text(":prev-mark")
|
||||||
.with_summary("Move to the previous bookmark of the given type in the "
|
.with_summary("Move to the previous bookmark of the given "
|
||||||
|
"type in the "
|
||||||
"current view")
|
"current view")
|
||||||
.with_parameter(help_text("type",
|
.with_parameter(help_text("type",
|
||||||
"The type of bookmark -- error, warning, "
|
"The type of bookmark -- error, warning, "
|
||||||
@ -6011,7 +6113,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_goto_location,
|
com_goto_location,
|
||||||
|
|
||||||
help_text(":prev-location")
|
help_text(":prev-location")
|
||||||
.with_summary("Move to the previous position in the location history")
|
.with_summary("Move to the previous position in the "
|
||||||
|
"location history")
|
||||||
.with_tags({"navigation"})},
|
.with_tags({"navigation"})},
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -6039,21 +6142,23 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_toggle_field,
|
com_toggle_field,
|
||||||
|
|
||||||
help_text(":hide-fields")
|
help_text(":hide-fields")
|
||||||
.with_summary(
|
.with_summary("Hide log message fields by replacing them "
|
||||||
"Hide log message fields by replacing them with an ellipsis")
|
"with an ellipsis")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("field-name",
|
help_text("field-name",
|
||||||
"The name of the field to hide in the format for the "
|
"The name of the field to hide in the format for "
|
||||||
|
"the "
|
||||||
"top log line. "
|
"top log line. "
|
||||||
"A qualified name can be used where the field name is "
|
"A qualified name can be used where the field "
|
||||||
|
"name is "
|
||||||
"prefixed "
|
"prefixed "
|
||||||
"by the format name and a dot to hide any field.")
|
"by the format name and a dot to hide any field.")
|
||||||
.one_or_more())
|
.one_or_more())
|
||||||
.with_example(
|
.with_example(
|
||||||
{"To hide the log_procname fields in all formats", "log_procname"})
|
{"To hide the log_procname fields in all formats", "log_procname"})
|
||||||
.with_example(
|
.with_example({"To hide only the log_procname field in "
|
||||||
{"To hide only the log_procname field in the syslog format",
|
"the syslog format",
|
||||||
"syslog_log.log_procname"})
|
"syslog_log.log_procname"})
|
||||||
.with_tags({"display"})},
|
.with_tags({"display"})},
|
||||||
{"show-fields",
|
{"show-fields",
|
||||||
com_toggle_field,
|
com_toggle_field,
|
||||||
@ -6093,8 +6198,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_show_lines,
|
com_show_lines,
|
||||||
|
|
||||||
help_text(":show-lines-before-and-after")
|
help_text(":show-lines-before-and-after")
|
||||||
.with_summary(
|
.with_summary("Show lines that were hidden by the "
|
||||||
"Show lines that were hidden by the 'hide-lines' commands")
|
"'hide-lines' commands")
|
||||||
.with_opposites({"hide-lines-before", "hide-lines-after"})
|
.with_opposites({"hide-lines-before", "hide-lines-after"})
|
||||||
.with_tags({"filtering"})},
|
.with_tags({"filtering"})},
|
||||||
{"hide-unmarked-lines",
|
{"hide-unmarked-lines",
|
||||||
@ -6114,7 +6219,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_highlight,
|
com_highlight,
|
||||||
|
|
||||||
help_text(":highlight")
|
help_text(":highlight")
|
||||||
.with_summary("Add coloring to log messages fragments that match the "
|
.with_summary("Add coloring to log messages fragments "
|
||||||
|
"that match the "
|
||||||
"given regular expression")
|
"given regular expression")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("pattern", "The regular expression to match"))
|
help_text("pattern", "The regular expression to match"))
|
||||||
@ -6126,9 +6232,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":clear-highlight")
|
help_text(":clear-highlight")
|
||||||
.with_summary("Remove a previously set highlight regular expression")
|
.with_summary("Remove a previously set highlight regular expression")
|
||||||
.with_parameter(help_text(
|
.with_parameter(help_text("pattern",
|
||||||
"pattern",
|
"The regular expression previously used "
|
||||||
"The regular expression previously used with :highlight"))
|
"with :highlight"))
|
||||||
.with_tags({"display"})
|
.with_tags({"display"})
|
||||||
.with_opposites({"highlight"})
|
.with_opposites({"highlight"})
|
||||||
.with_example(
|
.with_example(
|
||||||
@ -6153,13 +6259,14 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_filter,
|
com_filter,
|
||||||
|
|
||||||
help_text(":filter-out")
|
help_text(":filter-out")
|
||||||
.with_summary(
|
.with_summary("Remove lines that match the given "
|
||||||
"Remove lines that match the given regular expression "
|
"regular expression "
|
||||||
"in the current view")
|
"in the current view")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("pattern", "The regular expression to match"))
|
help_text("pattern", "The regular expression to match"))
|
||||||
.with_tags({"filtering"})
|
.with_tags({"filtering"})
|
||||||
.with_example({"To filter out log messages that contain the string "
|
.with_example({"To filter out log messages that "
|
||||||
|
"contain the string "
|
||||||
"'last message repeated'",
|
"'last message repeated'",
|
||||||
"last message repeated"}),
|
"last message repeated"}),
|
||||||
com_filter_prompt,
|
com_filter_prompt,
|
||||||
@ -6183,22 +6290,25 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":filter-expr")
|
help_text(":filter-expr")
|
||||||
.with_summary("Set the filter expression")
|
.with_summary("Set the filter expression")
|
||||||
.with_parameter(help_text(
|
.with_parameter(help_text("expr",
|
||||||
"expr",
|
"The SQL expression to evaluate for each "
|
||||||
"The SQL expression to evaluate for each log message. "
|
"log message. "
|
||||||
"The message values can be accessed using column names "
|
"The message values can be accessed "
|
||||||
"prefixed with a colon"))
|
"using column names "
|
||||||
|
"prefixed with a colon"))
|
||||||
.with_opposites({"clear-filter-expr"})
|
.with_opposites({"clear-filter-expr"})
|
||||||
.with_tags({"filtering"})
|
.with_tags({"filtering"})
|
||||||
.with_example({"To set a filter expression that matched syslog "
|
.with_example({"To set a filter expression that matched syslog "
|
||||||
"messages from 'syslogd'",
|
"messages from 'syslogd'",
|
||||||
":log_procname = 'syslogd'"})
|
":log_procname = 'syslogd'"})
|
||||||
.with_example(
|
.with_example({"To set a filter expression that "
|
||||||
{"To set a filter expression that matches log messages "
|
"matches log messages "
|
||||||
"where "
|
"where "
|
||||||
"'id' is followed by a number and contains the string "
|
"'id' is followed by a number and "
|
||||||
"'foo'",
|
"contains the string "
|
||||||
":log_body REGEXP 'id\\d+' AND :log_body REGEXP 'foo'"}),
|
"'foo'",
|
||||||
|
":log_body REGEXP 'id\\d+' AND "
|
||||||
|
":log_body REGEXP 'foo'"}),
|
||||||
|
|
||||||
com_filter_expr_prompt,
|
com_filter_expr_prompt,
|
||||||
},
|
},
|
||||||
@ -6213,8 +6323,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
help_text(":append-to")
|
help_text(":append-to")
|
||||||
.with_summary(
|
.with_summary("Append marked lines in the current view to "
|
||||||
"Append marked lines in the current view to the given file")
|
"the given file")
|
||||||
.with_parameter(help_text("path", "The path to the file to append to"))
|
.with_parameter(help_text("path", "The path to the file to append to"))
|
||||||
.with_tags({"io"})
|
.with_tags({"io"})
|
||||||
.with_example({"To append marked lines to the file "
|
.with_example({"To append marked lines to the file "
|
||||||
@ -6224,7 +6334,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
help_text(":write-to")
|
help_text(":write-to")
|
||||||
.with_summary("Overwrite the given file with any marked lines in the "
|
.with_summary("Overwrite the given file with any marked "
|
||||||
|
"lines in the "
|
||||||
"current view")
|
"current view")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("--anonymize", "Anonymize the lines").optional())
|
help_text("--anonymize", "Anonymize the lines").optional())
|
||||||
@ -6259,20 +6370,21 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
help_text(":write-jsonlines-to")
|
help_text(":write-jsonlines-to")
|
||||||
.with_summary(
|
.with_summary("Write SQL results to the given file in "
|
||||||
"Write SQL results to the given file in JSON Lines format")
|
"JSON Lines format")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("--anonymize", "Anonymize the JSON values").optional())
|
help_text("--anonymize", "Anonymize the JSON values").optional())
|
||||||
.with_parameter(help_text("path", "The path to the file to write"))
|
.with_parameter(help_text("path", "The path to the file to write"))
|
||||||
.with_tags({"io", "scripting", "sql"})
|
.with_tags({"io", "scripting", "sql"})
|
||||||
.with_example({"To write SQL results as JSON Lines to /tmp/table.json",
|
.with_example({"To write SQL results as JSON Lines to "
|
||||||
|
"/tmp/table.json",
|
||||||
"/tmp/table.json"})},
|
"/tmp/table.json"})},
|
||||||
{"write-table-to",
|
{"write-table-to",
|
||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
help_text(":write-table-to")
|
help_text(":write-table-to")
|
||||||
.with_summary(
|
.with_summary("Write SQL results to the given file in a "
|
||||||
"Write SQL results to the given file in a tabular format")
|
"tabular format")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("--anonymize", "Anonymize the table contents")
|
help_text("--anonymize", "Anonymize the table contents")
|
||||||
.optional())
|
.optional())
|
||||||
@ -6284,10 +6396,10 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
help_text(":write-raw-to")
|
help_text(":write-raw-to")
|
||||||
.with_summary(
|
.with_summary("In the log view, write the original log file content "
|
||||||
"In the log view, write the original log file content "
|
"of the marked messages to the file. In the DB view, "
|
||||||
"of the marked messages to the file. In the DB view, "
|
"the contents of the cells are written to the output "
|
||||||
"the contents of the cells are written to the output file.")
|
"file.")
|
||||||
.with_parameter(help_text("--view={log,db}",
|
.with_parameter(help_text("--view={log,db}",
|
||||||
"The view to use as the source of data")
|
"The view to use as the source of data")
|
||||||
.optional())
|
.optional())
|
||||||
@ -6295,9 +6407,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text("--anonymize", "Anonymize the lines").optional())
|
help_text("--anonymize", "Anonymize the lines").optional())
|
||||||
.with_parameter(help_text("path", "The path to the file to write"))
|
.with_parameter(help_text("path", "The path to the file to write"))
|
||||||
.with_tags({"io", "scripting", "sql"})
|
.with_tags({"io", "scripting", "sql"})
|
||||||
.with_example(
|
.with_example({"To write the marked lines in the log view "
|
||||||
{"To write the marked lines in the log view to /tmp/table.txt",
|
"to /tmp/table.txt",
|
||||||
"/tmp/table.txt"})},
|
"/tmp/table.txt"})},
|
||||||
{"write-view-to",
|
{"write-view-to",
|
||||||
com_save_to,
|
com_save_to,
|
||||||
|
|
||||||
@ -6336,9 +6448,10 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_pipe_to,
|
com_pipe_to,
|
||||||
|
|
||||||
help_text(":pipe-line-to")
|
help_text(":pipe-line-to")
|
||||||
.with_summary(
|
.with_summary("Pipe the focused line to the given shell "
|
||||||
"Pipe the focused line to the given shell command. Any fields "
|
"command. Any fields "
|
||||||
"defined by the format will be set as environment variables.")
|
"defined by the format will be set as "
|
||||||
|
"environment variables.")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("shell-cmd", "The shell command-line to execute"))
|
help_text("shell-cmd", "The shell command-line to execute"))
|
||||||
.with_tags({"io"})
|
.with_tags({"io"})
|
||||||
@ -6350,12 +6463,11 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text(":redirect-to")
|
help_text(":redirect-to")
|
||||||
.with_summary("Redirect the output of commands that write to "
|
.with_summary("Redirect the output of commands that write to "
|
||||||
"stdout to the given file")
|
"stdout to the given file")
|
||||||
.with_parameter(
|
.with_parameter(help_text("path",
|
||||||
help_text(
|
"The path to the file to write."
|
||||||
"path",
|
" If not specified, the current redirect "
|
||||||
"The path to the file to write."
|
"will be cleared")
|
||||||
" If not specified, the current redirect will be cleared")
|
.optional())
|
||||||
.optional())
|
|
||||||
.with_tags({"io", "scripting"})
|
.with_tags({"io", "scripting"})
|
||||||
.with_example({"To write the output of lnav commands to the file "
|
.with_example({"To write the output of lnav commands to the file "
|
||||||
"/tmp/script-output.txt",
|
"/tmp/script-output.txt",
|
||||||
@ -6369,7 +6481,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"pattern", "The regular expression used in the filter command"))
|
"pattern", "The regular expression used in the filter command"))
|
||||||
.with_tags({"filtering"})
|
.with_tags({"filtering"})
|
||||||
.with_opposites({"disable-filter"})
|
.with_opposites({"disable-filter"})
|
||||||
.with_example({"To enable the disabled filter with the pattern 'last "
|
.with_example({"To enable the disabled filter with the "
|
||||||
|
"pattern 'last "
|
||||||
"message repeated'",
|
"message repeated'",
|
||||||
"last message repeated"})},
|
"last message repeated"})},
|
||||||
{"disable-filter",
|
{"disable-filter",
|
||||||
@ -6401,13 +6514,14 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_create_logline_table,
|
com_create_logline_table,
|
||||||
|
|
||||||
help_text(":create-logline-table")
|
help_text(":create-logline-table")
|
||||||
.with_summary("Create an SQL table using the top line of the log view "
|
.with_summary("Create an SQL table using the top line of "
|
||||||
|
"the log view "
|
||||||
"as a template")
|
"as a template")
|
||||||
.with_parameter(help_text("table-name", "The name for the new table"))
|
.with_parameter(help_text("table-name", "The name for the new table"))
|
||||||
.with_tags({"vtables", "sql"})
|
.with_tags({"vtables", "sql"})
|
||||||
.with_example(
|
.with_example({"To create a logline-style table named "
|
||||||
{"To create a logline-style table named 'task_durations'",
|
"'task_durations'",
|
||||||
"task_durations"})},
|
"task_durations"})},
|
||||||
{"delete-logline-table",
|
{"delete-logline-table",
|
||||||
com_delete_logline_table,
|
com_delete_logline_table,
|
||||||
|
|
||||||
@ -6417,9 +6531,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text("table-name", "The name of the table to delete"))
|
help_text("table-name", "The name of the table to delete"))
|
||||||
.with_opposites({"delete-logline-table"})
|
.with_opposites({"delete-logline-table"})
|
||||||
.with_tags({"vtables", "sql"})
|
.with_tags({"vtables", "sql"})
|
||||||
.with_example(
|
.with_example({"To delete the logline-style table named "
|
||||||
{"To delete the logline-style table named 'task_durations'",
|
"'task_durations'",
|
||||||
"task_durations"})},
|
"task_durations"})},
|
||||||
{"create-search-table",
|
{"create-search-table",
|
||||||
com_create_search_table,
|
com_create_search_table,
|
||||||
|
|
||||||
@ -6431,13 +6545,15 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text("pattern",
|
help_text("pattern",
|
||||||
"The regular expression used to capture the table "
|
"The regular expression used to capture the table "
|
||||||
"columns. "
|
"columns. "
|
||||||
"If not given, the current search pattern is used.")
|
"If not given, the current search pattern is "
|
||||||
|
"used.")
|
||||||
.optional())
|
.optional())
|
||||||
.with_tags({"vtables", "sql"})
|
.with_tags({"vtables", "sql"})
|
||||||
.with_example(
|
.with_example({"To create a table named 'task_durations' that "
|
||||||
{"To create a table named 'task_durations' that matches log "
|
"matches log "
|
||||||
"messages with the pattern 'duration=(?<duration>\\d+)'",
|
"messages with the pattern "
|
||||||
R"(task_durations duration=(?<duration>\d+))"})},
|
"'duration=(?<duration>\\d+)'",
|
||||||
|
R"(task_durations duration=(?<duration>\d+))"})},
|
||||||
{"delete-search-table",
|
{"delete-search-table",
|
||||||
com_delete_search_table,
|
com_delete_search_table,
|
||||||
|
|
||||||
@ -6453,10 +6569,10 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_open,
|
com_open,
|
||||||
|
|
||||||
help_text(":open")
|
help_text(":open")
|
||||||
.with_summary(
|
.with_summary("Open the given file(s) in lnav. Opening files on "
|
||||||
"Open the given file(s) in lnav. Opening files on machines "
|
"machines "
|
||||||
"accessible via SSH can be done using the syntax: "
|
"accessible via SSH can be done using the syntax: "
|
||||||
"[user@]host:/path/to/logs")
|
"[user@]host:/path/to/logs")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text{"path", "The path to the file to open"}.one_or_more())
|
help_text{"path", "The path to the file to open"}.one_or_more())
|
||||||
.with_example({"To open the file '/path/to/file'", "/path/to/file"})
|
.with_example({"To open the file '/path/to/file'", "/path/to/file"})
|
||||||
@ -6469,8 +6585,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
.with_summary("Hide the given file(s) and skip indexing until it "
|
.with_summary("Hide the given file(s) and skip indexing until it "
|
||||||
"is shown again. If no path is given, the current "
|
"is shown again. If no path is given, the current "
|
||||||
"file in the view is hidden")
|
"file in the view is hidden")
|
||||||
.with_parameter(help_text{
|
.with_parameter(help_text{"path",
|
||||||
"path", "A path or glob pattern that specifies the files to hide"}
|
"A path or glob pattern that "
|
||||||
|
"specifies the files to hide"}
|
||||||
.zero_or_more())
|
.zero_or_more())
|
||||||
.with_opposites({"show-file"})},
|
.with_opposites({"show-file"})},
|
||||||
{"show-file",
|
{"show-file",
|
||||||
@ -6478,9 +6595,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":show-file")
|
help_text(":show-file")
|
||||||
.with_summary("Show the given file(s) and resume indexing.")
|
.with_summary("Show the given file(s) and resume indexing.")
|
||||||
.with_parameter(help_text{
|
.with_parameter(help_text{"path",
|
||||||
"path",
|
"The path or glob pattern that "
|
||||||
"The path or glob pattern that specifies the files to show"}
|
"specifies the files to show"}
|
||||||
.zero_or_more())
|
.zero_or_more())
|
||||||
.with_opposites({"hide-file"})},
|
.with_opposites({"hide-file"})},
|
||||||
{"show-only-this-file",
|
{"show-only-this-file",
|
||||||
@ -6494,8 +6611,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
|
|
||||||
help_text(":close")
|
help_text(":close")
|
||||||
.with_summary("Close the given file(s) or the top file in the view")
|
.with_summary("Close the given file(s) or the top file in the view")
|
||||||
.with_parameter(help_text{
|
.with_parameter(help_text{"path",
|
||||||
"path", "A path or glob pattern that specifies the files to close"}
|
"A path or glob pattern that "
|
||||||
|
"specifies the files to close"}
|
||||||
.zero_or_more())
|
.zero_or_more())
|
||||||
.with_opposites({"open"})},
|
.with_opposites({"open"})},
|
||||||
{
|
{
|
||||||
@ -6544,7 +6662,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text(":untag")
|
help_text(":untag")
|
||||||
.with_summary("Detach tags from the top log line")
|
.with_summary("Detach tags from the top log line")
|
||||||
.with_parameter(help_text("tag", "The tags to detach").one_or_more())
|
.with_parameter(help_text("tag", "The tags to detach").one_or_more())
|
||||||
.with_example({"To remove the tags '#BUG123' and '#needs-review' from "
|
.with_example({"To remove the tags '#BUG123' and "
|
||||||
|
"'#needs-review' from "
|
||||||
"the top line",
|
"the top line",
|
||||||
"#BUG123 #needs-review"})
|
"#BUG123 #needs-review"})
|
||||||
.with_opposites({"tag"})
|
.with_opposites({"tag"})
|
||||||
@ -6555,7 +6674,8 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
help_text(":delete-tags")
|
help_text(":delete-tags")
|
||||||
.with_summary("Remove the given tags from all log lines")
|
.with_summary("Remove the given tags from all log lines")
|
||||||
.with_parameter(help_text("tag", "The tags to delete").one_or_more())
|
.with_parameter(help_text("tag", "The tags to delete").one_or_more())
|
||||||
.with_example({"To remove the tags '#BUG123' and '#needs-review' from "
|
.with_example({"To remove the tags '#BUG123' and "
|
||||||
|
"'#needs-review' from "
|
||||||
"all log lines",
|
"all log lines",
|
||||||
"#BUG123 #needs-review"})
|
"#BUG123 #needs-review"})
|
||||||
.with_opposites({"tag"})
|
.with_opposites({"tag"})
|
||||||
@ -6580,17 +6700,18 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_session,
|
com_session,
|
||||||
|
|
||||||
help_text(":session")
|
help_text(":session")
|
||||||
.with_summary(
|
.with_summary("Add the given command to the session file "
|
||||||
"Add the given command to the session file (~/.lnav/session)")
|
"(~/.lnav/session)")
|
||||||
.with_parameter(help_text("lnav-command", "The lnav command to save."))
|
.with_parameter(help_text("lnav-command", "The lnav command to save."))
|
||||||
.with_example(
|
.with_example({"To add the command ':highlight foobar' to "
|
||||||
{"To add the command ':highlight foobar' to the session file",
|
"the session file",
|
||||||
":highlight foobar"})},
|
":highlight foobar"})},
|
||||||
{"summarize",
|
{"summarize",
|
||||||
com_summarize,
|
com_summarize,
|
||||||
|
|
||||||
help_text(":summarize")
|
help_text(":summarize")
|
||||||
.with_summary("Execute a SQL query that computes the characteristics "
|
.with_summary("Execute a SQL query that computes the "
|
||||||
|
"characteristics "
|
||||||
"of the values in the given column")
|
"of the values in the given column")
|
||||||
.with_parameter(
|
.with_parameter(
|
||||||
help_text("column-name", "The name of the column to analyze."))
|
help_text("column-name", "The name of the column to analyze."))
|
||||||
@ -6609,12 +6730,13 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_switch_to_view,
|
com_switch_to_view,
|
||||||
|
|
||||||
help_text(":toggle-view")
|
help_text(":toggle-view")
|
||||||
.with_summary(
|
.with_summary("Switch to the given view or, if it is "
|
||||||
"Switch to the given view or, if it is already displayed, "
|
"already displayed, "
|
||||||
"switch to the previous view")
|
"switch to the previous view")
|
||||||
.with_parameter(help_text(
|
.with_parameter(help_text(
|
||||||
"view-name", "The name of the view to toggle the display of."))
|
"view-name", "The name of the view to toggle the display of."))
|
||||||
.with_example({"To switch to the 'schema' view if it is not displayed "
|
.with_example({"To switch to the 'schema' view if it is "
|
||||||
|
"not displayed "
|
||||||
"or switch back to the previous view",
|
"or switch back to the previous view",
|
||||||
"schema"})},
|
"schema"})},
|
||||||
{"toggle-filtering",
|
{"toggle-filtering",
|
||||||
@ -6675,15 +6797,17 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
com_echo,
|
com_echo,
|
||||||
|
|
||||||
help_text(":echo")
|
help_text(":echo")
|
||||||
.with_summary(
|
.with_summary("Echo the given message to the screen or, if "
|
||||||
"Echo the given message to the screen or, if :redirect-to has "
|
":redirect-to has "
|
||||||
"been called, to output file specified in the redirect. "
|
"been called, to output file specified in the "
|
||||||
"Variable substitution is performed on the message. Use a "
|
"redirect. "
|
||||||
"backslash to escape any special characters, like '$'")
|
"Variable substitution is performed on the message. "
|
||||||
.with_parameter(
|
"Use a "
|
||||||
help_text("-n",
|
"backslash to escape any special characters, like '$'")
|
||||||
"Do not print a line-feed at the end of the output")
|
.with_parameter(help_text("-n",
|
||||||
.optional())
|
"Do not print a line-feed at "
|
||||||
|
"the end of the output")
|
||||||
|
.optional())
|
||||||
.with_parameter(help_text("msg", "The message to display"))
|
.with_parameter(help_text("msg", "The message to display"))
|
||||||
.with_tags({"io", "scripting"})
|
.with_tags({"io", "scripting"})
|
||||||
.with_example({"To output 'Hello, World!'", "Hello, World!"})},
|
.with_example({"To output 'Hello, World!'", "Hello, World!"})},
|
||||||
@ -6743,9 +6867,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"The value to write. If not given, the "
|
"The value to write. If not given, the "
|
||||||
"current value is returned")
|
"current value is returned")
|
||||||
.optional())
|
.optional())
|
||||||
.with_example(
|
.with_example({"To read the configuration of the "
|
||||||
{"To read the configuration of the '/ui/clock-format' option",
|
"'/ui/clock-format' option",
|
||||||
"/ui/clock-format"})
|
"/ui/clock-format"})
|
||||||
.with_example({"To set the '/ui/dim-text' option to 'false'",
|
.with_example({"To set the '/ui/dim-text' option to 'false'",
|
||||||
"/ui/dim-text false"})
|
"/ui/dim-text false"})
|
||||||
.with_tags({"configuration"})},
|
.with_tags({"configuration"})},
|
||||||
@ -6767,9 +6891,9 @@ readline_context::command_t STD_COMMANDS[] = {
|
|||||||
"using a spectrogram")
|
"using a spectrogram")
|
||||||
.with_parameter(help_text(
|
.with_parameter(help_text(
|
||||||
"field-name", "The name of the numeric field to visualize."))
|
"field-name", "The name of the numeric field to visualize."))
|
||||||
.with_example(
|
.with_example({"To visualize the sc_bytes field in the "
|
||||||
{"To visualize the sc_bytes field in the access_log format",
|
"access_log format",
|
||||||
"sc_bytes"})},
|
"sc_bytes"})},
|
||||||
{"quit",
|
{"quit",
|
||||||
com_quit,
|
com_quit,
|
||||||
|
|
||||||
|
@ -100,9 +100,6 @@ textfile_sub_source::text_value_for_line(textview_curses& tc,
|
|||||||
|
|
||||||
if (lf->get_text_format() == text_format_t::TF_BINARY) {
|
if (lf->get_text_format() == text_format_t::TF_BINARY) {
|
||||||
this->tss_hex_line.clear();
|
this->tss_hex_line.clear();
|
||||||
|
|
||||||
attr_line_builder alb(this->tss_hex_line);
|
|
||||||
|
|
||||||
auto fsize = lf->get_stat().st_size;
|
auto fsize = lf->get_stat().st_size;
|
||||||
auto fr = file_range{line * 16};
|
auto fr = file_range{line * 16};
|
||||||
fr.fr_size = std::min((file_ssize_t) 16, fsize - fr.fr_offset);
|
fr.fr_size = std::min((file_ssize_t) 16, fsize - fr.fr_offset);
|
||||||
@ -119,57 +116,12 @@ textfile_sub_source::text_value_for_line(textview_curses& tc,
|
|||||||
|
|
||||||
auto sbr = read_res.unwrap();
|
auto sbr = read_res.unwrap();
|
||||||
auto sf = sbr.to_string_fragment();
|
auto sf = sbr.to_string_fragment();
|
||||||
|
attr_line_builder alb(this->tss_hex_line);
|
||||||
{
|
{
|
||||||
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_FILE_OFFSET));
|
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_FILE_OFFSET));
|
||||||
alb.appendf(FMT_STRING("{: >16x} "), fr.fr_offset);
|
alb.appendf(FMT_STRING("{: >16x} "), fr.fr_offset);
|
||||||
}
|
}
|
||||||
auto byte_off = size_t{0};
|
alb.append_as_hexdump(sf);
|
||||||
for (auto ch : sf) {
|
|
||||||
if (byte_off == 8) {
|
|
||||||
alb.append(" ");
|
|
||||||
}
|
|
||||||
nonstd::optional<role_t> ro;
|
|
||||||
if (ch == '\0') {
|
|
||||||
ro = role_t::VCR_NULL;
|
|
||||||
} else if (isspace(ch) || iscntrl(ch)) {
|
|
||||||
ro = role_t::VCR_ASCII_CTRL;
|
|
||||||
} else if (!isprint(ch)) {
|
|
||||||
ro = role_t::VCR_NON_ASCII;
|
|
||||||
}
|
|
||||||
auto ag = ro.has_value() ? alb.with_attr(VC_ROLE.value(ro.value()))
|
|
||||||
: alb.with_default();
|
|
||||||
alb.appendf(FMT_STRING(" {:0>2x}"), ch);
|
|
||||||
byte_off += 1;
|
|
||||||
}
|
|
||||||
for (; byte_off < 16; byte_off++) {
|
|
||||||
if (byte_off == 8) {
|
|
||||||
alb.append(" ");
|
|
||||||
}
|
|
||||||
alb.append(" ");
|
|
||||||
}
|
|
||||||
alb.append(" ");
|
|
||||||
byte_off = 0;
|
|
||||||
for (auto ch : sf) {
|
|
||||||
if (byte_off == 8) {
|
|
||||||
alb.append(" ");
|
|
||||||
}
|
|
||||||
if (ch == '\0') {
|
|
||||||
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NULL));
|
|
||||||
alb.append("\u22c4");
|
|
||||||
} else if (isspace(ch)) {
|
|
||||||
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_ASCII_CTRL));
|
|
||||||
alb.append("_");
|
|
||||||
} else if (iscntrl(ch)) {
|
|
||||||
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_ASCII_CTRL));
|
|
||||||
alb.append("\u2022");
|
|
||||||
} else if (isprint(ch)) {
|
|
||||||
this->tss_hex_line.get_string().push_back(ch);
|
|
||||||
} else {
|
|
||||||
auto ag = alb.with_attr(VC_ROLE.value(role_t::VCR_NON_ASCII));
|
|
||||||
alb.append("\u00d7");
|
|
||||||
}
|
|
||||||
byte_off += 1;
|
|
||||||
}
|
|
||||||
auto alt_row_index = line % 4;
|
auto alt_row_index = line % 4;
|
||||||
if (alt_row_index == 2 || alt_row_index == 3) {
|
if (alt_row_index == 2 || alt_row_index == 3) {
|
||||||
this->tss_hex_line.with_attr_for_all(
|
this->tss_hex_line.with_attr_for_all(
|
||||||
|
Loading…
Reference in New Issue
Block a user