[console] colorize console output

pull/976/head
Timothy Stack 2 years ago
parent 7652c58248
commit 0785a432fa

@ -4,8 +4,11 @@
"properties": {
"$schema": {
"title": "/$schema",
"description": "Specifies the type of this file",
"type": "string"
"description": "The URI that specifies the schema that describes this type of file",
"type": "string",
"examples": [
"https://lnav.org/schemas/config-v1.schema.json"
]
},
"tuning": {
"description": "Internal settings",
@ -314,6 +317,36 @@
"description": "Styling for scrollbars",
"title": "/ui/theme-defs/<theme_name>/styles/scrollbar",
"$ref": "#/definitions/style"
},
"h1": {
"description": "Styling for top-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h1",
"$ref": "#/definitions/style"
},
"h2": {
"description": "Styling for 2nd-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h2",
"$ref": "#/definitions/style"
},
"h3": {
"description": "Styling for 3rd-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h3",
"$ref": "#/definitions/style"
},
"h4": {
"description": "Styling for 4th-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h4",
"$ref": "#/definitions/style"
},
"h5": {
"description": "Styling for 5th-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h5",
"$ref": "#/definitions/style"
},
"h6": {
"description": "Styling for 6th-level headers",
"title": "/ui/theme-defs/<theme_name>/styles/h6",
"$ref": "#/definitions/style"
}
},
"additionalProperties": false
@ -516,7 +549,7 @@
"title": "/ui/keymap-defs/<keymap_name>/<key_seq>/command",
"description": "The command to execute for the given key sequence. Use a script to execute more complicated operations.",
"type": "string",
"pattern": "[:|;].*",
"pattern": "^[:|;].*",
"examples": [
":goto next hour"
]

@ -419,18 +419,15 @@
"search-table": {
"description": "Search tables to automatically define for this log format",
"title": "/<format_name>/search-table",
"type": [
"string",
"object"
],
"type": "object",
"patternProperties": {
"\\w+": {
"(\\w+)": {
"description": "The set of search tables to be automatically defined",
"title": "/<format_name>/search-table/<>",
"title": "/<format_name>/search-table/<table_name>",
"type": "object",
"properties": {
"pattern": {
"title": "/<format_name>/search-table/<>/pattern",
"title": "/<format_name>/search-table/<table_name>/pattern",
"description": "The regular expression for this search table.",
"type": "string"
}

@ -244,9 +244,7 @@ add_library(
${GEN_SRCS}
config.h.in
all_logs_vtab.cc
ansi_scrubber.cc
archive_manager.cc
attr_line.cc
bin2c.hh
bookmarks.cc
bottom_status_source.cc
@ -317,7 +315,6 @@ add_library(
sql_util.cc
state-extension-functions.cc
styling.cc
string_attr_type.cc
text_format.cc
textfile_highlighters.cc
textfile_sub_source.cc
@ -342,8 +339,6 @@ add_library(
all_logs_vtab.hh
archive_manager.hh
archive_manager.cfg.hh
attr_line.hh
auto_mem.hh
big_array.hh
bottom_status_source.hh
bound_tags.hh
@ -369,6 +364,8 @@ add_library(
hotkeys.hh
input_dispatcher.hh
k_merge_tree.h
lnav_config.hh
lnav_config_fwd.hh
log_actions.hh
log_data_helper.hh
log_data_table.hh
@ -410,7 +407,6 @@ add_library(
sql_help.hh
sql_util.hh
strong_int.hh
string_attr_type.hh
sysclip.hh
sysclip.cfg.hh
term_extra.hh

@ -154,11 +154,8 @@ dist_noinst_DATA = \
noinst_HEADERS = \
all_logs_vtab.hh \
ansi_scrubber.hh \
archive_manager.hh \
archive_manager.cfg.hh \
attr_line.hh \
auto_mem.hh \
big_array.hh \
bin2c.hh \
bookmarks.hh \
@ -256,7 +253,6 @@ noinst_HEADERS = \
sqlite-extension-func.hh \
styling.hh \
statusview_curses.hh \
string_attr_type.hh \
strong_int.hh \
sysclip.hh \
sysclip.cfg.hh \
@ -306,9 +302,7 @@ THIRD_PARTY_SRCS = \
libdiag_a_SOURCES = \
$(THIRD_PARTY_SRCS) \
all_logs_vtab.cc \
ansi_scrubber.cc \
archive_manager.cc \
attr_line.cc \
bookmarks.cc \
bottom_status_source.cc \
collation-functions.cc \
@ -374,7 +368,6 @@ libdiag_a_SOURCES = \
sqlite-extension-func.cc \
statusview_curses.cc \
string-extension-functions.cc \
string_attr_type.cc \
styling.cc \
text_format.cc \
textfile_sub_source.cc \

@ -30,7 +30,7 @@
#include "all_logs_vtab.hh"
#include "config.h"
#include "string_attr_type.hh"
#include "base/attr_line.hh"
static auto intern_lifetime = intern_string::get_table_lifetime();

@ -40,8 +40,8 @@
#include "archive_manager.cfg.hh"
#include "archive_manager.hh"
#include "auto_mem.hh"
#include "base/auto_fd.hh"
#include "base/auto_mem.hh"
#include "base/fs_util.hh"
#include "base/humanize.hh"
#include "base/injector.hh"
@ -227,8 +227,10 @@ copy_data(const std::string& filename,
FMT_STRING("available space on disk ({}) is below the "
"minimum-free threshold ({}). Unable to unpack "
"'{}' to '{}'"),
humanize::file_size(tmp_space.available),
humanize::file_size(cfg.amc_min_free_space),
humanize::file_size(tmp_space.available,
humanize::alignment::none),
humanize::file_size(cfg.amc_min_free_space,
humanize::alignment::none),
entry_path.filename().string(),
entry_path.parent_path().string()));
}
@ -240,11 +242,11 @@ copy_data(const std::string& filename,
return Ok();
}
if (r != ARCHIVE_OK) {
return Err(
fmt::format(FMT_STRING("failed to read file: {} >> {} -- {}"),
filename,
archive_entry_pathname_utf8(entry),
archive_error_string(ar)));
return Err(fmt::format(
FMT_STRING("failed to extract '{}' from archive '{}' -- {}"),
archive_entry_pathname_utf8(entry),
filename,
archive_error_string(ar)));
}
r = archive_write_data_block(aw, buff, size, offset);
if (r != ARCHIVE_OK) {
@ -269,7 +271,7 @@ extract(const std::string& filename, const extract_cb& cb)
fs::create_directories(tmp_path.parent_path(), ec);
if (ec) {
return Err(fmt::format("Unable to create directory: {} -- {}",
return Err(fmt::format("unable to create directory: {} -- {}",
tmp_path.parent_path().string(),
ec.message()));
}

@ -1,6 +1,8 @@
add_library(
base STATIC
../config.h.in
ansi_scrubber.cc
attr_line.cc
auto_pid.cc
date_time_scanner.cc
fs_util.cc
@ -10,14 +12,20 @@ add_library(
intern_string.cc
is_utf8.cc
isc.cc
lnav.console.cc
lnav.gzip.cc
lnav_log.cc
network.tcp.cc
paths.cc
string_attr_type.cc
string_util.cc
strnatcmp.c
time_util.cc
ansi_scrubber.hh
attr_line.hh
auto_fd.hh
auto_mem.hh
auto_pid.hh
date_time_scanner.hh
enum_util.hh
@ -32,11 +40,13 @@ add_library(
intern_string.hh
is_utf8.hh
isc.hh
lnav.console.hh
lrucache.hpp
math_util.hh
network.tcp.hh
paths.hh
result.h
string_attr_type.hh
strnatcmp.h
time_util.hh)

@ -19,7 +19,10 @@ AM_CXXFLAGS = $(CODE_COVERAGE_CXXFLAGS)
noinst_LIBRARIES = libbase.a
noinst_HEADERS = \
ansi_scrubber.hh \
attr_line.hh \
auto_fd.hh \
auto_mem.hh \
auto_pid.hh \
date_time_scanner.hh \
enum_util.hh \
@ -36,6 +39,7 @@ noinst_HEADERS = \
is_utf8.hh \
isc.hh \
lnav_log.hh \
lnav.console.hh \
lnav.gzip.hh \
lrucache.hpp \
math_util.hh \
@ -43,11 +47,14 @@ noinst_HEADERS = \
opt_util.hh \
paths.hh \
result.h \
string_attr_type.hh \
string_util.hh \
strnatcmp.h \
time_util.hh
libbase_a_SOURCES = \
ansi_scrubber.cc \
attr_line.cc \
auto_pid.cc \
date_time_scanner.cc \
fs_util.cc \
@ -57,10 +64,12 @@ libbase_a_SOURCES = \
intern_string.cc \
is_utf8.cc \
isc.cc \
lnav.console.cc \
lnav.gzip.cc \
lnav_log.cc \
network.tcp.cc \
paths.cc \
string_attr_type.cc \
string_util.cc \
strnatcmp.c \
time_util.cc

@ -61,7 +61,7 @@ scrub_ansi_string(std::string& str, string_attrs_t& sa)
attr_t attrs = 0;
auto bg = nonstd::optional<int>();
auto fg = nonstd::optional<int>();
auto role = nonstd::optional<int>();
auto role = nonstd::optional<role_t>();
size_t lpc;
switch (pi.get_substr_start(&caps[2])[0]) {
@ -138,8 +138,10 @@ scrub_ansi_string(std::string& str, string_attrs_t& sa)
int role_int;
if (sscanf(&(str[caps[1].c_begin]), "%d", &role_int) == 1) {
if (role_int >= 0 && role_int < view_colors::VCR__MAX) {
role = role_int;
role_t role_tmp = (role_t) role_int;
if (role_tmp > role_t::VCR_NONE
&& role_tmp < role_t::VCR__MAX) {
role = role_tmp;
has_attrs = true;
}
}
@ -158,16 +160,15 @@ scrub_ansi_string(std::string& str, string_attrs_t& sa)
lr.lr_start = caps[0].c_begin;
lr.lr_end = -1;
if (attrs) {
sa.emplace_back(lr, view_curses::VC_STYLE.value(attrs));
sa.emplace_back(lr, VC_STYLE.value(attrs));
}
role | [&lr, &sa](int r) {
sa.emplace_back(lr, view_curses::VC_ROLE.value(r));
};
role |
[&lr, &sa](role_t r) { sa.emplace_back(lr, VC_ROLE.value(r)); };
fg | [&lr, &sa](int color) {
sa.emplace_back(lr, view_curses::VC_FOREGROUND.value(color));
sa.emplace_back(lr, VC_FOREGROUND.value(color));
};
bg | [&lr, &sa](int color) {
sa.emplace_back(lr, view_curses::VC_BACKGROUND.value(color));
sa.emplace_back(lr, VC_BACKGROUND.value(color));
};
}

@ -34,7 +34,7 @@
#include "ansi_scrubber.hh"
#include "auto_mem.hh"
#include "config.h"
#include "view_curses.hh"
#include "lnav_log.hh"
attr_line_t&
attr_line_t::with_ansi_string(const char* str, ...)
@ -257,21 +257,49 @@ attr_line_t::apply_hide()
struct line_range& lr = sattr.sa_range;
std::for_each(sa.begin(), sa.end(), [&](string_attr& attr) {
if (attr.sa_type == &view_curses::VC_STYLE
&& lr.contains(attr.sa_range)) {
if (attr.sa_type == &VC_STYLE && lr.contains(attr.sa_range)) {
attr.sa_type = &SA_REMOVED;
}
});
this->al_string.replace(lr.lr_start, lr.length(), "\xE2\x8B\xAE");
shift_string_attrs(sa, lr.lr_start + 1, -(lr.length() - 3));
sattr.sa_type = &view_curses::VC_ROLE;
sattr.sa_value = view_colors::VCR_HIDDEN;
sattr.sa_type = &VC_ROLE;
sattr.sa_value = role_t::VCR_HIDDEN;
lr.lr_end = lr.lr_start + 3;
}
}
}
attr_line_t&
attr_line_t::rtrim()
{
auto index = this->al_string.length();
for (; index > 0; index--) {
if (!isspace(this->al_string[index - 1])) {
break;
}
}
if (index > 0 && index < this->al_string.length()) {
this->erase(index);
}
return *this;
}
attr_line_t&
attr_line_t::erase(size_t pos, size_t len)
{
if (len == std::string::npos) {
len = this->al_string.size() - pos;
}
this->al_string.erase(pos, len);
shift_string_attrs(this->al_attrs, pos, -((int32_t) len));
return *this;
}
line_range
line_range::intersection(const line_range& other) const
{
@ -290,13 +318,14 @@ line_range::intersection(const line_range& other) const
line_range&
line_range::shift(int32_t start, int32_t amount)
{
if (this->lr_start >= start) {
if (start <= this->lr_start) {
this->lr_start = std::max(0, this->lr_start + amount);
}
if (this->lr_end != -1 && start <= this->lr_end) {
this->lr_end += amount;
if (this->lr_end < this->lr_start) {
this->lr_end = this->lr_start;
if (this->lr_end != -1) {
this->lr_end = std::max(0, this->lr_end + amount);
}
} else if (this->lr_end != -1) {
if (start < this->lr_end) {
this->lr_end = std::max(this->lr_start, this->lr_end + amount);
}
}

@ -38,10 +38,10 @@
#include <limits.h>
#include "base/intern_string.hh"
#include "base/lnav_log.hh"
#include "base/string_util.hh"
#include "fmt/format.h"
#include "intern_string.hh"
#include "string_attr_type.hh"
#include "string_util.hh"
/**
* Encapsulates a range in a string.
@ -137,18 +137,8 @@ struct line_range {
}
};
/**
* Container for attribute values for a substring.
*/
typedef union {
const void* sav_ptr;
int64_t sav_int;
} string_attr_value_t;
struct string_attr {
string_attr(
const struct line_range& lr,
const std::pair<const string_attr_type_base*, string_attr_value>& value)
string_attr(const struct line_range& lr, const string_attr_pair& value)
: sa_range(lr), sa_type(value.first), sa_value(value.second)
{
}
@ -444,9 +434,7 @@ public:
}
template<typename S>
attr_line_t& append(
S str,
const std::pair<const string_attr_type_base*, string_attr_value>& value)
attr_line_t& append(S str, const string_attr_pair& value)
{
size_t start_len = this->al_string.length();
@ -459,6 +447,63 @@ public:
return *this;
}
template<typename S>
attr_line_t& append(const std::pair<S, string_attr_pair>& value)
{
size_t start_len = this->al_string.length();
this->al_string.append(std::move(value.first));
line_range lr{(int) start_len, (int) this->al_string.length()};
this->al_attrs.emplace_back(lr, value.second);
return *this;
}
template<typename S>
attr_line_t& append_quoted(const std::pair<S, string_attr_pair>& value)
{
this->al_string.append("\u201c");
size_t start_len = this->al_string.length();
this->al_string.append(std::move(value.first));
line_range lr{(int) start_len, (int) this->al_string.length()};
this->al_attrs.emplace_back(lr, value.second);
this->al_string.append("\u201d");
return *this;
}
attr_line_t& append_quoted(const intern_string_t str)
{
this->al_string.append("\u201c");
this->al_string.append(str.get(), str.size());
this->al_string.append("\u201d");
return *this;
}
template<typename S>
attr_line_t& append_quoted(S s)
{
this->al_string.append("\u201c");
this->al_string.append(std::move(s));
this->al_string.append("\u201d");
return *this;
}
attr_line_t& append(const intern_string_t str)
{
this->al_string.append(str.get(), str.size());
return *this;
}
template<typename S>
attr_line_t& append(S str)
{
@ -466,6 +511,14 @@ public:
return *this;
}
template<typename... Args>
attr_line_t& append(fmt::string_view format_str, const Args&... args)
{
this->template append(fmt::vformat(
format_str, fmt::make_args_checked<Args...>(format_str, args...)));
return *this;
}
attr_line_t& append(const char* str, size_t len)
{
this->al_string.append(str, len);
@ -507,14 +560,9 @@ public:
return *this;
}
attr_line_t& erase(size_t pos, size_t len = std::string::npos)
{
this->al_string.erase(pos, len);
attr_line_t& erase(size_t pos, size_t len = std::string::npos);
shift_string_attrs(this->al_attrs, pos, -((int32_t) len));
return *this;
}
attr_line_t& rtrim();
attr_line_t& erase_utf8_chars(size_t start)
{
@ -548,7 +596,8 @@ public:
return this->al_string.substr(lr.lr_start, lr.sublen(this->al_string));
}
string_fragment to_string_fragment(string_attrs_t::const_iterator iter)
string_fragment to_string_fragment(
string_attrs_t::const_iterator iter) const
{
return string_fragment(this->al_string.c_str(),
iter->sa_range.lr_start,
@ -571,6 +620,11 @@ public:
return this->length() == 0;
}
bool blank() const
{
return is_blank(this->al_string);
}
/** Clear the string and the attributes for the string. */
attr_line_t& clear()
{
@ -584,11 +638,18 @@ public:
void split_lines(std::vector<attr_line_t>& lines) const;
std::vector<attr_line_t> split_lines() const
{
std::vector<attr_line_t> retval;
this->split_lines(retval);
return retval;
}
size_t nearest_text(size_t x) const;
void apply_hide();
private:
const static size_t RESERVE_SIZE = 128;
std::string al_string;

@ -40,7 +40,7 @@
#include "base/result.h"
typedef void (*free_func_t)(void*);
using free_func_t = void (*)(void*);
/**
* Resource management class for memory allocated by a custom allocator.

@ -108,5 +108,19 @@ build_path(const std::vector<ghc::filesystem::path>& paths)
return retval;
}
Result<struct stat, std::string>
stat_file(const ghc::filesystem::path& path)
{
struct stat retval;
if (statp(path, &retval) == 0) {
return Ok(retval);
}
return Err(fmt::format(FMT_STRING("failed to find file: {} -- {}"),
path.string(),
strerror(errno)));
}
} // namespace filesystem
} // namespace lnav

@ -62,6 +62,8 @@ Result<auto_fd, std::string> open_file(const ghc::filesystem::path& path,
int flags,
mode_t mode);
Result<struct stat, std::string> stat_file(const ghc::filesystem::path& path);
Result<std::pair<ghc::filesystem::path, auto_fd>, std::string> open_temp_file(
const ghc::filesystem::path& pattern);

@ -38,7 +38,7 @@
namespace humanize {
std::string
file_size(file_ssize_t value)
file_size(file_ssize_t value, alignment align)
{
static const double LN1024 = log(1024.0);
static const std::vector<const char*> UNITS = {
@ -56,13 +56,21 @@ file_size(file_ssize_t value)
}
if (value == 0) {
return "0.0 B";
switch (align) {
case alignment::none:
return "0B";
case alignment::columnar:
return "0.0 B";
}
}
auto exp
= floor(std::min(log(value) / LN1024, (double) (UNITS.size() - 1)));
auto divisor = pow(1024, exp);
if (align == alignment::none && divisor <= 1) {
return fmt::format(FMT_STRING("{}B"), value, UNITS[exp]);
}
return fmt::format(FMT_STRING("{:.1f}{}B"),
divisor == 0 ? value : value / divisor,
UNITS[exp]);

@ -36,11 +36,16 @@
TEST_CASE("humanize::file_size")
{
CHECK(humanize::file_size(0) == "0.0 B");
CHECK(humanize::file_size(1) == "1.0 B");
CHECK(humanize::file_size(1024) == "1.0KB");
CHECK(humanize::file_size(1500) == "1.5KB");
CHECK(humanize::file_size(55LL * 784LL * 1024LL * 1024LL) == "42.1GB");
CHECK(humanize::file_size(-1LL) == "Unknown");
CHECK(humanize::file_size(std::numeric_limits<int64_t>::max()) == "8.0EB");
CHECK(humanize::file_size(0, humanize::alignment::columnar) == "0.0 B");
CHECK(humanize::file_size(1, humanize::alignment::columnar) == "1.0 B");
CHECK(humanize::file_size(1024, humanize::alignment::columnar) == "1.0KB");
CHECK(humanize::file_size(1500, humanize::alignment::columnar) == "1.5KB");
CHECK(humanize::file_size(55LL * 784LL * 1024LL * 1024LL,
humanize::alignment::columnar)
== "42.1GB");
CHECK(humanize::file_size(-1LL, humanize::alignment::columnar)
== "Unknown");
CHECK(humanize::file_size(std::numeric_limits<int64_t>::max(),
humanize::alignment::columnar)
== "8.0EB");
}

@ -38,13 +38,18 @@
namespace humanize {
enum class alignment {
none,
columnar,
};
/**
* Format the given size as a human-friendly string.
*
* @param value The value to format.
* @return The formatted string.
*/
std::string file_size(file_ssize_t value);
std::string file_size(file_ssize_t value, alignment align);
const std::string& sparkline(double value, nonstd::optional<double> upper);

@ -0,0 +1,265 @@
/**
* 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 <algorithm>
#include "lnav.console.hh"
#include "config.h"
#include "fmt/color.h"
#include "view_curses.hh"
namespace lnav {
namespace console {
user_message
user_message::error(const attr_line_t& al)
{
user_message retval;
retval.um_level = level::error;
retval.um_message.append(al);
return retval;
}
user_message
user_message::info(const attr_line_t& al)
{
user_message retval;
retval.um_level = level::info;
retval.um_message.append(al);
return retval;
}
user_message
user_message::ok(const attr_line_t& al)
{
user_message retval;
retval.um_level = level::ok;
retval.um_message.append(al);
return retval;
}
user_message
user_message::warning(const attr_line_t& al)
{
user_message retval;
retval.um_level = level::warning;
retval.um_message.append(al);
return retval;
}
attr_line_t
user_message::to_attr_line(std::set<render_flags> flags) const
{
attr_line_t retval;
if (flags.count(render_flags::prefix)) {
switch (this->um_level) {
case level::ok:
retval.append(lnav::roles::ok("\u2714 "));
break;
case level::info:
retval.append(lnav::roles::status("\u24d8 info")).append(": ");
break;
case level::warning:
retval.append(lnav::roles::warning("\u26a0 warning"))
.append(": ");
break;
case level::error:
retval.append(lnav::roles::error("\u2718 error")).append(": ");
break;
}
}
retval.append(this->um_message).append("\n");
if (!this->um_reason.empty()) {
bool first_line = true;
for (const auto& line : this->um_reason.split_lines()) {
attr_line_t prefix;
if (first_line) {
prefix.append(lnav::roles::error(" reason")).append(": ");
first_line = false;
} else {
prefix.append(lnav::roles::error(" | "));
}
retval.append(prefix).append(line).append("\n");
}
}
if (!this->um_snippets.empty()) {
for (const auto& snip : this->um_snippets) {
attr_line_t header;
header.append(lnav::roles::comment(" --> "))
.append(lnav::roles::file(snip.s_source));
if (snip.s_line > 0) {
header.append(":").append(FMT_STRING("{}"), snip.s_line);
if (snip.s_column > 0) {
header.append(":").append(FMT_STRING("{}"), snip.s_column);
}
}
retval.append(header).append("\n");
if (!snip.s_content.blank()) {
for (const auto& line : snip.s_content.split_lines()) {
retval.append(lnav::roles::comment(" | "))
.append(line)
.append("\n");
}
}
}
}
if (!this->um_notes.empty()) {
bool first_line = true;
for (const auto& note : this->um_notes) {
for (const auto& line : note.split_lines()) {
attr_line_t prefix;
if (first_line) {
prefix.append(lnav::roles::comment(" = note")).append(": ");
first_line = false;
} else {
prefix.append(" ");
}
retval.append(prefix).append(line).append("\n");
}
}
}
if (!this->um_help.empty()) {
bool first_line = true;
for (const auto& line : this->um_help.split_lines()) {
attr_line_t prefix;
if (first_line) {
prefix.append(lnav::roles::comment(" = help")).append(": ");
first_line = false;
} else {
prefix.append(" ");
}
retval.append(prefix).append(line).append("\n");
}
}
return retval;
}
void
println(FILE* file, const attr_line_t& al)
{
const auto& str = al.get_string();
if (!isatty(fileno(file))) {
fmt::print(file, "{}\n", str);
return;
}
string_attrs_t style_attrs;
for (const auto& sa : al.get_attrs()) {
if (sa.sa_type != &VC_ROLE) {
continue;
}
style_attrs.emplace_back(sa);
}
std::sort(style_attrs.begin(), style_attrs.end(), [](auto lhs, auto rhs) {
return lhs.sa_range < rhs.sa_range;
});
auto start = size_t{0};
for (const auto& attr : style_attrs) {
fmt::print(
file, "{}", str.substr(start, attr.sa_range.lr_start - start));
if (attr.sa_type == &VC_ROLE) {
auto saw = string_attr_wrapper<role_t>(&attr);
auto role = saw.get();
auto line_style = fmt::text_style();
switch (role) {
case role_t::VCR_ERROR:
line_style = fmt::fg(fmt::terminal_color::red);
break;
case role_t::VCR_WARNING:
line_style = fmt::fg(fmt::terminal_color::yellow);
break;
case role_t::VCR_COMMENT:
line_style = fmt::fg(fmt::terminal_color::cyan);
break;
case role_t::VCR_OK:
line_style = fmt::emphasis::bold
| fmt::fg(fmt::terminal_color::red);
break;
case role_t::VCR_STATUS:
line_style = fmt::emphasis::bold
| fmt::fg(fmt::terminal_color::magenta);
break;
case role_t::VCR_VARIABLE:
line_style = fmt::emphasis::underline;
break;
case role_t::VCR_SYMBOL:
case role_t::VCR_NUMBER:
case role_t::VCR_FILE:
line_style = fmt::emphasis::bold;
break;
case role_t::VCR_H1:
case role_t::VCR_H2:
case role_t::VCR_H3:
case role_t::VCR_H4:
case role_t::VCR_H5:
case role_t::VCR_H6:
line_style = fmt::emphasis::underline;
break;
default:
break;
}
fmt::print(
file,
line_style,
"{}",
str.substr(attr.sa_range.lr_start, attr.sa_range.length()));
}
start = attr.sa_range.lr_end;
}
fmt::print(file, "{}\n", str.substr(start));
}
void
print(FILE* file, const user_message& um)
{
println(file, um.to_attr_line().rtrim());
}
} // namespace console
} // namespace lnav

@ -0,0 +1,149 @@
/**
* 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_console_hh
#define lnav_console_hh
#include <set>
#include <vector>
#include "base/attr_line.hh"
namespace lnav {
namespace console {
void println(FILE* file, const attr_line_t& al);
struct snippet {
static snippet from(std::string src, const attr_line_t& content)
{
snippet retval;
retval.s_source = std::move(src);
retval.s_content = content;
return retval;
}
snippet& with_line(int32_t line)
{
this->s_line = line;
return *this;
}
snippet& with_column(int32_t column)
{
this->s_column = column;
return *this;
}
std::string s_source;
int32_t s_line{0};
int32_t s_column{0};
attr_line_t s_content;
};
struct user_message {
enum class level {
ok,
info,
warning,
error,
};
static user_message error(const attr_line_t& al);
static user_message warning(const attr_line_t& al);
static user_message info(const attr_line_t& al);
static user_message ok(const attr_line_t& al);
user_message& with_reason(const attr_line_t& al)
{
this->um_reason = al;
this->um_reason.rtrim();
return *this;
}
user_message& with_errno_reason()
{
this->um_reason = strerror(errno);
return *this;
}
user_message& with_snippet(const snippet& sn)
{
this->um_snippets.emplace_back(sn);
return *this;
}
user_message& with_snippets(std::vector<snippet> snippets)
{
this->um_snippets.insert(this->um_snippets.end(),
std::make_move_iterator(snippets.begin()),
std::make_move_iterator(snippets.end()));
return *this;
}
user_message& with_note(const attr_line_t& al)
{
this->um_notes.emplace_back(al);
return *this;
}
user_message& with_help(const attr_line_t& al)
{
this->um_help = al;
this->um_help.rtrim();
return *this;
}
enum class render_flags {
prefix,
};
attr_line_t to_attr_line(std::set<render_flags> flags
= {render_flags::prefix}) const;
level um_level{level::ok};
attr_line_t um_message;
std::vector<snippet> um_snippets;
attr_line_t um_reason;
std::vector<attr_line_t> um_notes;
attr_line_t um_help;
};
void print(FILE* file, const user_message& um);
} // namespace console
} // namespace lnav
#endif

@ -38,3 +38,10 @@ string_attr_type<const intern_string_t> SA_FORMAT("format");
string_attr_type<void> SA_REMOVED("removed");
string_attr_type<std::string> SA_INVALID("invalid");
string_attr_type<std::string> SA_ERROR("error");
string_attr_type<role_t> VC_ROLE("role");
string_attr_type<role_t> VC_ROLE_FG("role-fg");
string_attr_type<int64_t> VC_STYLE("style");
string_attr_type<int64_t> VC_GRAPHIC("graphic");
string_attr_type<int64_t> VC_FOREGROUND("foreground");
string_attr_type<int64_t> VC_BACKGROUND("background");

@ -0,0 +1,346 @@
/**
* Copyright (c) 2020, 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_string_attr_type_hh
#define lnav_string_attr_type_hh
#include <string>
#include <utility>
#include <stdint.h>
#include "base/intern_string.hh"
#include "mapbox/variant.hpp"
class logfile;
struct bookmark_metadata;
/** Roles that can be mapped to curses attributes using attrs_for_role() */
enum class role_t : int32_t {
VCR_NONE = -1,
VCR_TEXT, /*< Raw text. */
VCR_IDENTIFIER,
VCR_SEARCH, /*< A search hit. */
VCR_OK,
VCR_ERROR, /*< An error message. */
VCR_WARNING, /*< A warning message. */
VCR_ALT_ROW, /*< Highlight for alternating rows in a list */
VCR_HIDDEN,
VCR_ADJUSTED_TIME,
VCR_SKEWED_TIME,
VCR_OFFSET_TIME,
VCR_INVALID_MSG,
VCR_STATUS, /*< Normal status line text. */
VCR_WARN_STATUS,
VCR_ALERT_STATUS, /*< Alert status line text. */
VCR_ACTIVE_STATUS, /*< */
VCR_ACTIVE_STATUS2, /*< */
VCR_STATUS_TITLE,
VCR_STATUS_SUBTITLE,
VCR_STATUS_STITCH_TITLE_TO_SUB,
VCR_STATUS_STITCH_SUB_TO_TITLE,
VCR_STATUS_STITCH_SUB_TO_NORMAL,
VCR_STATUS_STITCH_NORMAL_TO_SUB,
VCR_STATUS_STITCH_TITLE_TO_NORMAL,
VCR_STATUS_STITCH_NORMAL_TO_TITLE,
VCR_STATUS_TITLE_HOTKEY,
VCR_STATUS_DISABLED_TITLE,
VCR_STATUS_HOTKEY,
VCR_INACTIVE_STATUS,
VCR_INACTIVE_ALERT_STATUS,
VCR_SCROLLBAR,
VCR_SCROLLBAR_ERROR,
VCR_SCROLLBAR_WARNING,
VCR_FOCUSED,
VCR_DISABLED_FOCUSED,
VCR_POPUP,
VCR_COLOR_HINT,
VCR_KEYWORD,
VCR_STRING,
VCR_COMMENT,
VCR_DOC_DIRECTIVE,
VCR_VARIABLE,
VCR_SYMBOL,
VCR_NUMBER,
VCR_RE_SPECIAL,
VCR_RE_REPEAT,
VCR_FILE,
VCR_DIFF_DELETE, /*< Deleted line in a diff. */
VCR_DIFF_ADD, /*< Added line in a diff. */
VCR_DIFF_SECTION, /*< Section marker in a diff. */
VCR_LOW_THRESHOLD,
VCR_MED_THRESHOLD,
VCR_HIGH_THRESHOLD,
VCR_H1,
VCR_H2,
VCR_H3,
VCR_H4,
VCR_H5,
VCR_H6,
VCR__MAX
};
using string_attr_value = mapbox::util::variant<int64_t,
role_t,
const intern_string_t,
std::string,
std::shared_ptr<logfile>,
bookmark_metadata*>;
class string_attr_type_base {
public:
explicit string_attr_type_base(const char* name) noexcept : sat_name(name)
{
}
const char* const sat_name;
};
using string_attr_pair
= std::pair<const string_attr_type_base*, string_attr_value>;
template<typename T>
class string_attr_type : public string_attr_type_base {
public:
using value_type = T;
explicit string_attr_type(const char* name) noexcept
: string_attr_type_base(name)
{
}
template<typename U = T>
std::enable_if_t<!std::is_void<U>::value, string_attr_pair> value(
const U& val) const
{
return std::make_pair(this, val);
}
template<typename U = T>
std::enable_if_t<std::is_void<U>::value, string_attr_pair> value() const
{
return std::make_pair(this, string_attr_value{});
}
};
extern string_attr_type<void> SA_ORIGINAL_LINE;
extern string_attr_type<void> SA_BODY;
extern string_attr_type<void> SA_HIDDEN;
extern string_attr_type<const intern_string_t> SA_FORMAT;
extern string_attr_type<void> SA_REMOVED;
extern string_attr_type<std::string> SA_INVALID;
extern string_attr_type<std::string> SA_ERROR;
extern string_attr_type<role_t> VC_ROLE;
extern string_attr_type<role_t> VC_ROLE_FG;
extern string_attr_type<int64_t> VC_STYLE;
extern string_attr_type<int64_t> VC_GRAPHIC;
extern string_attr_type<int64_t> VC_FOREGROUND;
extern string_attr_type<int64_t> VC_BACKGROUND;
namespace lnav {
namespace roles {
template<typename S>
inline std::pair<S, string_attr_pair>
error(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_ERROR));
}
template<typename S>
inline std::pair<S, string_attr_pair>
warning(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_WARNING));
}
template<typename S>
inline std::pair<S, string_attr_pair>
status(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_STATUS));
}
template<typename S>
inline std::pair<S, string_attr_pair>
ok(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_OK));
}
template<typename S>
inline std::pair<S, string_attr_pair>
file(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_FILE));
}
template<typename S>
inline std::pair<S, string_attr_pair>
symbol(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_SYMBOL));
}
template<typename S>
inline std::pair<S, string_attr_pair>
keyword(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_KEYWORD));
}
template<typename S>
inline std::pair<S, string_attr_pair>
variable(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_VARIABLE));
}
template<typename S>
inline std::pair<S, string_attr_pair>
number(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_NUMBER));
}
template<typename S>
inline std::pair<S, string_attr_pair>
comment(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_COMMENT));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h1(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H1));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h2(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H2));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h3(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H3));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h4(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H4));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h5(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H5));
}
template<typename S>
inline std::pair<S, string_attr_pair>
h6(S str)
{
return std::make_pair(std::move(str),
VC_ROLE.template value(role_t::VCR_H6));
}
namespace literals {
inline std::pair<std::string, string_attr_pair> operator"" _symbol(
const char* str, std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_SYMBOL));
}
inline std::pair<std::string, string_attr_pair> operator"" _variable(
const char* str, std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_VARIABLE));
}
inline std::pair<std::string, string_attr_pair> operator"" _h1(const char* str,
std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_H1));
}
inline std::pair<std::string, string_attr_pair> operator"" _h2(const char* str,
std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_H2));
}
inline std::pair<std::string, string_attr_pair> operator"" _h3(const char* str,
std::size_t len)
{
return std::make_pair(std::string(str, len),
VC_ROLE.template value(role_t::VCR_H3));
}
} // namespace literals
} // namespace roles
} // namespace lnav
#endif

@ -27,6 +27,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <algorithm>
#include <iterator>
#include <regex>
#include <sstream>
@ -253,6 +254,13 @@ strtonum(T& num_out, const char* string, size_t len)
return retval;
}
bool
is_blank(const std::string& str)
{
return std::all_of(
str.begin(), str.end(), [](const auto ch) { return isspace(ch); });
}
template size_t strtonum<long long>(long long& num_out,
const char* string,
size_t len);

@ -188,6 +188,8 @@ utf8_string_length(const std::string& str)
bool is_url(const char* fn);
bool is_blank(const std::string& str);
size_t abbreviate_str(char* str, size_t len, size_t max_len);
void split_ws(const std::string& str, std::vector<std::string>& toks_out);

@ -33,4 +33,53 @@
#include "config.h"
std::set<std::string> bookmark_metadata::KNOWN_TAGS;
std::unordered_set<std::string> bookmark_metadata::KNOWN_TAGS;
void
bookmark_metadata::add_tag(const std::string& tag)
{
if (std::find(this->bm_tags.begin(), this->bm_tags.end(), tag)
== this->bm_tags.end())
{
this->bm_tags.push_back(tag);
}
}
bool
bookmark_metadata::remove_tag(const std::string& tag)
{
auto iter = std::find(this->bm_tags.begin(), this->bm_tags.end(), tag);
bool retval = false;
if (iter != this->bm_tags.end()) {
this->bm_tags.erase(iter);
retval = true;
}
return retval;
}
bool
bookmark_metadata::empty() const
{
return this->bm_name.empty() && this->bm_comment.empty()
&& this->bm_tags.empty();
}
void
bookmark_metadata::clear()
{
this->bm_comment.clear();
this->bm_tags.clear();
}
bookmark_type_t*
bookmark_type_t::find_type(const std::string& name)
{
auto iter = std::find_if(type_begin(), type_end(), mark_eq(name));
bookmark_type_t* retval = nullptr;
if (iter != type_end()) {
retval = (*iter);
}
return retval;
}

@ -34,51 +34,26 @@
#include <algorithm>
#include <map>
#include <set>
#include <unordered_set>
#include <string>
#include <vector>
#include "base/lnav_log.hh"
struct bookmark_metadata {
static std::set<std::string> KNOWN_TAGS;
static std::unordered_set<std::string> KNOWN_TAGS;
std::string bm_name;
std::string bm_comment;
std::vector<std::string> bm_tags;
void add_tag(const std::string& tag)
{
if (std::find(this->bm_tags.begin(), this->bm_tags.end(), tag)
== this->bm_tags.end())
{
this->bm_tags.push_back(tag);
}
};
void add_tag(const std::string& tag);
bool remove_tag(const std::string& tag)
{
auto iter = std::find(this->bm_tags.begin(), this->bm_tags.end(), tag);
bool retval = false;
if (iter != this->bm_tags.end()) {
this->bm_tags.erase(iter);
retval = true;
}
return retval;
};
bool remove_tag(const std::string& tag);
bool empty() const
{
return this->bm_name.empty() && this->bm_comment.empty()
&& this->bm_tags.empty();
};
bool empty() const;
void clear()
{
this->bm_comment.clear();
this->bm_tags.clear();
};
void clear();
};
/**
@ -96,12 +71,12 @@ struct bookmark_metadata {
*/
template<typename LineType>
class bookmark_vector : public std::vector<LineType> {
typedef std::vector<LineType> base_vector;
using base_vector = std::vector<LineType>;
public:
typedef typename base_vector::size_type size_type;
typedef typename base_vector::iterator iterator;
typedef typename base_vector::const_iterator const_iterator;
using size_type = typename base_vector::size_type;
using iterator = typename base_vector::iterator;
using const_iterator = typename base_vector::const_iterator;
/**
* Insert a bookmark into this vector, but only if it is not already in the
@ -111,11 +86,11 @@ public:
*/
iterator insert_once(LineType vl)
{
iterator lb, retval;
iterator retval;
require(vl >= 0);
lb = std::lower_bound(this->begin(), this->end(), vl);
auto lb = std::lower_bound(this->begin(), this->end(), vl);
if (lb == this->end() || *lb != vl) {
this->insert(lb, vl);
retval = this->end();
@ -132,11 +107,11 @@ public:
if (stop == LineType(-1)) {
return std::make_pair(lb, this->end());
} else {
auto up = std::upper_bound(this->begin(), this->end(), stop);
return std::make_pair(lb, up);
}
auto up = std::upper_bound(this->begin(), this->end(), stop);
return std::make_pair(lb, up);
};
/**
@ -164,45 +139,36 @@ public:
*/
class bookmark_type_t {
public:
typedef std::vector<bookmark_type_t*>::iterator type_iterator;
using type_iterator = std::vector<bookmark_type_t*>::iterator;
static type_iterator type_begin()
{
return get_all_types().begin();
};
}
static type_iterator type_end()
{
return get_all_types().end();
};
static bookmark_type_t* find_type(const std::string& name)
{
auto iter = find_if(type_begin(), type_end(), mark_eq(name));
bookmark_type_t* retval = nullptr;
}
if (iter != type_end()) {
retval = (*iter);
}
return retval;
};
static bookmark_type_t* find_type(const std::string& name);
static std::vector<bookmark_type_t*>& get_all_types()
{
static std::vector<bookmark_type_t*> all_types;
return all_types;
};
}
explicit bookmark_type_t(std::string name) : bt_name(std::move(name))
{
get_all_types().push_back(this);
};
}
const std::string& get_name() const
{
return this->bt_name;
};
}
private:
struct mark_eq {
@ -227,7 +193,7 @@ bookmark_vector<LineType>::next(LineType start) const
require(start >= -1);
auto ub = upper_bound(this->cbegin(), this->cend(), start);
auto ub = std::upper_bound(this->cbegin(), this->cend(), start);
if (ub != this->cend()) {
retval = *ub;
}
@ -245,7 +211,7 @@ bookmark_vector<LineType>::prev(LineType start) const
require(start >= 0);
auto lb = lower_bound(this->cbegin(), this->cend(), start);
auto lb = std::lower_bound(this->cbegin(), this->cend(), start);
if (lb != this->cbegin()) {
lb -= 1;
retval = *lb;
@ -261,7 +227,7 @@ bookmark_vector<LineType>::prev(LineType start) const
*/
template<typename LineType>
struct bookmarks {
typedef std::map<const bookmark_type_t*, bookmark_vector<LineType> > type;
using type = std::map<const bookmark_type_t*, bookmark_vector<LineType>>;
};
#endif

@ -158,18 +158,18 @@ void
bottom_status_source::update_hits(textview_curses* tc)
{
status_field& sf = this->bss_fields[BSF_HITS];
view_colors::role_t new_role;
role_t new_role;
if (tc->is_searching()) {
this->bss_hit_spinner += 1;
if (this->bss_hit_spinner % 2) {
new_role = view_colors::VCR_ACTIVE_STATUS;
new_role = role_t::VCR_ACTIVE_STATUS;
} else {
new_role = view_colors::VCR_ACTIVE_STATUS2;
new_role = role_t::VCR_ACTIVE_STATUS2;
}
sf.set_cylon(true);
} else {
new_role = view_colors::VCR_STATUS;
new_role = role_t::VCR_STATUS;
sf.set_cylon(false);
}
// this->bss_error.clear();
@ -186,7 +186,7 @@ bottom_status_source::update_loading(file_off_t off, file_size_t total)
if (total == 0 || (size_t) off == total) {
sf.set_cylon(false);
sf.set_role(view_colors::VCR_STATUS);
sf.set_role(role_t::VCR_STATUS);
if (this->bss_paused) {
sf.set_value("\xE2\x80\x96 Paused");
} else {
@ -199,7 +199,7 @@ bottom_status_source::update_loading(file_off_t off, file_size_t total)
this->bss_load_percent = pct;
sf.set_cylon(true);
sf.set_role(view_colors::VCR_ACTIVE_STATUS2);
sf.set_role(role_t::VCR_ACTIVE_STATUS2);
sf.set_value(" Loading %2d%% ", pct);
}
}

@ -85,9 +85,9 @@ public:
void update_loading(file_off_t off, file_size_t total);
private:
status_field bss_prompt{1024, view_colors::VCR_STATUS};
status_field bss_error{1024, view_colors::VCR_ALERT_STATUS};
status_field bss_line_error{1024, view_colors::VCR_ALERT_STATUS};
status_field bss_prompt{1024, role_t::VCR_STATUS};
status_field bss_error{1024, role_t::VCR_ALERT_STATUS};
status_field bss_line_error{1024, role_t::VCR_ALERT_STATUS};
status_field bss_fields[BSF__MAX];
int bss_hit_spinner{0};
int bss_load_percent{0};

@ -31,12 +31,14 @@
#include "command_executor.hh"
#include "base/ansi_scrubber.hh"
#include "base/fs_util.hh"
#include "base/injector.hh"
#include "base/string_util.hh"
#include "bound_tags.hh"
#include "config.h"
#include "db_sub_source.hh"
#include "help_text_formatter.hh"
#include "lnav.hh"
#include "lnav_config.hh"
#include "lnav_util.hh"
@ -101,14 +103,14 @@ sql_progress_finished()
lnav_data.ld_views[LNV_DB].redo_search();
}
Result<std::string, std::string> execute_from_file(
Result<std::string, lnav::console::user_message> execute_from_file(
exec_context& ec,
const ghc::filesystem::path& path,
int line_number,
char mode,
const std::string& cmdline);
Result<std::string, std::string>
Result<std::string, lnav::console::user_message>
execute_command(exec_context& ec, const std::string& cmdline)
{
std::vector<std::string> args;
@ -122,15 +124,18 @@ execute_command(exec_context& ec, const std::string& cmdline)
if ((iter = lnav_commands.find(args[0])) == lnav_commands.end()) {
return ec.make_error("unknown command - {}", args[0]);
} else {
return iter->second->c_func(ec, cmdline, args);
}
ec.ec_current_help = &iter->second->c_help;
auto retval = iter->second->c_func(ec, cmdline, args);
ec.ec_current_help = nullptr;
return retval;
}
return ec.make_error("no command to execute");
}
Result<std::string, std::string>
Result<std::string, lnav::console::user_message>
execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
{
db_label_source& dls = lnav_data.ld_db_row_source;
@ -153,7 +158,11 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
auto cmd_iter = sql_cmd_map->find(args[0]);
if (cmd_iter != sql_cmd_map->end()) {
return cmd_iter->second->c_func(ec, stmt_str, args);
ec.ec_current_help = &cmd_iter->second->c_help;
auto retval = cmd_iter->second->c_func(ec, stmt_str, args);
ec.ec_current_help = nullptr;
return retval;
}
}
@ -163,9 +172,12 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
ec.ec_accumulator->clear();
std::pair<std::string, int> source = ec.ec_source.top();
sql_progress_guard progress_guard(
sql_progress, sql_progress_finished, source.first, source.second);
auto source = ec.ec_source.top();
sql_progress_guard progress_guard(sql_progress,
sql_progress_finished,
source.s_source,
source.s_line,
source.s_content);
gettimeofday(&start_tv, nullptr);
retcode = sqlite3_prepare_v2(
lnav_data.ld_db.in(), stmt_str.c_str(), -1, stmt.out(), nullptr);
@ -174,7 +186,8 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
alt_msg = "";
return ec.make_error("{}", errmsg);
} else if (stmt == nullptr) {
}
if (stmt == nullptr) {
alt_msg = "";
return ec.make_error("No statement given");
}
@ -301,8 +314,11 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
log_error("sqlite3_step error code: %d", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
if (startswith(errmsg, "lnav-error:")) {
return Err(lnav::from_json<lnav::console::user_message>(
&errmsg[11]));
}
return ec.make_error("{}", errmsg);
break;
}
}
}
@ -409,7 +425,7 @@ execute_sql(exec_context& ec, const std::string& sql, std::string& alt_msg)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
execute_file_contents(exec_context& ec,
const ghc::filesystem::path& path,
bool multiline)
@ -490,7 +506,7 @@ execute_file_contents(exec_context& ec,
return Ok(retval);
}
Result<std::string, std::string>
Result<std::string, lnav::console::user_message>
execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
{
available_scripts scripts;
@ -583,7 +599,7 @@ execute_file(exec_context& ec, const std::string& path_and_args, bool multiline)
return Ok(retval);
}
Result<std::string, std::string>
Result<std::string, lnav::console::user_message>
execute_from_file(exec_context& ec,
const ghc::filesystem::path& path,
int line_number,
@ -591,7 +607,7 @@ execute_from_file(exec_context& ec,
const std::string& cmdline)
{
std::string retval, alt_msg;
auto _sg = ec.enter_source(path.string(), line_number);
auto _sg = ec.enter_source(path.string(), line_number, cmdline);
switch (mode) {
case ':':
@ -621,7 +637,7 @@ execute_from_file(exec_context& ec,
return Ok(retval);
}
Result<std::string, std::string>
Result<std::string, lnav::console::user_message>
execute_any(exec_context& ec, const std::string& cmdline_with_mode)
{
std::string retval, alt_msg, cmdline = cmdline_with_mode.substr(1);
@ -663,7 +679,8 @@ execute_any(exec_context& ec, const std::string& cmdline_with_mode)
void
execute_init_commands(
exec_context& ec,
std::vector<std::pair<Result<std::string, std::string>, std::string>>& msgs)
std::vector<std::pair<Result<std::string, lnav::console::user_message>,
std::string>>& msgs)
{
if (lnav_data.ld_cmd_init_done) {
return;
@ -678,29 +695,30 @@ execute_init_commands(
wait_for_children();
ec.ec_source.emplace("command-option", option_index++);
switch (cmd.at(0)) {
case ':':
msgs.emplace_back(execute_command(ec, cmd.substr(1)), alt_msg);
break;
case '/':
lnav_data.ld_view_stack.top() |
[cmd](auto tc) { tc->execute_search(cmd.substr(1)); };
break;
case ';':
setup_logline_table(ec);
msgs.emplace_back(execute_sql(ec, cmd.substr(1), alt_msg),
alt_msg);
break;
case '|':
msgs.emplace_back(execute_file(ec, cmd.substr(1)), alt_msg);
break;
}
rescan_files();
rebuild_indexes_repeatedly();
{
auto _sg = ec.enter_source("command-option", option_index++, cmd);
switch (cmd.at(0)) {
case ':':
msgs.emplace_back(execute_command(ec, cmd.substr(1)),
alt_msg);
break;
case '/':
lnav_data.ld_view_stack.top() |
[cmd](auto tc) { tc->execute_search(cmd.substr(1)); };
break;
case ';':
setup_logline_table(ec);
msgs.emplace_back(execute_sql(ec, cmd.substr(1), alt_msg),
alt_msg);
break;
case '|':
msgs.emplace_back(execute_file(ec, cmd.substr(1)), alt_msg);
break;
}
ec.ec_source.pop();
rescan_files();
rebuild_indexes_repeatedly();
}
}
lnav_data.ld_commands.clear();
@ -854,19 +872,6 @@ add_global_vars(exec_context& ec)
}
}
std::string
exec_context::get_error_prefix()
{
if (this->ec_source.size() <= 1) {
return "error: ";
}
std::pair<std::string, int> source = this->ec_source.top();
return fmt::format(
FMT_STRING("{}:{}: error: "), source.first, source.second);
}
void
exec_context::set_output(const std::string& name,
FILE* file,
@ -903,10 +908,28 @@ exec_context::exec_context(std::vector<logline_value>* line_values,
{
this->ec_local_vars.push(std::map<std::string, std::string>());
this->ec_path_stack.emplace_back(".");
this->ec_source.emplace("command", 1);
this->ec_source.emplace(
lnav::console::snippet::from("command", "").with_line(1));
this->ec_output_stack.emplace_back("screen", nonstd::nullopt);
}
void
exec_context::add_error_context(lnav::console::user_message& um)
{
if (!this->ec_source.empty()) {
auto source = this->ec_source.top();
um.with_snippet(source);
}
if (this->ec_current_help != nullptr) {
attr_line_t help;
format_help_text_for_term(*this->ec_current_help, -1, help, true);
um.with_help(help);
}
}
exec_context::output_guard::output_guard(exec_context& context,
std::string name,
const nonstd::optional<output_t>& file)

@ -37,9 +37,11 @@
#include <sqlite3.h>
#include "base/auto_fd.hh"
#include "base/lnav.console.hh"
#include "bookmarks.hh"
#include "fmt/format.h"
#include "ghc/filesystem.hpp"
#include "help_text.hh"
#include "optional.hpp"
#include "shlex.resolver.hh"
#include "vis_line.hh"
@ -82,14 +84,18 @@ struct exec_context {
return *this;
}
std::string get_error_prefix();
void add_error_context(lnav::console::user_message& um);
template<typename... Args>
Result<std::string, std::string> make_error(fmt::string_view format_str,
const Args&... args)
Result<std::string, lnav::console::user_message> make_error(
fmt::string_view format_str, const Args&... args)
{
return Err(this->get_error_prefix()
+ fmt::vformat(format_str, fmt::make_format_args(args...)));
auto retval = lnav::console::user_message::error(
fmt::vformat(format_str, fmt::make_format_args(args...)));
this->add_error_context(retval);
return Err(retval);
}
nonstd::optional<FILE*> get_output()
@ -132,9 +138,12 @@ struct exec_context {
exec_context& sg_context;
};
source_guard enter_source(const std::string& path, int line_number)
source_guard enter_source(const std::string& path,
int line_number,
const std::string& content)
{
this->ec_source.emplace(path, line_number);
this->ec_source.emplace(
lnav::console::snippet::from(path, content).with_line(line_number));
return {*this};
}
@ -155,7 +164,8 @@ struct exec_context {
std::stack<std::map<std::string, std::string>> ec_local_vars;
std::map<std::string, std::string> ec_global_vars;
std::vector<ghc::filesystem::path> ec_path_stack;
std::stack<std::pair<std::string, int>> ec_source;
std::stack<lnav::console::snippet> ec_source;
help_text* ec_current_help{nullptr};
std::vector<std::pair<std::string, nonstd::optional<output_t>>>
ec_output_stack;
@ -166,21 +176,19 @@ struct exec_context {
pipe_callback_t ec_pipe_callback;
};
Result<std::string, std::string> execute_command(exec_context& ec,
const std::string& cmdline);
Result<std::string, std::string> execute_sql(exec_context& ec,
const std::string& sql,
std::string& alt_msg);
Result<std::string, std::string> execute_file(exec_context& ec,
const std::string& path_and_args,
bool multiline = true);
Result<std::string, std::string> execute_any(exec_context& ec,
const std::string& cmdline);
Result<std::string, lnav::console::user_message> execute_command(
exec_context& ec, const std::string& cmdline);
Result<std::string, lnav::console::user_message> execute_sql(
exec_context& ec, const std::string& sql, std::string& alt_msg);
Result<std::string, lnav::console::user_message> execute_file(
exec_context& ec, const std::string& path_and_args, bool multiline = true);
Result<std::string, lnav::console::user_message> execute_any(
exec_context& ec, const std::string& cmdline);
void execute_init_commands(
exec_context& ec,
std::vector<std::pair<Result<std::string, std::string>, std::string>>&
msgs);
std::vector<std::pair<Result<std::string, lnav::console::user_message>,
std::string>>& msgs);
std::future<std::string> pipe_callback(exec_context& ec,
const std::string& cmdline,

@ -32,8 +32,6 @@
#ifndef curl_looper_hh
#define curl_looper_hh
#include "config.h"
#include <atomic>
#include <map>
#include <string>
@ -41,6 +39,7 @@
#include <vector>
#include "base/isc.hh"
#include "config.h"
#if !defined(HAVE_LIBCURL)
@ -67,7 +66,7 @@ public:
# include <curl/curl.h>
# include "auto_mem.hh"
# include "base/auto_mem.hh"
# include "base/lnav_log.hh"
# include "base/time_util.hh"

@ -112,11 +112,11 @@ db_label_source::text_attrs_for_line(textview_curses& tc,
}
for (size_t lpc = 0; lpc < this->dls_headers.size() - 1; lpc++) {
if (row % 2 == 0) {
sa.emplace_back(lr2, view_curses::VC_STYLE.value(A_BOLD));
sa.emplace_back(lr2, VC_STYLE.value(A_BOLD));
}
lr.lr_start += this->dls_cell_width[lpc];
lr.lr_end = lr.lr_start + 1;
sa.emplace_back(lr, view_curses::VC_GRAPHIC.value(ACS_VLINE));
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_VLINE));
lr.lr_start += 1;
}
@ -344,10 +344,10 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
string_attrs_t& sa = this->dos_lines.back().get_attrs();
struct line_range lr(1, 2);
sa.emplace_back(lr, view_curses::VC_GRAPHIC.value(ACS_LTEE));
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LTEE));
lr.lr_start = 3 + jpw_value.wt_ptr.size() + 3;
lr.lr_end = -1;
sa.emplace_back(lr, view_curses::VC_STYLE.value(A_BOLD));
sa.emplace_back(lr, VC_STYLE.value(A_BOLD));
double num_value = 0.0;
@ -390,10 +390,10 @@ db_overlay_source::list_overlay_count(const listview_curses& lv)
string_attrs_t& sa = this->dos_lines.back().get_attrs();
struct line_range lr(1, 2);
sa.emplace_back(lr, view_curses::VC_GRAPHIC.value(ACS_LLCORNER));
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_LLCORNER));
lr.lr_start = 2;
lr.lr_end = -1;
sa.emplace_back(lr, view_curses::VC_GRAPHIC.value(ACS_HLINE));
sa.emplace_back(lr, VC_GRAPHIC.value(ACS_HLINE));
retval += 1;
}
@ -443,12 +443,12 @@ db_overlay_source::list_value_for_overlay(const listview_curses& lv,
if (!this->dos_labels->dls_headers[lpc].hm_graphable) {
attrs = A_UNDERLINE;
}
sa.emplace_back(header_range, view_curses::VC_STYLE.value(attrs));
sa.emplace_back(header_range, VC_STYLE.value(attrs));
}
struct line_range lr(0);
sa.emplace_back(lr, view_curses::VC_STYLE.value(A_BOLD | A_UNDERLINE));
sa.emplace_back(lr, VC_STYLE.value(A_BOLD | A_UNDERLINE));
return true;
} else if (this->dos_active && y >= 2
&& ((size_t) y) < (this->dos_lines.size() + 2))

@ -48,13 +48,13 @@ public:
{
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_left_pad(1);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_DESCRIPTION].set_share(1);
this->tss_fields[TSF_DESCRIPTION].set_role(view_colors::VCR_STATUS);
this->tss_fields[TSF_DESCRIPTION].set_role(role_t::VCR_STATUS);
};
size_t statusview_fields() override

@ -32,7 +32,7 @@
#include <stdlib.h>
#include <string.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/lnav_log.hh"
#include "config.h"

@ -29,10 +29,9 @@
#include "field_overlay_source.hh"
#include "ansi_scrubber.hh"
#include "base/ansi_scrubber.hh"
#include "base/humanize.time.hh"
#include "config.h"
#include "lnav_util.hh"
#include "log_format_ext.hh"
#include "log_vtab_impl.hh"
#include "readline_highlighters.hh"
@ -145,7 +144,7 @@ field_overlay_source::build_summary_lines(const listview_curses& lv)
"%'.2lf") "/min; "
"Time span: " ANSI_BOLD("%s"),
lss.file_count(),
view_colors::VCR_ERROR,
role_t::VCR_ERROR,
error_rate,
time_span.c_str());
} else {
@ -158,7 +157,7 @@ field_overlay_source::build_summary_lines(const listview_curses& lv)
"Time span: " ANSI_BOLD("%s"),
lss.file_count(),
tss.size(),
view_colors::VCR_ERROR,
role_t::VCR_ERROR,
error_rate,
time_span.c_str());
}
@ -167,18 +166,18 @@ field_overlay_source::build_summary_lines(const listview_curses& lv)
.with_attr(string_attr(
line_range(sum_msg.find("Error rate"),
sum_msg.find("Error rate") + rate_len),
view_curses::VC_STYLE.value(A_REVERSE)))
VC_STYLE.value(A_REVERSE)))
.with_attr(
string_attr(line_range(1, 2),
view_curses::VC_GRAPHIC.value(ACS_ULCORNER)))
VC_GRAPHIC.value(ACS_ULCORNER)))
.with_attr(string_attr(
line_range(2, 6), view_curses::VC_GRAPHIC.value(ACS_HLINE)))
line_range(2, 6), VC_GRAPHIC.value(ACS_HLINE)))
.with_attr(string_attr(
line_range(sum_msg.length() + 1, sum_msg.length() + 5),
view_curses::VC_GRAPHIC.value(ACS_HLINE)))
VC_GRAPHIC.value(ACS_HLINE)))
.with_attr(string_attr(
line_range(sum_msg.length() + 5, sum_msg.length() + 6),
view_curses::VC_GRAPHIC.value(ACS_URCORNER)))
VC_GRAPHIC.value(ACS_URCORNER)))
.right_justify(width - 2);
}
}
@ -234,11 +233,11 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
auto al = attr_line_t(emsg)
.with_attr(string_attr(
line_range{1, 2},
view_curses::VC_GRAPHIC.value(ACS_LLCORNER)))
VC_GRAPHIC.value(ACS_LLCORNER)))
.with_attr(
string_attr(line_range{0, 22},
view_curses::VC_ROLE.value(
view_colors::VCR_INVALID_MSG)));
VC_ROLE.value(
role_t::VCR_INVALID_MSG)));
this->fos_lines.emplace_back(al);
}
}
@ -259,12 +258,12 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
time_lr.lr_start = 1;
time_lr.lr_end = 2;
time_line.with_attr(
string_attr(time_lr, view_curses::VC_GRAPHIC.value(ACS_LLCORNER)));
string_attr(time_lr, VC_GRAPHIC.value(ACS_LLCORNER)));
time_str.append(" Out-Of-Time-Order Message");
time_lr.lr_start = 3;
time_lr.lr_end = time_str.length();
time_line.with_attr(string_attr(
time_lr, view_curses::VC_ROLE.value(view_colors::VCR_SKEWED_TIME)));
time_lr, VC_ROLE.value(role_t::VCR_SKEWED_TIME)));
time_str.append(" --");
}
@ -273,7 +272,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
time_str.append(curr_timestamp);
time_lr.lr_end = time_str.length();
time_line.with_attr(
string_attr(time_lr, view_curses::VC_STYLE.value(A_BOLD)));
string_attr(time_lr, VC_STYLE.value(A_BOLD)));
time_str.append(" -- ");
time_lr.lr_start = time_str.length();
time_str.append(humanize::time::point::from_tv(ll->get_timeval())
@ -281,7 +280,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
.as_precise_time_ago());
time_lr.lr_end = time_str.length();
time_line.with_attr(
string_attr(time_lr, view_curses::VC_STYLE.value(A_BOLD)));
string_attr(time_lr, VC_STYLE.value(A_BOLD)));
struct line_range time_range = find_string_attr_range(
this->fos_log_helper.ldh_line_attrs, &logline::L_TIMESTAMP);
@ -312,7 +311,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
time_lr.lr_end = time_str.length();
time_line.with_attr(string_attr(
time_lr,
view_curses::VC_ROLE.value(view_colors::VCR_SKEWED_TIME)));
VC_ROLE.value(role_t::VCR_SKEWED_TIME)));
timersub(&curr_tv, &actual_tv, &diff_tv);
time_str.append("; Diff: ");
@ -321,7 +320,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
humanize::time::duration::from_tv(diff_tv).to_string());
time_lr.lr_end = time_str.length();
time_line.with_attr(
string_attr(time_lr, view_curses::VC_STYLE.value(A_BOLD)));
string_attr(time_lr, VC_STYLE.value(A_BOLD)));
}
}
@ -419,7 +418,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
+ format_name + ":");
this->fos_lines.back().with_attr(string_attr(
line_range(32, 32 + format_name.length()),
view_curses::VC_STYLE.value(vc.attrs_for_ident(format_name)
VC_STYLE.value(vc.attrs_for_ident(format_name)
| A_BOLD)));
last_format = curr_format;
}
@ -455,12 +454,12 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
auto prefix_len = field_name.length() - orig_field_name.length();
al.with_attr(string_attr(
line_range(3 + prefix_len, 3 + prefix_len + field_name.size()),
view_curses::VC_STYLE.value(
VC_STYLE.value(
vc.attrs_for_ident(orig_field_name))));
} else {
al.with_attr(string_attr(
line_range(8, 8 + lv.lv_meta.lvm_struct_name.size()),
view_curses::VC_STYLE.value(
VC_STYLE.value(
vc.attrs_for_ident(lv.lv_meta.lvm_struct_name))));
}
@ -473,7 +472,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
al.clear()
.append(" extract(")
.append(lv.lv_meta.lvm_name.get(),
view_curses::VC_STYLE.value(
VC_STYLE.value(
vc.attrs_for_ident(lv.lv_meta.lvm_name)))
.append(")")
.append(this->fos_known_key_size - lv.lv_meta.lvm_name.size()
@ -536,12 +535,12 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
" Discovered fields for logline table from message format: ");
this->fos_lines.back().with_attr(string_attr(
line_range(23, 23 + 7),
view_curses::VC_STYLE.value(vc.attrs_for_ident("logline"))));
VC_STYLE.value(vc.attrs_for_ident("logline"))));
auto& al = this->fos_lines.back();
auto& disc_str = al.get_string();
al.with_attr(string_attr(line_range(disc_str.length(), -1),
view_curses::VC_STYLE.value(A_BOLD)));
VC_STYLE.value(A_BOLD)));
disc_str.append(this->fos_log_helper.ldh_msg_format);
}
@ -556,7 +555,7 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
al.with_attr(
string_attr(line_range(3, 3 + name.length()),
view_curses::VC_STYLE.value(vc.attrs_for_ident(name))));
VC_STYLE.value(vc.attrs_for_ident(name))));
this->fos_lines.emplace_back(al);
this->add_key_line_attrs(
@ -585,7 +584,7 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
al.with_string(" + ")
.with_attr(string_attr(
line_range(1, 2),
view_curses::VC_GRAPHIC.value(
VC_GRAPHIC.value(
line_meta.bm_tags.empty() ? ACS_LLCORNER : ACS_LTEE)))
.append(line_meta.bm_comment);
al.insert(0, filename_width, ' ');
@ -595,10 +594,10 @@ field_overlay_source::build_meta_line(const listview_curses& lv,
attr_line_t al;
al.with_string(" +").with_attr(string_attr(
line_range(1, 2), view_curses::VC_GRAPHIC.value(ACS_LLCORNER)));
line_range(1, 2), VC_GRAPHIC.value(ACS_LLCORNER)));
for (const auto& str : line_meta.bm_tags) {
al.append(1, ' ').append(
str, view_curses::VC_STYLE.value(vc.attrs_for_ident(str)));
str, VC_STYLE.value(vc.attrs_for_ident(str)));
}
const auto* tc = dynamic_cast<const textview_curses*>(&lv);

@ -51,11 +51,11 @@ public:
string_attrs_t& sa = this->fos_lines.back().get_attrs();
struct line_range lr(1, 2);
int64_t graphic = (int64_t) (last_line ? ACS_LLCORNER : ACS_LTEE);
sa.emplace_back(lr, view_curses::VC_GRAPHIC.value(graphic));
sa.emplace_back(lr, VC_GRAPHIC.value(graphic));
lr.lr_start = 3 + key_size + 3;
lr.lr_end = -1;
sa.emplace_back(lr, view_curses::VC_STYLE.value(A_BOLD));
sa.emplace_back(lr, VC_STYLE.value(A_BOLD));
};
bool list_value_for_overlay(const listview_curses& lv,

@ -38,6 +38,7 @@
#include "base/humanize.network.hh"
#include "base/isc.hh"
#include "base/opt_util.hh"
#include "base/string_util.hh"
#include "config.h"
#include "lnav_util.hh"
#include "logfile.hh"

@ -29,6 +29,7 @@
#include "files_sub_source.hh"
#include "base/ansi_scrubber.hh"
#include "base/humanize.hh"
#include "base/humanize.network.hh"
#include "base/opt_util.hh"
@ -284,7 +285,8 @@ files_sub_source::text_value_for_line(textview_curses& tc,
value_out = fmt::format(FMT_STRING(" {:<{}} {:>8} {} \u2014 {} {}"),
fn,
filename_width,
humanize::file_size(lf->get_index_size()),
humanize::file_size(lf->get_index_size(),
humanize::alignment::columnar),
start_time,
end_time,
fmt::join(file_notes, "; "));
@ -308,19 +310,19 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
if (selected) {
value_out.emplace_back(line_range{0, 1},
view_curses::VC_GRAPHIC.value(ACS_RARROW));
VC_GRAPHIC.value(ACS_RARROW));
}
if (line < fc.fc_name_to_errors.size()) {
if (selected) {
value_out.emplace_back(
line_range{0, -1},
view_curses::VC_ROLE.value(view_colors::VCR_DISABLED_FOCUSED));
VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
}
value_out.emplace_back(
line_range{4 + (int) filename_width, -1},
view_curses::VC_ROLE_FG.value(view_colors::VCR_ERROR));
VC_ROLE_FG.value(role_t::VCR_ERROR));
return;
}
line -= fc.fc_name_to_errors.size();
@ -329,11 +331,11 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
if (selected) {
value_out.emplace_back(
line_range{0, -1},
view_curses::VC_ROLE.value(view_colors::VCR_DISABLED_FOCUSED));
VC_ROLE.value(role_t::VCR_DISABLED_FOCUSED));
}
if (line == fc.fc_other_files.size() - 1) {
value_out.emplace_back(line_range{0, -1},
view_curses::VC_STYLE.value(A_UNDERLINE));
VC_STYLE.value(A_UNDERLINE));
}
return;
}
@ -343,7 +345,7 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
if (selected) {
value_out.emplace_back(
line_range{0, -1},
view_curses::VC_ROLE.value(view_colors::VCR_FOCUSED));
VC_ROLE.value(role_t::VCR_FOCUSED));
}
auto& lss = lnav_data.ld_log_source;
@ -355,10 +357,10 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
visible = ' ';
}
value_out.emplace_back(line_range{2, 3},
view_curses::VC_GRAPHIC.value(visible));
VC_GRAPHIC.value(visible));
if (visible == ACS_DIAMOND) {
value_out.emplace_back(line_range{2, 3},
view_curses::VC_FOREGROUND.value(
VC_FOREGROUND.value(
vcolors.ansi_to_theme_color(COLOR_GREEN)));
}
@ -366,12 +368,12 @@ files_sub_source::text_attrs_for_line(textview_curses& tc,
(int) filename_width + 3 + 4,
(int) filename_width + 3 + 10,
};
value_out.emplace_back(lr, view_curses::VC_STYLE.value(A_BOLD));
value_out.emplace_back(lr, VC_STYLE.value(A_BOLD));
lr.lr_start = this->fss_last_line_len;
lr.lr_end = -1;
value_out.emplace_back(lr,
view_curses::VC_FOREGROUND.value(
VC_FOREGROUND.value(
vcolors.ansi_to_theme_color(COLOR_YELLOW)));
}
@ -417,8 +419,10 @@ files_overlay_source::list_value_for_overlay(const listview_curses& lv,
"... {:>8}/{}",
PROG[spinner_index() % PROG_SIZE],
prog.ep_path.filename().string(),
humanize::file_size(prog.ep_out_size),
humanize::file_size(prog.ep_total_size)));
humanize::file_size(prog.ep_out_size,
humanize::alignment::none),
humanize::file_size(prog.ep_total_size,
humanize::alignment::none)));
return true;
}
if (!sp->sp_tailers.empty()) {

@ -29,6 +29,7 @@
#include "filter_status_source.hh"
#include "base/ansi_scrubber.hh"
#include "base/opt_util.hh"
#include "config.h"
#include "files_sub_source.hh"
@ -49,32 +50,32 @@ static auto CLOSE_HELP = ANSI_BOLD("X") ": Close";
filter_status_source::filter_status_source()
{
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_value(" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_TITLE_HOTKEY);
role_t::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_COUNT].set_min_width(16);
this->tss_fields[TSF_COUNT].set_share(1);
this->tss_fields[TSF_COUNT].set_role(view_colors::VCR_STATUS);
this->tss_fields[TSF_COUNT].set_role(role_t::VCR_STATUS);
this->tss_fields[TSF_FILTERED].set_min_width(20);
this->tss_fields[TSF_FILTERED].set_share(1);
this->tss_fields[TSF_FILTERED].set_role(view_colors::VCR_STATUS);
this->tss_fields[TSF_FILTERED].set_role(role_t::VCR_STATUS);
this->tss_fields[TSF_FILES_TITLE].set_width(7);
this->tss_fields[TSF_FILES_TITLE].set_role(
view_colors::VCR_STATUS_DISABLED_TITLE);
role_t::VCR_STATUS_DISABLED_TITLE);
this->tss_fields[TSF_FILES_TITLE].set_value(" " ANSI_ROLE("F") "iles ",
view_colors::VCR_STATUS_HOTKEY);
role_t::VCR_STATUS_HOTKEY);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_width(2);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS, view_colors::VCR_STATUS);
role_t::VCR_STATUS, role_t::VCR_STATUS);
this->tss_fields[TSF_HELP].right_justify(true);
this->tss_fields[TSF_HELP].set_width(20);
@ -83,7 +84,7 @@ filter_status_source::filter_status_source()
this->tss_error.set_min_width(20);
this->tss_error.set_share(1);
this->tss_error.set_role(view_colors::VCR_ALERT_STATUS);
this->tss_error.set_role(role_t::VCR_ALERT_STATUS);
}
size_t
@ -106,27 +107,27 @@ filter_status_source::statusview_fields()
if (lnav_data.ld_mode == LNM_FILES || lnav_data.ld_mode == LNM_SEARCH_FILES)
{
this->tss_fields[TSF_FILES_TITLE].set_value(
" " ANSI_ROLE("F") "iles ", view_colors::VCR_STATUS_TITLE_HOTKEY);
" " ANSI_ROLE("F") "iles ", role_t::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_FILES_TITLE].set_role(
view_colors::VCR_STATUS_TITLE);
role_t::VCR_STATUS_TITLE);
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_TITLE].set_value(" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_HOTKEY);
role_t::VCR_STATUS_HOTKEY);
this->tss_fields[TSF_TITLE].set_role(
view_colors::VCR_STATUS_DISABLED_TITLE);
role_t::VCR_STATUS_DISABLED_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS, view_colors::VCR_STATUS);
role_t::VCR_STATUS, role_t::VCR_STATUS);
} else {
this->tss_fields[TSF_FILES_TITLE].set_value(
" " ANSI_ROLE("F") "iles ", view_colors::VCR_STATUS_HOTKEY);
" " ANSI_ROLE("F") "iles ", role_t::VCR_STATUS_HOTKEY);
if (lnav_data.ld_active_files.fc_name_to_errors.empty()) {
this->tss_fields[TSF_FILES_TITLE].set_role(
view_colors::VCR_STATUS_DISABLED_TITLE);
role_t::VCR_STATUS_DISABLED_TITLE);
} else {
this->tss_fields[TSF_FILES_TITLE].set_role(
view_colors::VCR_ALERT_STATUS);
role_t::VCR_ALERT_STATUS);
auto& fc = lnav_data.ld_active_files;
if (fc.fc_name_to_errors.size() == 1) {
@ -138,15 +139,15 @@ filter_status_source::statusview_fields()
}
}
this->tss_fields[TSF_FILES_RIGHT_STITCH].set_stitch_value(
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE,
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL);
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE,
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL);
this->tss_fields[TSF_TITLE].set_value(
" " ANSI_ROLE("T") "ext Filters ",
view_colors::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
role_t::VCR_STATUS_TITLE_HOTKEY);
this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
}
lnav_data.ld_view_stack.top() | [this](auto tc) {
@ -209,13 +210,13 @@ filter_status_source::update_filtered(text_sub_source* tss)
if (tss->get_filtered_count() == this->bss_last_filtered_count) {
if (timer.fade_diff(this->bss_filter_counter) == 0) {
this->tss_fields[TSF_FILTERED].set_role(
view_colors::VCR_STATUS);
role_t::VCR_STATUS);
al.with_attr(string_attr(line_range{0, -1},
view_curses::VC_STYLE.value(A_BOLD)));
VC_STYLE.value(A_BOLD)));
}
} else {
this->tss_fields[TSF_FILTERED].set_role(
view_colors::VCR_ALERT_STATUS);
role_t::VCR_ALERT_STATUS);
this->bss_last_filtered_count = tss->get_filtered_count();
timer.start_fade(this->bss_filter_counter, 3);
}

@ -72,8 +72,8 @@ public:
status_field& statusview_value_for_field(int field) override;
status_field fss_prompt{1024, view_colors::VCR_STATUS};
status_field fss_error{1024, view_colors::VCR_ALERT_STATUS};
status_field fss_prompt{1024, role_t::VCR_STATUS};
status_field fss_error{1024, role_t::VCR_ALERT_STATUS};
private:
status_field fss_help;

@ -61,7 +61,7 @@ filter_sub_source::filter_sub_source()
this->fss_match_view.set_sub_source(&this->fss_match_source);
this->fss_match_view.set_height(0_vl);
this->fss_match_view.set_show_scrollbar(true);
this->fss_match_view.set_default_role(view_colors::VCR_POPUP);
this->fss_match_view.set_default_role(role_t::VCR_POPUP);
}
bool
@ -353,36 +353,35 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc,
if (selected) {
value_out.emplace_back(line_range{0, 1},
view_curses::VC_GRAPHIC.value(ACS_RARROW));
VC_GRAPHIC.value(ACS_RARROW));
}
chtype enabled = tf->is_enabled() ? ACS_DIAMOND : ' ';
line_range lr{2, 3};
value_out.emplace_back(lr, view_curses::VC_GRAPHIC.value(enabled));
value_out.emplace_back(lr, VC_GRAPHIC.value(enabled));
if (tf->is_enabled()) {
value_out.emplace_back(lr,
view_curses::VC_FOREGROUND.value(
VC_FOREGROUND.value(
vcolors.ansi_to_theme_color(COLOR_GREEN)));
}
int fg_role = tf->get_type() == text_filter::INCLUDE
? view_colors::VCR_OK
: view_colors::VCR_ERROR;
role_t fg_role = tf->get_type() == text_filter::INCLUDE ? role_t::VCR_OK
: role_t::VCR_ERROR;
value_out.emplace_back(line_range{4, 7},
view_curses::VC_ROLE_FG.value(fg_role));
VC_ROLE_FG.value(fg_role));
value_out.emplace_back(line_range{4, 7},
view_curses::VC_STYLE.value(A_BOLD));
VC_STYLE.value(A_BOLD));
value_out.emplace_back(line_range{8, 17},
view_curses::VC_STYLE.value(A_BOLD));
VC_STYLE.value(A_BOLD));
value_out.emplace_back(line_range{23, 24},
view_curses::VC_GRAPHIC.value(ACS_VLINE));
VC_GRAPHIC.value(ACS_VLINE));
if (selected) {
value_out.emplace_back(
line_range{0, -1},
view_curses::VC_ROLE.value(view_colors::VCR_FOCUSED));
VC_ROLE.value(role_t::VCR_FOCUSED));
}
attr_line_t content{tf->get_id()};
@ -624,7 +623,7 @@ filter_sub_source::rl_display_matches(readline_curses* rc)
for (auto& match : matches) {
if (match == current_match) {
al.append(match, view_curses::VC_STYLE.value(A_REVERSE));
al.append(match, VC_STYLE.value(A_REVERSE));
selected_line = line;
} else {
al.append(match);

@ -35,6 +35,7 @@
#include <string.h>
#include <sys/stat.h>
#include "base/auto_mem.hh"
#include "base/injector.hh"
#include "base/lnav_log.hh"
#include "config.h"

@ -32,18 +32,18 @@
#ifndef grep_proc_hh
#define grep_proc_hh
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <deque>
#include <exception>
#include <string>
#include <vector>
#include <poll.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "base/auto_fd.hh"
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/lnav_log.hh"
#include "line_buffer.hh"
#include "pcrepp/pcrepp.hh"

@ -32,13 +32,15 @@
#include "help_text_formatter.hh"
#include "ansi_scrubber.hh"
#include "base/ansi_scrubber.hh"
#include "base/string_util.hh"
#include "config.h"
#include "fmt/format.h"
#include "fmt/printf.h"
#include "readline_highlighters.hh"
using namespace lnav::roles::literals;
std::multimap<std::string, help_text*> help_text::TAGGED;
static std::vector<help_text*>
@ -83,7 +85,6 @@ format_help_text_for_term(const help_text& ht,
{
static const size_t body_indent = 2;
view_colors& vc = view_colors::singleton();
text_wrap_settings tws;
size_t start_index = out.get_string().length();
@ -91,29 +92,27 @@ format_help_text_for_term(const help_text& ht,
switch (ht.ht_context) {
case help_context_t::HC_COMMAND: {
out.append("Synopsis", view_curses::VC_STYLE.value(A_UNDERLINE))
out.append("Synopsis"_h2)
.append("\n")
.append(body_indent, ' ')
.append(":")
.append(ht.ht_name, view_curses::VC_STYLE.value(A_BOLD));
.append(lnav::roles::symbol(ht.ht_name));
for (const auto& param : ht.ht_parameters) {
out.append(" ");
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("[");
}
out.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(param.ht_name));
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("]");
}
if (param.ht_nargs == help_nargs_t::HN_ONE_OR_MORE) {
out.append("1", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("1"_variable);
out.append(" [");
out.append("...", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("..."_variable);
out.append(" ");
out.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("N", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(param.ht_name));
out.append("N"_variable);
out.append("]");
}
}
@ -130,13 +129,12 @@ format_help_text_for_term(const help_text& ht,
bool needs_comma = false;
if (!synopsis_only) {
out.append("Synopsis", view_curses::VC_STYLE.value(A_UNDERLINE))
.append("\n");
out.append("Synopsis"_h2).append("\n");
}
line_start = out.length();
out.append(body_indent, ' ')
.append(ht.ht_name, view_curses::VC_STYLE.value(A_BOLD))
.append(lnav::roles::symbol(ht.ht_name))
.append("(");
for (const auto& param : ht.ht_parameters) {
if (!param.ht_flag_name && needs_comma) {
@ -153,15 +151,13 @@ format_help_text_for_term(const help_text& ht,
}
if (param.ht_flag_name) {
out.append(" ")
.append(param.ht_flag_name,
view_curses::VC_STYLE.value(A_BOLD))
.append(lnav::roles::symbol(param.ht_flag_name))
.append(" ");
}
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("[");
}
out.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(param.ht_name));
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("]");
}
@ -183,29 +179,27 @@ format_help_text_for_term(const help_text& ht,
break;
}
case help_context_t::HC_SQL_COMMAND: {
out.append("Synopsis", view_curses::VC_STYLE.value(A_UNDERLINE))
out.append("Synopsis"_h2)
.append("\n")
.append(body_indent, ' ')
.append(";")
.append(ht.ht_name, view_curses::VC_STYLE.value(A_BOLD));
.append(lnav::roles::symbol(ht.ht_name));
for (const auto& param : ht.ht_parameters) {
out.append(" ");
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("[");
}
out.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(param.ht_name));
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("]");
}
if (param.ht_nargs == help_nargs_t::HN_ONE_OR_MORE) {
out.append("1", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("1"_variable);
out.append(" [");
out.append("...", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("..."_variable);
out.append(" ");
out.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("N", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(param.ht_name));
out.append("N"_variable);
out.append("]");
}
}
@ -222,12 +216,14 @@ format_help_text_for_term(const help_text& ht,
bool is_infix = ht.ht_context == help_context_t::HC_SQL_INFIX;
if (!synopsis_only) {
out.append("Synopsis", view_curses::VC_STYLE.value(A_UNDERLINE))
.append("\n");
out.append("Synopsis"_h2).append("\n");
}
out.append(body_indent, ' ');
if (is_infix) {
out.append(ht.ht_name);
} else {
out.append(lnav::roles::keyword(ht.ht_name));
}
out.append(body_indent, ' ')
.append(ht.ht_name,
view_curses::VC_STYLE.value(is_infix ? 0 : A_BOLD));
for (const auto& param : ht.ht_parameters) {
if (break_all
|| (int) (out.get_string().length() - start_index
@ -249,33 +245,29 @@ format_help_text_for_term(const help_text& ht,
}
if (param.ht_flag_name) {
out.ensure_space().append(
param.ht_flag_name,
view_curses::VC_STYLE.value(A_BOLD));
lnav::roles::keyword(param.ht_flag_name));
}
if (param.ht_group_start) {
out.ensure_space().append(
param.ht_group_start,
view_curses::VC_STYLE.value(A_BOLD));
lnav::roles::keyword(param.ht_group_start));
}
if (param.ht_name[0]) {
out.ensure_space().append(
param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
lnav::roles::variable(param.ht_name));
if (!param.ht_parameters.empty()) {
if (param.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE
|| param.ht_nargs == help_nargs_t::HN_ONE_OR_MORE)
{
out.append(
"1", view_curses::VC_STYLE.value(A_UNDERLINE));
out.append("1"_variable);
}
if (param.ht_parameters[0].ht_flag_name) {
out.append(" ")
.append(param.ht_parameters[0].ht_flag_name,
view_curses::VC_STYLE.value(A_BOLD))
.append(lnav::roles::keyword(
param.ht_parameters[0].ht_flag_name))
.append(" ");
}
out.append(param.ht_parameters[0].ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(
param.ht_parameters[0].ht_name));
}
}
if (param.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE
@ -284,38 +276,35 @@ format_help_text_for_term(const help_text& ht,
bool needs_comma = param.ht_parameters.empty()
|| !param.ht_flag_name;
out.append("1", view_curses::VC_STYLE.value(A_UNDERLINE))
out.append("1"_variable)
.append(" [")
.append(needs_comma ? ", " : "")
.append("...")
.append(needs_comma ? "" : " ")
.append((needs_comma || !param.ht_flag_name)
? ""
: param.ht_flag_name,
view_curses::VC_STYLE.value(A_BOLD))
.append(lnav::roles::keyword(
(needs_comma || !param.ht_flag_name)
? ""
: param.ht_flag_name))
.append(" ")
.append(param.ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE))
.append("N", view_curses::VC_STYLE.value(A_UNDERLINE));
.append(lnav::roles::variable(param.ht_name))
.append("N"_variable);
if (!param.ht_parameters.empty()) {
if (param.ht_parameters[0].ht_flag_name) {
out.append(" ")
.append(param.ht_parameters[0].ht_flag_name,
view_curses::VC_STYLE.value(A_BOLD))
.append(lnav::roles::keyword(
param.ht_parameters[0].ht_flag_name))
.append(" ");
}
out.append(param.ht_parameters[0].ht_name,
view_curses::VC_STYLE.value(A_UNDERLINE))
.append("N",
view_curses::VC_STYLE.value(A_UNDERLINE));
out.append(lnav::roles::variable(
param.ht_parameters[0].ht_name))
.append("N"_variable);
}
out.append("]");
}
if (param.ht_group_end) {
out.ensure_space().append(
param.ht_group_end,
view_curses::VC_STYLE.value(A_BOLD));
lnav::roles::keyword(param.ht_group_end));
}
if (param.ht_nargs == help_nargs_t::HN_ZERO_OR_MORE
|| param.ht_nargs == help_nargs_t::HN_OPTIONAL)
@ -345,8 +334,8 @@ format_help_text_for_term(const help_text& ht,
= std::max(strlen(param.ht_name), max_param_name_width);
}
out.append(ht.ht_parameters.size() == 1 ? "Parameter" : "Parameters",
view_curses::VC_STYLE.value(A_UNDERLINE))
out.append(ht.ht_parameters.size() == 1 ? "Parameter"_h2
: "Parameters"_h2)
.append("\n");
for (const auto& param : ht.ht_parameters) {
@ -355,10 +344,7 @@ format_help_text_for_term(const help_text& ht,
}
out.append(body_indent, ' ')
.append(
param.ht_name,
view_curses::VC_STYLE.value(
vc.attrs_for_role(view_colors::VCR_VARIABLE) | A_BOLD))
.append(lnav::roles::variable(param.ht_name))
.append(max_param_name_width - strlen(param.ht_name), ' ')
.append(" ")
.append(attr_line_t::from_ansi_str(param.ht_summary),
@ -374,8 +360,7 @@ format_help_text_for_term(const help_text& ht,
= std::max(strlen(result.ht_name), max_result_name_width);
}
out.append(ht.ht_results.size() == 1 ? "Result" : "Results",
view_curses::VC_STYLE.value(A_UNDERLINE))
out.append(ht.ht_results.size() == 1 ? "Result"_h2 : "Results"_h2)
.append("\n");
for (const auto& result : ht.ht_results) {
@ -384,10 +369,7 @@ format_help_text_for_term(const help_text& ht,
}
out.append(body_indent, ' ')
.append(
result.ht_name,
view_curses::VC_STYLE.value(
vc.attrs_for_role(view_colors::VCR_VARIABLE) | A_BOLD))
.append(lnav::roles::variable(result.ht_name))
.append(max_result_name_width - strlen(result.ht_name), ' ')
.append(" ")
.append(attr_line_t::from_ansi_str(result.ht_summary),
@ -416,9 +398,7 @@ format_help_text_for_term(const help_text& ht,
}
stable_sort(related_refs.begin(), related_refs.end());
out.append("See Also", view_curses::VC_STYLE.value(A_UNDERLINE))
.append("\n")
.append(body_indent, ' ');
out.append("See Also"_h2).append("\n").append(body_indent, ' ');
bool first = true;
size_t line_start = out.length();
@ -430,7 +410,7 @@ format_help_text_for_term(const help_text& ht,
out.append("\n").append(body_indent, ' ');
line_start = out.length();
}
out.append(ref, view_curses::VC_STYLE.value(A_BOLD));
out.append(lnav::roles::symbol(ref));
first = false;
}
}
@ -449,8 +429,7 @@ format_example_text_for_term(const help_text& ht,
if (!ht.ht_example.empty()) {
int count = 1;
out.append(ht.ht_example.size() == 1 ? "Example" : "Examples",
view_curses::VC_STYLE.value(A_UNDERLINE))
out.append(ht.ht_example.size() == 1 ? "Example"_h2 : "Examples"_h2)
.append("\n");
for (const auto& ex : ht.ht_example) {
attr_line_t ex_line(ex.he_cmd);

@ -32,7 +32,7 @@
#include <functional>
#include "attr_line.hh"
#include "base/attr_line.hh"
#include "help_text.hh"
using help_example_to_attr_line_fun_t

@ -132,7 +132,7 @@ highlighter::annotate(attr_line_t& al, int start) const
if (lr.lr_end > lr.lr_start
&& (this->h_nestable
|| find_string_attr_containing(
sa, &view_curses::VC_STYLE, lr)
sa, &VC_STYLE, lr)
== sa.end()))
{
int attrs = 0;
@ -142,20 +142,20 @@ highlighter::annotate(attr_line_t& al, int start) const
}
if (!this->h_fg.empty()) {
sa.emplace_back(lr,
view_curses::VC_FOREGROUND.value(
VC_FOREGROUND.value(
vc.match_color(this->h_fg)));
}
if (!this->h_bg.empty()) {
sa.emplace_back(lr,
view_curses::VC_BACKGROUND.value(
VC_BACKGROUND.value(
vc.match_color(this->h_bg)));
}
if (this->h_role != view_colors::VCR_NONE) {
if (this->h_role != role_t::VCR_NONE) {
sa.emplace_back(lr,
view_curses::VC_ROLE.value(this->h_role));
VC_ROLE.value(this->h_role));
}
if (attrs) {
sa.emplace_back(lr, view_curses::VC_STYLE.value(attrs));
sa.emplace_back(lr, VC_STYLE.value(attrs));
}
off = matches[1];

@ -70,7 +70,7 @@ struct highlighter {
return *this;
}
highlighter& with_role(view_colors::role_t role)
highlighter& with_role(role_t role)
{
this->h_role = role;
@ -123,7 +123,7 @@ struct highlighter {
void annotate(attr_line_t& al, int start) const;
std::string h_pattern;
view_colors::role_t h_role{view_colors::VCR_NONE};
role_t h_role{role_t::VCR_NONE};
styling::color_unit h_fg{styling::color_unit::make_empty()};
styling::color_unit h_bg{styling::color_unit::make_empty()};
pcre* h_code;

@ -135,13 +135,13 @@ hist_source2::init()
this->hs_chart
.with_attrs_for_ident(HT_NORMAL,
vc.attrs_for_role(view_colors::VCR_TEXT))
vc.attrs_for_role(role_t::VCR_TEXT))
.with_attrs_for_ident(HT_WARNING,
vc.attrs_for_role(view_colors::VCR_WARNING))
vc.attrs_for_role(role_t::VCR_WARNING))
.with_attrs_for_ident(HT_ERROR,
vc.attrs_for_role(view_colors::VCR_ERROR))
vc.attrs_for_role(role_t::VCR_ERROR))
.with_attrs_for_ident(HT_MARK,
vc.attrs_for_role(view_colors::VCR_KEYWORD));
vc.attrs_for_role(role_t::VCR_KEYWORD));
}
void

@ -62,7 +62,7 @@ struct stacked_bar_chart_base {
explicit show_one(int so_index) : so_index(so_index) {}
};
typedef mapbox::util::variant<show_none, show_all, show_one> show_state;
using show_state = mapbox::util::variant<show_none, show_all, show_one>;
enum class direction {
forward,
@ -228,7 +228,7 @@ public:
if (ci.ci_attrs != 0) {
value_out.emplace_back(
lr, view_curses::VC_STYLE.value(ci.ci_attrs | A_REVERSE));
lr, VC_STYLE.value(ci.ci_attrs | A_REVERSE));
}
};
@ -328,7 +328,7 @@ public:
hist_source2()
{
this->clear();
};
}
~hist_source2() override = default;
@ -337,22 +337,22 @@ public:
void set_time_slice(int64_t slice)
{
this->hs_time_slice = slice;
};
}
int64_t get_time_slice() const
{
return this->hs_time_slice;
};
}
size_t text_line_count() override
{
return this->hs_line_count;
};
}
size_t text_line_width(textview_curses& curses) override
{
return 48 + 8 * 4;
};
}
void clear();
@ -374,7 +374,7 @@ public:
line_flags_t flags) override
{
return 0;
};
}
nonstd::optional<struct timeval> time_for_row(vis_line_t row) override;
@ -397,7 +397,7 @@ private:
bucket_block()
{
memset(this->bb_buckets, 0, sizeof(this->bb_buckets));
};
}
unsigned int bb_used{0};
bucket_t bb_buckets[BLOCK_SIZE];

@ -29,6 +29,7 @@
#include "hotkeys.hh"
#include "base/ansi_scrubber.hh"
#include "base/injector.hh"
#include "base/math_util.hh"
#include "base/opt_util.hh"

@ -318,11 +318,12 @@ SELECT *result-column* FROM *table* WHERE *\[cond\]* GROUP BY *grouping-expr* OR
Query the database and return zero or more rows of data.
**Parameters**
* **result-column** --- The expression used to generate a result for this column.
* **table** --- The table(s) to query for data
* **cond** --- The conditions used to select the rows to return.
* **grouping-expr** --- The expression to use when grouping rows.
* **ordering-term** --- The values to use when ordering the result set.
* **limit-expr** --- The maximum number of rows to return
* **limit-expr** --- The maximum number of rows to return.
**Examples**
To select all of the columns from the table 'syslog_log':

@ -40,8 +40,8 @@
#include <unistd.h>
#include <zlib.h>
#include "auto_mem.hh"
#include "base/auto_fd.hh"
#include "base/auto_mem.hh"
#include "base/file_range.hh"
#include "base/lnav_log.hh"
#include "base/result.h"

@ -275,8 +275,8 @@ listview_curses::do_update()
gutter_y++)
{
int range_start = 0, range_end;
view_colors::role_t role = this->vc_default_role;
view_colors::role_t bar_role = view_colors::VCR_SCROLLBAR;
role_t role = this->vc_default_role;
role_t bar_role = role_t::VCR_SCROLLBAR;
int attrs;
chtype ch = ACS_VLINE;

@ -90,8 +90,8 @@ public:
int start,
int end,
chtype& ch_out,
view_colors::role_t& role_out,
view_colors::role_t& bar_role_out)
role_t& role_out,
role_t& bar_role_out)
{
ch_out = ACS_VLINE;
};

@ -74,14 +74,16 @@
#endif
#include "all_logs_vtab.hh"
#include "ansi_scrubber.hh"
#include "base/ansi_scrubber.hh"
#include "base/fs_util.hh"
#include "base/func_util.hh"
#include "base/future_util.hh"
#include "base/humanize.hh"
#include "base/humanize.network.hh"
#include "base/humanize.time.hh"
#include "base/injector.bind.hh"
#include "base/isc.hh"
#include "base/lnav.console.hh"
#include "base/lnav_log.hh"
#include "base/paths.hh"
#include "base/string_util.hh"
@ -155,6 +157,7 @@
#endif
using namespace std::literals::chrono_literals;
using namespace lnav::roles::literals;
static bool initial_build = false;
static std::multimap<lnav_flags_t, std::string> DEFAULT_FILES;
@ -177,18 +180,20 @@ const int ZOOM_LEVELS[] = {
const ssize_t ZOOM_COUNT = sizeof(ZOOM_LEVELS) / sizeof(int);
const char* lnav_zoom_strings[] = {"1-second",
"30-second",
"1-minute",
"5-minute",
"15-minute",
"1-hour",
"4-hour",
"8-hour",
"1-day",
"1-week",
nullptr};
const char* lnav_zoom_strings[] = {
"1-second",
"30-second",
"1-minute",
"5-minute",
"15-minute",
"1-hour",
"4-hour",
"8-hour",
"1-day",
"1-week",
nullptr,
};
static const char* view_titles[LNV__MAX] = {
"LOG",
@ -299,12 +304,14 @@ bool
setup_logline_table(exec_context& ec)
{
// Hidden columns don't show up in the table_info pragma.
static const char* hidden_table_columns[] = {"log_time_msecs",
"log_path",
"log_text",
"log_body",
static const char* hidden_table_columns[] = {
"log_time_msecs",
"log_path",
"log_text",
"log_body",
nullptr};
nullptr,
};
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
bool retval = false;
@ -751,15 +758,11 @@ append_default_files(lnav_flags_t flag)
if (lnav_data.ld_flags & flag) {
auto cwd = ghc::filesystem::current_path();
std::pair<std::multimap<lnav_flags_t, std::string>::iterator,
std::multimap<lnav_flags_t, std::string>::iterator>
range;
for (range = DEFAULT_FILES.equal_range(flag);
for (auto range = DEFAULT_FILES.equal_range(flag);
range.first != range.second;
range.first++)
{
std::string path = range.first->second;
struct stat st;
auto path = range.first->second;
if (access(path.c_str(), R_OK) == 0) {
auto_mem<char> abspath;
@ -770,11 +773,13 @@ append_default_files(lnav_flags_t flag)
} else {
lnav_data.ld_active_files.fc_file_names[abspath.in()];
}
} else if (stat(path.c_str(), &st) == 0) {
fprintf(stderr,
"error: cannot read -- %s%s\n",
cwd.c_str(),
path.c_str());
} else if (lnav::filesystem::stat_file(path).isOk()) {
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("default syslog file is not readable -- ")
.append(lnav::roles::file(cwd))
.append(lnav::roles::file(path))));
retval = false;
}
}
@ -842,6 +847,27 @@ rl_blur(readline_curses* rc)
readline_context::command_map_t lnav_commands;
static attr_line_t
command_arg_help()
{
return attr_line_t()
.append(
"command arguments must start with one of the following symbols "
"to denote the type of command:\n")
.append(" ")
.append(":"_symbol)
.append(" - ")
.append("an lnav command (e.g. :goto 42)\n")
.append(" ")
.append(";"_symbol)
.append(" - ")
.append("an SQL statement (e.g. SELECT * FROM syslog_log)\n")
.append(" ")
.append("|"_symbol)
.append(" - ")
.append("an lnav script (e.g. |rename-stdin foo)\n");
}
static void
usage()
{
@ -1045,9 +1071,7 @@ public:
me.me_y = y - tc->get_y() - 1;
tc->handle_mouse(me);
};
private:
}
};
static bool
@ -1512,6 +1536,8 @@ looper()
highlight_source_t::THEME);
lnav_data.ld_files_view.set_overlay_source(&lnav_data.ld_files_overlay);
lnav_data.ld_user_message_view.set_window(lnav_data.ld_window);
lnav_data.ld_status[LNS_TOP].set_top(0);
lnav_data.ld_status[LNS_BOTTOM].set_top(-(rlc.get_height() + 1));
for (auto& sc : lnav_data.ld_status) {
@ -1530,6 +1556,7 @@ looper()
&lnav_data.ld_preview_status_source);
lnav_data.ld_match_view.set_show_bottom_border(true);
lnav_data.ld_user_message_view.set_show_bottom_border(true);
for (auto& sc : lnav_data.ld_status) {
sc.window_change();
@ -1713,6 +1740,7 @@ looper()
lnav_data.ld_example_view.do_update();
lnav_data.ld_match_view.do_update();
lnav_data.ld_preview_view.do_update();
lnav_data.ld_user_message_view.do_update();
if (ui_clock::now() >= next_status_update_time) {
for (auto& sc : lnav_data.ld_status) {
sc.do_update();
@ -1824,6 +1852,8 @@ looper()
ch, tc->get_top());
};
lnav_data.ld_user_message_source.clear();
if (!lnav_data.ld_looping) {
// No reason to keep processing input after the
// user has quit. The view stack will also be
@ -1953,8 +1983,9 @@ looper()
if (initial_build) {
static bool ran_cleanup = false;
std::vector<std::pair<Result<std::string, std::string>,
std::string>>
std::vector<std::pair<
Result<std::string, lnav::console::user_message>,
std::string>>
cmd_results;
execute_init_commands(ec, cmd_results);
@ -2024,6 +2055,7 @@ looper()
lnav_data.ld_match_view.set_needs_update();
lnav_data.ld_filter_view.set_needs_update();
lnav_data.ld_files_view.set_needs_update();
lnav_data.ld_user_message_view.set_needs_update();
}
if (lnav_data.ld_child_terminated) {
@ -2127,20 +2159,18 @@ get_textview_for_mode(ln_mode_t mode)
}
static void
print_errors(std::vector<std::string> error_list)
print_errors(std::vector<lnav::console::user_message> error_list)
{
for (auto& iter : error_list) {
fprintf(stderr,
"%s%s",
iter.c_str(),
iter[iter.size() - 1] == '\n' ? "" : "\n");
lnav::console::print(stderr, iter);
}
}
int
main(int argc, char* argv[])
{
std::vector<std::string> config_errors, loader_errors;
std::vector<lnav::console::user_message> config_errors;
std::vector<lnav::console::user_message> loader_errors;
exec_context& ec = lnav_data.ld_exec_context;
int lpc, c, retval = EXIT_SUCCESS;
@ -2223,15 +2253,22 @@ main(int argc, char* argv[])
}
break;
default:
fprintf(
lnav::console::print(
stderr,
"error: command arguments should start with a "
"colon, semi-colon, or pipe-symbol to denote:\n");
fprintf(stderr,
"error: a built-in command, SQL query, "
"or a file path that contains commands to "
"execute\n");
usage();
lnav::console::user_message::error(
attr_line_t("invalid value for ")
.append_quoted("-c"_symbol)
.append(" option"))
.with_snippet(lnav::console::snippet::from(
"arg",
attr_line_t(" -c ")
.append(optarg)
.append("\n")
.append(4, ' ')
.append(lnav::roles::error(
"^ command type prefix "
"is missing"))))
.with_help(command_arg_help()));
exit(EXIT_FAILURE);
break;
}
@ -2244,12 +2281,18 @@ main(int argc, char* argv[])
|| strcmp("/dev/stdin", optarg) == 0) {
exec_stdin = true;
}
lnav_data.ld_commands.push_back("|" + std::string(optarg));
lnav_data.ld_commands.emplace_back(
fmt::format(FMT_STRING("|{}"), optarg));
break;
case 'I':
if (access(optarg, X_OK) != 0) {
perror("invalid config path");
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("invalid configuration directory: ")
.append(lnav::roles::file(optarg)))
.with_errno_reason());
exit(EXIT_FAILURE);
}
lnav_data.ld_config_paths.emplace_back(optarg);
@ -2305,14 +2348,15 @@ main(int argc, char* argv[])
if (isatty(STDIN_FILENO) && read(STDIN_FILENO, &b, 1) == -1) {
perror("Read key from STDIN");
}
} break;
break;
}
case 'v':
lnav_data.ld_flags |= LNF_VERBOSE;
break;
case 'V':
printf("%s\n", VCS_PACKAGE_STRING);
fmt::print("{}\n", VCS_PACKAGE_STRING);
exit(0);
break;
@ -2361,7 +2405,26 @@ main(int argc, char* argv[])
= lnav::paths::dotlnav() / "configs/installed";
if (argc == 0) {
fprintf(stderr, "error: expecting file format paths\n");
const auto install_reason
= attr_line_t("the ")
.append("-i"_symbol)
.append(
" option expects one or more log format definition "
"files to install in your lnav configuration "
"directory");
const auto install_help
= attr_line_t(
"log format definitions are JSON files that tell lnav "
"how to understand log files\n")
.append(
"See: https://docs.lnav.org/en/latest/formats.html");
lnav::console::print(stderr,
lnav::console::user_message::error(
"missing format files to install")
.with_reason(install_reason)
.with_help(install_help));
usage();
return EXIT_FAILURE;
}
@ -2380,27 +2443,35 @@ main(int argc, char* argv[])
auto file_type_result = detect_config_file_type(argv[lpc]);
if (file_type_result.isErr()) {
fprintf(stderr,
"error: %s\n",
file_type_result.unwrapErr().c_str());
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("unable to open configuration file: ")
.append(lnav::roles::file(argv[lpc])))
.with_reason(file_type_result.unwrapErr()));
return EXIT_FAILURE;
}
auto file_type = file_type_result.unwrap();
std::string dst_name;
auto src_path = ghc::filesystem::path(argv[lpc]);
ghc::filesystem::path dst_name;
if (file_type == config_file_type::CONFIG) {
dst_name = basename(argv[lpc]);
dst_name = src_path.filename();
} else {
std::vector<intern_string_t> format_list
= load_format_file(argv[lpc], loader_errors);
auto format_list = load_format_file(src_path, loader_errors);
if (!loader_errors.empty()) {
print_errors(loader_errors);
return EXIT_FAILURE;
}
if (format_list.empty()) {
fprintf(
stderr, "error: format file is empty: %s\n", argv[lpc]);
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("invalid format file: ")
.append(lnav::roles::file(src_path.string())))
.with_reason("there must be at least one format "
"definition in the file"));
return EXIT_FAILURE;
}
@ -2442,7 +2513,11 @@ main(int argc, char* argv[])
}
}
fprintf(stderr, "info: installed: %s\n", dst_path.c_str());
lnav::console::print(
stderr,
lnav::console::user_message::ok(
attr_line_t("installed -- ")
.append(lnav::roles::file(dst_path))));
}
}
return EXIT_SUCCESS;
@ -2541,6 +2616,8 @@ main(int argc, char* argv[])
.add_child_view(&lnav_data.ld_filter_source.fss_editor);
lnav_data.ld_files_view.set_sub_source(&lnav_data.ld_files_source)
.add_input_delegate(lnav_data.ld_files_source);
lnav_data.ld_user_message_view.set_sub_source(
&lnav_data.ld_user_message_source);
for (lpc = 0; lpc < LNV__MAX; lpc++) {
lnav_data.ld_views[lpc].set_gutter_source(new log_gutter_source());
@ -2775,26 +2852,31 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
lnav_data.ld_active_files.fc_file_names[argv[lpc]].with_tail(
!(lnav_data.ld_flags & LNF_HEADLESS));
} else {
fprintf(stderr,
"Cannot stat file: %s -- %s\n",
argv[lpc],
strerror(errno));
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("unable to open file: ")
.append(lnav::roles::file(argv[lpc])))
.with_errno_reason());
retval = EXIT_FAILURE;
}
} else if (access(argv[lpc], R_OK) == -1) {
fprintf(stderr,
"Cannot read file: %s -- %s\n",
argv[lpc],
strerror(errno));
lnav::console::print(stderr,
lnav::console::user_message::error(
attr_line_t("cannot read file: ")
.append(lnav::roles::file(argv[lpc])))
.with_errno_reason());
retval = EXIT_FAILURE;
} else if (S_ISFIFO(st.st_mode)) {
auto_fd fifo_fd;
if ((fifo_fd = open(argv[lpc], O_RDONLY)) == -1) {
fprintf(stderr,
"Cannot open fifo: %s -- %s\n",
argv[lpc],
strerror(errno));
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("cannot open fifo: ")
.append(lnav::roles::file(argv[lpc])))
.with_errno_reason());
retval = EXIT_FAILURE;
} else {
auto fifo_tmp_fd
@ -2901,7 +2983,15 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
if (!(lnav_data.ld_flags & (LNF_HEADLESS | LNF_CHECK_CONFIG))
&& !isatty(STDOUT_FILENO))
{
fprintf(stderr, "error: stdout is not a tty.\n");
lnav::console::print(
stderr,
lnav::console::user_message::error(
"unable to display interactive text UI")
.with_reason("stdout is not a TTY")
.with_help(attr_line_t("pass the ")
.append("-n"_symbol)
.append(" option to run lnav in headless mode "
"or don't redirect stdout")));
retval = EXIT_FAILURE;
}
@ -2955,7 +3045,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
&& lnav_data.ld_commands.empty() && lnav_data.ld_pt_search.empty()
&& !(lnav_data.ld_flags & (LNF_HELP | LNF_NO_DEFAULT)))
{
fprintf(stderr, "error: no log files given/found.\n");
lnav::console::print(
stderr,
lnav::console::user_message::error("no log files given/found"));
retval = EXIT_FAILURE;
}
@ -3002,7 +3094,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
if (lnav_data.ld_flags & LNF_HEADLESS) {
std::vector<
std::pair<Result<std::string, std::string>, std::string>>
std::pair<Result<std::string, lnav::console::user_message>,
std::string>>
cmd_results;
textview_curses *log_tc, *text_tc, *tc;
bool output_view = true;
@ -3012,10 +3105,12 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
if (!lnav_data.ld_active_files.fc_name_to_errors.empty()) {
for (const auto& pair :
lnav_data.ld_active_files.fc_name_to_errors) {
fprintf(stderr,
"error: unable to open file: %s -- %s\n",
pair.first.c_str(),
pair.second.fei_description.c_str());
lnav::console::print(
stderr,
lnav::console::user_message::error(
attr_line_t("unable to open file: ")
.append(lnav::roles::file(pair.first)))
.with_reason(pair.second.fei_description));
}
return EXIT_FAILURE;
@ -3064,7 +3159,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
for (auto& pair : cmd_results) {
if (pair.first.isErr()) {
fprintf(stderr, "%s\n", pair.first.unwrapErr().c_str());
lnav::console::print(stderr, pair.first.unwrapErr());
output_view = false;
} else {
auto msg = pair.first.unwrap();
@ -3182,27 +3277,36 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
&& !(lnav_data.ld_flags & LNF_QUIET)
&& !(lnav_data.ld_flags & LNF_HEADLESS))
{
if (ghc::filesystem::file_size(stdin_tmp_path)
> MAX_STDIN_CAPTURE_SIZE) {
ghc::filesystem::permissions(stdin_tmp_path,
ghc::filesystem::perms::owner_read);
auto stdin_size = ghc::filesystem::file_size(stdin_tmp_path);
if (stdin_size > MAX_STDIN_CAPTURE_SIZE) {
log_info("not saving large stdin capture -- %s",
stdin_tmp_path.c_str());
ghc::filesystem::remove(stdin_tmp_path);
} else {
auto home = getenv("HOME");
auto home = getenv_opt("HOME");
auto path_str = stdin_tmp_path.string();
if (home != nullptr && startswith(path_str, home)) {
path_str = path_str.substr(strlen(home));
if (home && startswith(path_str, home.value())) {
path_str = path_str.substr(strlen(home.value()));
if (path_str[0] != '/') {
path_str.insert(0, 1, '/');
}
path_str.insert(0, 1, '~');
}
fprintf(stderr,
"info: stdin was captured, you can reopen it using -- "
"lnav %s\n",
path_str.c_str());
lnav::console::print(
stderr,
lnav::console::user_message::info(
attr_line_t()
.append(lnav::roles::number(humanize::file_size(
stdin_size, humanize::alignment::none)))
.append(" of data from stdin was captured and "
"will be saved for one day. You can "
"reopen it by running:\n")
.append(" {} ", lnav_data.ld_program_name)
.append(lnav::roles::file(path_str))));
}
}
}

@ -43,6 +43,7 @@
#include <sys/time.h>
#include "archive_manager.hh"
#include "base/ansi_scrubber.hh"
#include "base/future_util.hh"
#include "base/isc.hh"
#include "bottom_status_source.hh"
@ -256,6 +257,10 @@ struct lnav_data_t {
textview_curses ld_match_view;
plain_text_source ld_preview_source;
textview_curses ld_preview_view;
plain_text_source ld_user_message_source;
textview_curses ld_user_message_view;
std::chrono::time_point<std::chrono::steady_clock>
ld_user_message_expiration;
view_stack<textview_curses> ld_view_stack;
textview_curses* ld_last_view;

@ -40,9 +40,8 @@
#include <glob.h>
#include <sys/stat.h>
#include <termios.h>
#include <yajl/api/yajl_tree.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/fs_util.hh"
#include "base/humanize.network.hh"
#include "base/injector.hh"
@ -181,7 +180,7 @@ refresh_pt_search()
return retval;
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_adjust_log_time(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -251,7 +250,7 @@ com_adjust_log_time(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_unix_time(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -313,7 +312,7 @@ com_unix_time(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_current_time(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -337,7 +336,7 @@ com_current_time(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -445,7 +444,7 @@ com_goto(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_relative_goto(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -484,7 +483,7 @@ com_relative_goto(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_mark(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -501,7 +500,7 @@ com_mark(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_mark_expr(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -576,7 +575,7 @@ com_mark_expr_prompt(exec_context& ec, const std::string& cmdline)
trim(lnav_data.ld_log_source.get_sql_marker_text()));
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_clear_mark_expr(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -593,7 +592,7 @@ com_clear_mark_expr(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_goto_mark(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -673,7 +672,7 @@ com_goto_mark(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_goto_location(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -855,7 +854,7 @@ write_line_to(FILE* outfile, const attr_line_t& al)
}
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_save_to(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1276,7 +1275,7 @@ com_save_to(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_pipe_to(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1439,7 +1438,7 @@ com_pipe_to(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_redirect_to(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1503,7 +1502,7 @@ com_redirect_to(exec_context& ec,
return Ok("info: redirecting output to file -- " + split_args[0]);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_highlight(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1566,7 +1565,7 @@ com_highlight(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_clear_highlight(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1602,7 +1601,7 @@ com_clear_highlight(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_help(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -1615,10 +1614,10 @@ com_help(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string> com_enable_filter(
static Result<std::string, lnav::console::user_message> com_enable_filter(
exec_context& ec, std::string cmdline, std::vector<std::string>& args);
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1715,7 +1714,7 @@ com_filter(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_delete_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1745,7 +1744,7 @@ com_delete_filter(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_enable_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1780,7 +1779,7 @@ com_enable_filter(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_disable_filter(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1815,7 +1814,7 @@ com_disable_filter(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_filter_expr(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1899,7 +1898,7 @@ com_filter_expr_prompt(exec_context& ec, const std::string& cmdline)
trim(lnav_data.ld_log_source.get_sql_filter_text()));
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_clear_filter_expr(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1917,7 +1916,7 @@ com_clear_filter_expr(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_enable_word_wrap(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1934,7 +1933,7 @@ com_enable_word_wrap(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_disable_word_wrap(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -1953,7 +1952,7 @@ com_disable_word_wrap(exec_context& ec,
static std::set<std::string> custom_logline_tables;
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_create_logline_table(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2008,7 +2007,7 @@ com_create_logline_table(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_delete_logline_table(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2048,7 +2047,7 @@ com_delete_logline_table(exec_context& ec,
static std::set<std::string> custom_search_tables;
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_create_search_table(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2117,7 +2116,7 @@ com_create_search_table(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_delete_search_table(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2154,7 +2153,7 @@ com_delete_search_table(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_session(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2218,7 +2217,7 @@ com_session(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -2424,7 +2423,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
if (gl->gl_pathc > 10) {
al.append(" ... ")
.append(std::to_string(gl->gl_pathc - 10),
view_curses::VC_STYLE.value(A_BOLD))
VC_STYLE.value(A_BOLD))
.append(" files not shown ...");
}
lnav_data.ld_preview_status_source.get_description()
@ -2482,7 +2481,7 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_close(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -2546,7 +2545,7 @@ com_close(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_file_visibility(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2662,7 +2661,7 @@ com_file_visibility(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_hide_file(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2678,7 +2677,7 @@ com_hide_file(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_show_file(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2694,7 +2693,7 @@ com_show_file(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_show_only_this_file(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2709,7 +2708,7 @@ com_show_only_this_file(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_comment(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2772,7 +2771,7 @@ com_comment_prompt(exec_context& ec, const std::string& cmdline)
return "";
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_clear_comment(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2817,7 +2816,7 @@ com_clear_comment(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_tag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -2862,7 +2861,7 @@ com_tag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_untag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -2914,7 +2913,7 @@ com_untag(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_delete_tags(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -2935,7 +2934,7 @@ com_delete_tags(exec_context& ec,
"The :delete-tag command only works in the log view");
}
std::set<std::string>& known_tags = bookmark_metadata::KNOWN_TAGS;
auto& known_tags = bookmark_metadata::KNOWN_TAGS;
std::vector<std::string> tags;
for (size_t lpc = 1; lpc < args.size(); lpc++) {
@ -2989,7 +2988,7 @@ com_delete_tags(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_partition_name(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3023,7 +3022,7 @@ com_partition_name(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_clear_partition(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3064,7 +3063,7 @@ com_clear_partition(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_pt_time(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3136,7 +3135,7 @@ com_pt_time(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_summarize(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3154,10 +3153,12 @@ com_summarize(exec_context& ec,
auto_mem<char, sqlite3_free> query_frag;
std::vector<std::string> other_columns;
std::vector<std::string> num_columns;
const auto& top_source = ec.ec_source.top();
sql_progress_guard progress_guard(sql_progress,
sql_progress_finished,
ec.ec_source.top().first,
ec.ec_source.top().second);
top_source.s_source,
top_source.s_line,
top_source.s_content);
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
int retcode;
std::string query;
@ -3334,7 +3335,7 @@ com_summarize(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_add_test(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3378,7 +3379,7 @@ com_add_test(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_switch_to_view(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3411,7 +3412,7 @@ com_switch_to_view(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_toggle_filtering(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3429,7 +3430,7 @@ com_toggle_filtering(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_zoom_to(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3497,7 +3498,7 @@ com_zoom_to(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_reset_session(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3511,7 +3512,7 @@ com_reset_session(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_load_session(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3525,7 +3526,7 @@ com_load_session(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_save_session(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3538,7 +3539,7 @@ com_save_session(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_set_min_log_level(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3565,7 +3566,7 @@ com_set_min_log_level(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_toggle_field(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3643,7 +3644,7 @@ com_toggle_field(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_hide_line(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3744,7 +3745,7 @@ com_hide_line(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_show_lines(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3765,7 +3766,7 @@ com_show_lines(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_hide_unmarked(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3791,7 +3792,7 @@ com_hide_unmarked(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_show_unmarked(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3807,7 +3808,7 @@ com_show_unmarked(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_rebuild(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3820,7 +3821,7 @@ com_rebuild(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_shexec(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3833,7 +3834,7 @@ com_shexec(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_poll_now(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3847,7 +3848,7 @@ com_poll_now(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_redraw(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3861,7 +3862,7 @@ com_redraw(exec_context& ec,
return Ok(std::string());
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_echo(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval = "error: expecting a message";
@ -3911,7 +3912,7 @@ com_echo(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_alt_msg(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -3939,7 +3940,7 @@ com_alt_msg(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_eval(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
std::string retval;
@ -4003,7 +4004,7 @@ com_eval(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_config(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -4014,16 +4015,15 @@ com_config(exec_context& ec,
args.emplace_back("config-option");
} else if (args.size() > 1) {
yajlpp_parse_context ypc("input", &lnav_config_handlers);
std::vector<std::string> errors;
std::vector<lnav::console::user_message> errors;
std::string option = args[1];
lnav_config = rollback_lnav_config;
ypc.set_path(option)
.with_obj(lnav_config)
.with_error_reporter(
[&errors](const auto& ypc, auto level, auto* msg) {
errors.push_back(msg);
});
.with_error_reporter([&errors](const auto& ypc, auto msg) {
errors.push_back(msg);
});
ypc.ypc_active_paths.insert(option);
ypc.update_callbacks();
@ -4103,7 +4103,6 @@ com_config(exec_context& ec,
auto consumed
= strtonum(val, value.c_str(), value.length());
log_debug("got val %d", (int) val);
if (consumed != value.length()) {
return ec.make_error("expecting an integer, found: {}",
value);
@ -4124,22 +4123,24 @@ com_config(exec_context& ec,
}
if (!errors.empty()) {
return ec.make_error(errors[0]);
return Err(errors[0]);
}
if (changed) {
intern_string_t path = intern_string::lookup(option);
lnav_config_locations[path]
= {intern_string::lookup(ec.ec_source.top().first),
ec.ec_source.top().second};
lnav_config_locations[path] = {
intern_string::lookup(ec.ec_source.top().s_source),
ec.ec_source.top().s_line,
};
reload_config(errors);
if (!errors.empty()) {
lnav_config = rollback_lnav_config;
reload_config(errors);
return Err("error: " + errors[0]);
} else if (!ec.ec_dry_run) {
return Err(errors[0]);
}
if (!ec.ec_dry_run) {
retval = "info: changed config option -- " + option;
rollback_lnav_config = lnav_config;
save_config();
@ -4157,7 +4158,7 @@ com_config(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_reset_config(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -4497,7 +4498,7 @@ public:
std::string dsvs_error_msg;
};
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_spectrogram(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -4560,7 +4561,7 @@ com_spectrogram(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_quit(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
{
if (args.empty()) {
@ -4802,7 +4803,7 @@ user_prompt(std::vector<std::string>& args)
lnav_data.ld_status[LNS_BOTTOM].do_update();
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
com_prompt(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)

@ -46,8 +46,8 @@
#include <sys/stat.h>
#include <unistd.h>
#include "auto_mem.hh"
#include "base/auto_fd.hh"
#include "base/auto_mem.hh"
#include "base/auto_pid.hh"
#include "base/fs_util.hh"
#include "base/injector.bind.hh"
@ -59,6 +59,7 @@
#include "config.h"
#include "default-config.h"
#include "styling.hh"
#include "view_curses.hh"
#include "yajlpp/yajlpp.hh"
#include "yajlpp/yajlpp_def.hh"
@ -124,7 +125,7 @@ ensure_dotlnav()
auto path = lnav::paths::dotlnav();
for (auto sub_path : subdirs) {
for (const auto* sub_path : subdirs) {
auto full_path = path / sub_path;
log_perror(mkdir(full_path.c_str(), 0755));
@ -164,7 +165,7 @@ ensure_dotlnav()
continue;
}
log_debug("Removing old stdin capture: %s", gl->gl_pathv[lpc]);
log_info("Removing old stdin capture: %s", gl->gl_pathv[lpc]);
log_perror(remove(gl->gl_pathv[lpc]));
}
}
@ -315,8 +316,9 @@ read_repo_path(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
return 1;
}
static const struct json_path_container format_handlers
= {json_path_handler("format-repos#", read_repo_path)};
static const struct json_path_container format_handlers = {
json_path_handler("format-repos#", read_repo_path),
};
void
install_extra_formats()
@ -351,7 +353,7 @@ install_extra_formats()
yajl_config(jhandle, yajl_allow_comments, 1);
while ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
if (yajl_parse(jhandle, buffer, rc) != yajl_status_ok) {
auto msg = yajl_get_error(jhandle, 1, buffer, rc);
auto* msg = yajl_get_error(jhandle, 1, buffer, rc);
fprintf(
stderr, "Unable to parse remote-config.json -- %s", msg);
yajl_free_error(jhandle, msg);
@ -359,7 +361,7 @@ install_extra_formats()
}
}
if (yajl_complete_parse(jhandle) != yajl_status_ok) {
auto msg = yajl_get_error(jhandle, 1, buffer, rc);
auto* msg = yajl_get_error(jhandle, 1, buffer, rc);
fprintf(stderr, "Unable to parse remote-config.json -- %s", msg);
yajl_free_error(jhandle, msg);
@ -368,23 +370,19 @@ install_extra_formats()
}
struct userdata {
userdata(std::vector<std::string>& errors) : ud_errors(errors){};
explicit userdata(std::vector<lnav::console::user_message>& errors)
: ud_errors(errors){};
std::vector<std::string>& ud_errors;
std::vector<lnav::console::user_message>& ud_errors;
};
static void
config_error_reporter(const yajlpp_parse_context& ypc,
lnav_log_level_t level,
const char* msg)
const lnav::console::user_message& msg)
{
if (level >= lnav_log_level_t::ERROR) {
struct userdata* ud = (userdata*) ypc.ypc_userdata;
auto* ud = (userdata*) ypc.ypc_userdata;
ud->ud_errors.emplace_back(msg);
} else {
fprintf(stderr, "warning:%s\n", msg);
}
ud->ud_errors.emplace_back(msg);
}
static const struct json_path_container key_command_handlers = {
@ -393,38 +391,40 @@ static const struct json_path_container key_command_handlers = {
.with_description(
"The command to execute for the given key sequence. Use a script "
"to execute more complicated operations.")
.with_pattern("[:|;].*")
.with_pattern("^[:|;].*")
.with_example(":goto next hour")
.FOR_FIELD(key_command, kc_cmd),
.for_field(&key_command::kc_cmd),
yajlpp::property_handler("alt-msg")
.with_synopsis("<msg>")
.with_description(
"The help message to display after the key is pressed.")
.FOR_FIELD(key_command, kc_alt_msg)};
static const struct json_path_container keymap_def_handlers
= {yajlpp::pattern_property_handler("(?<key_seq>(?:x[0-9a-f]{2})+)")
.with_synopsis("<utf8-key-code-in-hex>")
.with_description(
"Map of key codes to commands to execute. The field names are "
"the keys to be mapped using as a hexadecimal representation of "
"the UTF-8 encoding. Each byte of the UTF-8 should start with "
"an 'x' followed by the hexadecimal representation of the byte.")
.with_obj_provider<key_command, key_map>(
[](const yajlpp_provider_context& ypc, key_map* km) {
key_command& retval
= km->km_seq_to_cmd[ypc.ypc_extractor.get_substr(
"key_seq")];
return &retval;
})
.with_path_provider<key_map>(
[](key_map* km, std::vector<std::string>& paths_out) {
for (const auto& iter : km->km_seq_to_cmd) {
paths_out.emplace_back(iter.first);
}
})
.with_children(key_command_handlers)};
.for_field<>(&key_command::kc_alt_msg),
};
static const struct json_path_container keymap_def_handlers = {
yajlpp::pattern_property_handler("(?<key_seq>(?:x[0-9a-f]{2})+)")
.with_synopsis("<utf8-key-code-in-hex>")
.with_description(
"Map of key codes to commands to execute. The field names are "
"the keys to be mapped using as a hexadecimal representation of "
"the UTF-8 encoding. Each byte of the UTF-8 should start with "
"an 'x' followed by the hexadecimal representation of the byte.")
.with_obj_provider<key_command, key_map>(
[](const yajlpp_provider_context& ypc, key_map* km) {
key_command& retval
= km->km_seq_to_cmd[ypc.ypc_extractor.get_substr(
"key_seq")];
return &retval;
})
.with_path_provider<key_map>(
[](key_map* km, std::vector<std::string>& paths_out) {
for (const auto& iter : km->km_seq_to_cmd) {
paths_out.emplace_back(iter.first);
}
})
.with_children(key_command_handlers),
};
static const struct json_path_container keymap_defs_handlers = {
yajlpp::pattern_property_handler("(?<keymap_name>[\\w\\-]+)")
@ -442,7 +442,8 @@ static const struct json_path_container keymap_defs_handlers = {
paths_out.emplace_back(iter.first);
}
})
.with_children(keymap_def_handlers)};
.with_children(keymap_def_handlers),
};
static const struct json_path_container global_var_handlers = {
yajlpp::pattern_property_handler("(?<var_name>\\w+)")
@ -456,7 +457,8 @@ static const struct json_path_container global_var_handlers = {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(_lnav_config, lc_global_vars)};
.FOR_FIELD(_lnav_config, lc_global_vars),
};
static const struct json_path_container style_config_handlers =
json_path_container{
@ -469,7 +471,7 @@ static const struct json_path_container style_config_handlers =
.with_example("#fff")
.with_example("Green")
.with_example("$black")
.FOR_FIELD(style_config, sc_color),
.for_field(&style_config::sc_color),
yajlpp::property_handler("background-color")
.with_synopsis("#hex|color_name")
.with_description(
@ -478,13 +480,13 @@ static const struct json_path_container style_config_handlers =
"variable reference.")
.with_example("#2d2a2e")
.with_example("Green")
.FOR_FIELD(style_config, sc_background_color),
.for_field(&style_config::sc_background_color),
yajlpp::property_handler("underline")
.with_description("Indicates that the text should be underlined.")
.FOR_FIELD(style_config, sc_underline),
.for_field(&style_config::sc_underline),
yajlpp::property_handler("bold")
.with_description("Indicates that the text should be bolded.")
.FOR_FIELD(style_config, sc_bold),
.for_field(&style_config::sc_bold),
}
.with_definition_id("style");
@ -594,103 +596,147 @@ static const struct json_path_container theme_styles_handlers = {
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_scrollbar;
})
.with_children(style_config_handlers)};
static const struct json_path_container theme_syntax_styles_handlers
= {yajlpp::property_handler("keyword")
.with_description("Styling for keywords in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_keyword;
})
.with_children(style_config_handlers),
yajlpp::property_handler("string")
.with_description("Styling for single/double-quoted strings in text")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_string;
})
.with_children(style_config_handlers),
yajlpp::property_handler("comment")
.with_description("Styling for comments in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_comment;
})
.with_children(style_config_handlers),
yajlpp::property_handler("doc-directive")
.with_description(
"Styling for documentation directives in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_doc_directive;
})
.with_children(style_config_handlers),
yajlpp::property_handler("variable")
.with_description("Styling for variables in text")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_variable;
})
.with_children(style_config_handlers),
yajlpp::property_handler("symbol")
.with_description("Styling for symbols in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_symbol;
})
.with_children(style_config_handlers),
yajlpp::property_handler("number")
.with_description("Styling for numbers in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_number;
})
.with_children(style_config_handlers),
yajlpp::property_handler("re-special")
.with_description(
"Styling for special characters in regular expressions")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_re_special;
})
.with_children(style_config_handlers),
yajlpp::property_handler("re-repeat")
.with_description("Styling for repeats in regular expressions")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_re_repeat;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-delete")
.with_description("Styling for deleted lines in diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_delete;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-add")
.with_description("Styling for added lines in diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_add;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-section")
.with_description("Styling for diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_section;
})
.with_children(style_config_handlers),
yajlpp::property_handler("file")
.with_description("Styling for file names in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_file;
})
.with_children(style_config_handlers)};
.with_children(style_config_handlers),
yajlpp::property_handler("h1")
.with_description("Styling for top-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[0];
})
.with_children(style_config_handlers),
yajlpp::property_handler("h2")
.with_description("Styling for 2nd-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[1];
})
.with_children(style_config_handlers),
yajlpp::property_handler("h3")
.with_description("Styling for 3rd-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[2];
})
.with_children(style_config_handlers),
yajlpp::property_handler("h4")
.with_description("Styling for 4th-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[3];
})
.with_children(style_config_handlers),
yajlpp::property_handler("h5")
.with_description("Styling for 5th-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[4];
})
.with_children(style_config_handlers),
yajlpp::property_handler("h6")
.with_description("Styling for 6th-level headers")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_header[5];
})
.with_children(style_config_handlers),
};
static const struct json_path_container theme_syntax_styles_handlers = {
yajlpp::property_handler("keyword")
.with_description("Styling for keywords in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_keyword;
})
.with_children(style_config_handlers),
yajlpp::property_handler("string")
.with_description("Styling for single/double-quoted strings in text")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_string;
})
.with_children(style_config_handlers),
yajlpp::property_handler("comment")
.with_description("Styling for comments in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_comment;
})
.with_children(style_config_handlers),
yajlpp::property_handler("doc-directive")
.with_description(
"Styling for documentation directives in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_doc_directive;
})
.with_children(style_config_handlers),
yajlpp::property_handler("variable")
.with_description("Styling for variables in text")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_variable;
})
.with_children(style_config_handlers),
yajlpp::property_handler("symbol")
.with_description("Styling for symbols in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_symbol;
})
.with_children(style_config_handlers),
yajlpp::property_handler("number")
.with_description("Styling for numbers in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_number;
})
.with_children(style_config_handlers),
yajlpp::property_handler("re-special")
.with_description(
"Styling for special characters in regular expressions")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_re_special;
})
.with_children(style_config_handlers),
yajlpp::property_handler("re-repeat")
.with_description("Styling for repeats in regular expressions")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_re_repeat;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-delete")
.with_description("Styling for deleted lines in diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_delete;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-add")
.with_description("Styling for added lines in diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_add;
})
.with_children(style_config_handlers),
yajlpp::property_handler("diff-section")
.with_description("Styling for diffs")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_diff_section;
})
.with_children(style_config_handlers),
yajlpp::property_handler("file")
.with_description("Styling for file names in source files")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
return &root->lt_style_file;
})
.with_children(style_config_handlers),
};
static const struct json_path_container theme_status_styles_handlers = {
yajlpp::property_handler("text")
@ -772,24 +818,25 @@ static const struct json_path_container theme_status_styles_handlers = {
.with_children(style_config_handlers),
};
static const struct json_path_container theme_log_level_styles_handlers
= {yajlpp::pattern_property_handler(
"(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|"
"warning|error|critical|fatal|invalid)")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
style_config& sc = root->lt_level_styles[string2level(
ypc.ypc_extractor.get_substr_i("level").get())];
return &sc;
})
.with_path_provider<lnav_theme>(
[](struct lnav_theme* cfg, std::vector<std::string>& paths_out) {
for (int lpc = LEVEL_TRACE; lpc < LEVEL__MAX; lpc++) {
paths_out.emplace_back(level_names[lpc]);
}
})
.with_children(style_config_handlers)};
static const struct json_path_container theme_log_level_styles_handlers = {
yajlpp::pattern_property_handler(
"(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|"
"warning|error|critical|fatal|invalid)")
.with_obj_provider<style_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
style_config& sc = root->lt_level_styles[string2level(
ypc.ypc_extractor.get_substr_i("level").get())];
return &sc;
})
.with_path_provider<lnav_theme>(
[](struct lnav_theme* cfg, std::vector<std::string>& paths_out) {
for (int lpc = LEVEL_TRACE; lpc < LEVEL__MAX; lpc++) {
paths_out.emplace_back(level_names[lpc]);
}
})
.with_children(style_config_handlers),
};
static const struct json_path_container highlighter_handlers = {
yajlpp::property_handler("pattern")
@ -807,36 +854,38 @@ static const struct json_path_container highlighter_handlers = {
.with_children(style_config_handlers),
};
static const struct json_path_container theme_highlights_handlers
= {yajlpp::pattern_property_handler("(?<highlight_name>\\w+)")
.with_obj_provider<highlighter_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
highlighter_config& hc
= root->lt_highlights[ypc.ypc_extractor
.get_substr_i("highlight_name")
.get()];
return &hc;
})
.with_path_provider<lnav_theme>(
[](struct lnav_theme* cfg, std::vector<std::string>& paths_out) {
for (const auto& pair : cfg->lt_highlights) {
paths_out.emplace_back(pair.first);
}
})
.with_children(highlighter_handlers)};
static const struct json_path_container theme_vars_handlers
= {yajlpp::pattern_property_handler("(?<var_name>\\w+)")
.with_synopsis("name")
.with_description("A theme variable definition")
.with_path_provider<lnav_theme>(
[](struct lnav_theme* lt, std::vector<std::string>& paths_out) {
for (const auto& iter : lt->lt_vars) {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(lnav_theme, lt_vars)};
static const struct json_path_container theme_highlights_handlers = {
yajlpp::pattern_property_handler("(?<highlight_name>\\w+)")
.with_obj_provider<highlighter_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
highlighter_config& hc
= root->lt_highlights[ypc.ypc_extractor
.get_substr_i("highlight_name")
.get()];
return &hc;
})
.with_path_provider<lnav_theme>(
[](struct lnav_theme* cfg, std::vector<std::string>& paths_out) {
for (const auto& pair : cfg->lt_highlights) {
paths_out.emplace_back(pair.first);
}
})
.with_children(highlighter_handlers),
};
static const struct json_path_container theme_vars_handlers = {
yajlpp::pattern_property_handler("(?<var_name>\\w+)")
.with_synopsis("name")
.with_description("A theme variable definition")
.with_path_provider<lnav_theme>(
[](struct lnav_theme* lt, std::vector<std::string>& paths_out) {
for (const auto& iter : lt->lt_vars) {
paths_out.emplace_back(iter.first);
}
})
.FOR_FIELD(lnav_theme, lt_vars),
};
static const struct json_path_container theme_def_handlers = {
yajlpp::property_handler("vars")
@ -881,7 +930,8 @@ static const struct json_path_container theme_defs_handlers = {
paths_out.emplace_back(iter.first);
}
})
.with_children(theme_def_handlers)};
.with_children(theme_def_handlers),
};
static const struct json_path_container ui_handlers = {
yajlpp::property_handler("clock-format")
@ -889,13 +939,13 @@ static const struct json_path_container ui_handlers = {
.with_description("The format for the clock displayed in "
"the top-left corner using strftime(3) conversions")
.with_example("%a %b %d %H:%M:%S %Z")
.FOR_FIELD(_lnav_config, lc_ui_clock_format),
.for_field(&_lnav_config::lc_ui_clock_format),
yajlpp::property_handler("dim-text")
.with_synopsis("bool")
.with_description("Reduce the brightness of text (useful for xterms). "
"This setting can be useful when running in an xterm "
"where the white color is very bright.")
.FOR_FIELD(_lnav_config, lc_ui_dim_text),
.for_field(&_lnav_config::lc_ui_dim_text),
yajlpp::property_handler("default-colors")
.with_synopsis("bool")
.with_description(
@ -903,15 +953,15 @@ static const struct json_path_container ui_handlers = {
"instead of black and white for all text coloring. This setting "
"can be useful when transparent background or alternate color "
"theme terminal is used.")
.FOR_FIELD(_lnav_config, lc_ui_default_colors),
.for_field(&_lnav_config::lc_ui_default_colors),
yajlpp::property_handler("keymap")
.with_synopsis("keymap_name")
.with_description("The name of the keymap to use.")
.FOR_FIELD(_lnav_config, lc_ui_keymap),
.for_field(&_lnav_config::lc_ui_keymap),
yajlpp::property_handler("theme")
.with_synopsis("theme_name")
.with_description("The name of the theme to use.")
.FOR_FIELD(_lnav_config, lc_ui_theme),
.for_field(&_lnav_config::lc_ui_theme),
yajlpp::property_handler("theme-defs")
.with_description("Theme definitions.")
.with_children(theme_defs_handlers),
@ -1105,8 +1155,11 @@ static const struct json_path_container tuning_handlers = {
.with_children(sysclip_handlers),
};
const char* DEFAULT_CONFIG_SCHEMA
= "https://lnav.org/schemas/config-v1.schema.json";
static const std::set<std::string> SUPPORTED_CONFIG_SCHEMAS = {
"https://lnav.org/schemas/config-v1.schema.json",
DEFAULT_CONFIG_SCHEMA,
};
const char* DEFAULT_FORMAT_SCHEMA
@ -1122,13 +1175,22 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
auto file_id = std::string((const char*) str, len);
if (SUPPORTED_CONFIG_SCHEMAS.count(file_id) == 0) {
const auto* handler = ypc->ypc_current_handler;
attr_line_t notes{"expecting one of the following $schema values:"};
for (const auto& schema : SUPPORTED_CONFIG_SCHEMAS) {
notes.append("\n").append(
lnav::roles::symbol(fmt::format(FMT_STRING(" {}"), schema)));
}
ypc->report_error(
lnav_log_level_t::ERROR,
"%s:%d: error: unsupported configuration $schema -- %s\n",
ypc->ypc_source.c_str(),
ypc->get_line_number(),
file_id.c_str());
return 0;
lnav::console::user_message::error(
attr_line_t("'")
.append(lnav::roles::symbol(file_id))
.append(
"' is not a supported configuration $schema version"))
.with_snippet(ypc->get_snippet())
.with_note(notes)
.with_help(handler->get_help_text(ypc)));
}
return 1;
@ -1136,8 +1198,9 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
const json_path_container lnav_config_handlers = json_path_container {
json_path_handler("$schema", read_id)
.with_synopsis("The URI of the schema for this file")
.with_description("Specifies the type of this file"),
.with_synopsis("<schema-uri>")
.with_description("The URI that specifies the schema that describes this type of file")
.with_example(DEFAULT_CONFIG_SCHEMA),
yajlpp::property_handler("tuning")
.with_description("Internal settings")
@ -1174,15 +1237,7 @@ detect_config_file_type(const ghc::filesystem::path& path)
{
static const char* id_path[] = {"$schema", nullptr};
auto read_res = lnav::filesystem::read_file(path);
if (read_res.isErr()) {
return Err(fmt::format(FMT_STRING("unable to open file4: {} -- {}"),
path.string(),
read_res.unwrapErr()));
}
auto content = read_res.unwrap();
auto content = TRY(lnav::filesystem::read_file(path));
if (startswith(content, "#")) {
content.insert(0, "//");
}
@ -1192,9 +1247,8 @@ detect_config_file_type(const ghc::filesystem::path& path)
yajl_tree_parse(content.c_str(), error_buffer, sizeof(error_buffer)),
yajl_tree_free);
if (content_tree == nullptr) {
return Err(fmt::format(FMT_STRING("unable to parse file: {} -- {}"),
path.string(),
error_buffer));
return Err(
fmt::format(FMT_STRING("JSON parsing failed -- {}"), error_buffer));
}
auto* id_val = yajl_tree_get(content_tree.get(), id_path, yajl_t_string);
@ -1206,18 +1260,16 @@ detect_config_file_type(const ghc::filesystem::path& path)
return Ok(config_file_type::FORMAT);
}
return Err(fmt::format(
FMT_STRING("unsupported configuration version in file: {} -- {}"),
path.string(),
FMT_STRING("unsupported configuration version in file -- {}"),
id_val->u.string));
} else {
return Ok(config_file_type::FORMAT);
}
return Ok(config_file_type::FORMAT);
}
static void
load_config_from(_lnav_config& lconfig,
const ghc::filesystem::path& path,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
yajlpp_parse_context ypc(path.string(), &lnav_config_handlers);
struct userdata ud(errors);
@ -1229,14 +1281,15 @@ load_config_from(_lnav_config& lconfig,
ypc.with_error_reporter(config_error_reporter);
if ((fd = lnav::filesystem::openp(path, O_RDONLY)) == -1) {
if (errno != ENOENT) {
errors.emplace_back(fmt::format(
FMT_STRING("error: unable to open format file -- {}"),
path.string()));
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("unable to open configuration file: ")
.append(lnav::roles::file(path)))
.with_errno_reason());
}
} else {
auto_mem<yajl_handle_t> handle(yajl_free);
char buffer[2048];
off_t offset = 0;
ssize_t rc = -1;
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
@ -1247,18 +1300,19 @@ load_config_from(_lnav_config& lconfig,
rc = read(fd, buffer, sizeof(buffer));
if (rc == 0) {
break;
} else if (rc == -1) {
}
if (rc == -1) {
errors.emplace_back(
fmt::format(FMT_STRING("{}:unable to read file -- {}"),
path.string(),
strerror(errno)));
lnav::console::user_message::error(
attr_line_t("unable to read format file: ")
.append(lnav::roles::file(path)))
.with_errno_reason());
break;
}
if (ypc.parse((const unsigned char*) buffer, rc) != yajl_status_ok)
{
break;
}
offset += rc;
}
if (rc == 0) {
ypc.complete_parse();
@ -1270,7 +1324,7 @@ static void
load_default_config(struct _lnav_config& config_obj,
const std::string& path,
const bin_src_file& bsf,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
yajlpp_parse_context ypc_builtin(bsf.get_name(), &lnav_config_handlers);
auto_mem<yajl_handle_t> handle(yajl_free);
@ -1298,7 +1352,7 @@ load_default_config(struct _lnav_config& config_obj,
static void
load_default_configs(struct _lnav_config& config_obj,
const std::string& path,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
for (auto& bsf : lnav_config_json) {
load_default_config(config_obj, path, bsf, errors);
@ -1307,7 +1361,7 @@ load_default_configs(struct _lnav_config& config_obj,
void
load_config(const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
auto user_config = lnav::paths::dotlnav() / "config.json";
@ -1370,14 +1424,14 @@ load_config(const std::vector<ghc::filesystem::path>& extra_paths,
void
reset_config(const std::string& path)
{
std::vector<std::string> errors;
std::vector<lnav::console::user_message> errors;
load_default_configs(lnav_config, path, errors);
reload_config(errors);
for (auto& err : errors) {
log_debug("reset %s", err.c_str());
for (const auto& err : errors) {
log_debug("reset %s", err.um_message.get_string().c_str());
}
}
@ -1418,7 +1472,7 @@ save_config()
}
void
reload_config(std::vector<std::string>& errors)
reload_config(std::vector<lnav::console::user_message>& errors)
{
lnav_config_listener* curr = lnav_config_listener::LISTENER_LIST;
@ -1439,10 +1493,16 @@ reload_config(std::vector<std::string>& errors)
return;
}
errors.emplace_back(fmt::format(FMT_STRING("{}:{}:{}"),
loc_iter->second.sl_source,
loc_iter->second.sl_line_number,
errmsg));
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t()
.append("invalid value for property ")
.append_quoted(lnav::roles::symbol(path)))
.with_reason(errmsg)
.with_snippet(
lnav::console::snippet::from(
loc_iter->second.sl_source.to_string(), "")
.with_line(loc_iter->second.sl_line_number)));
};
for (const auto& jph : lnav_config_handlers.jpc_children) {

@ -41,6 +41,7 @@
#include "archive_manager.cfg.hh"
#include "base/file_range.hh"
#include "base/lnav.console.hh"
#include "base/result.h"
#include "file_vtab.cfg.hh"
#include "ghc/filesystem.hpp"
@ -117,11 +118,11 @@ Result<config_file_type, std::string> detect_config_file_type(
const ghc::filesystem::path& path);
void load_config(const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
void reset_config(const std::string& path);
void reload_config(std::vector<std::string>& errors);
void reload_config(std::vector<lnav::console::user_message>& errors);
std::string save_config();

@ -36,11 +36,13 @@
#include <stdio.h>
#include <sys/stat.h>
#include "ansi_scrubber.hh"
#include "base/ansi_scrubber.hh"
#include "base/result.h"
#include "config.h"
#include "fmt/format.h"
#include "view_curses.hh"
#include "yajlpp/yajlpp.hh"
#include "yajlpp/yajlpp_def.hh"
bool
change_to_parent_dir()
@ -101,8 +103,249 @@ err_prefix(const std::string msg)
return std::string(ANSI_COLOR(COLOR_RED) "\u2718" ANSI_NORM " ") + msg;
}
Result<std::string, std::string>
err_to_ok(const std::string msg)
Result<std::string, lnav::console::user_message>
err_to_ok(const lnav::console::user_message msg)
{
return Ok(err_prefix(msg));
return Ok(msg.to_attr_line().get_string());
}
namespace lnav {
static void
to_json(yajlpp_gen& gen, const attr_line_t& al)
{
{
yajlpp_map root_map(gen);
root_map.gen("str");
root_map.gen(al.get_string());
root_map.gen("attrs");
{
yajlpp_array attr_array(gen);
for (const auto& sa : al.get_attrs()) {
yajlpp_map elem_map(gen);
elem_map.gen("start");
elem_map.gen(sa.sa_range.lr_start);
elem_map.gen("end");
elem_map.gen(sa.sa_range.lr_end);
elem_map.gen("type");
elem_map.gen(sa.sa_type->sat_name);
elem_map.gen("value");
sa.sa_value.match(
[&](int64_t i) { elem_map.gen(i); },
[&](role_t r) {
elem_map.gen(lnav::enums::to_underlying(r));
},
[&](const intern_string_t& str) { elem_map.gen(str); },
[&](const std::string& str) { elem_map.gen(str); },
[&](const std::shared_ptr<logfile>& lf) {
elem_map.gen("");
},
[&](const bookmark_metadata* bm) { elem_map.gen(""); });
}
}
}
}
std::string
to_json(const attr_line_t& al)
{
yajlpp_gen gen;
yajl_gen_config(gen, yajl_gen_beautify, false);
to_json(gen, al);
return gen.to_string_fragment().to_string();
}
std::string
to_json(const lnav::console::user_message& um)
{
yajlpp_gen gen;
yajl_gen_config(gen, yajl_gen_beautify, false);
{
yajlpp_map root_map(gen);
root_map.gen("level");
switch (um.um_level) {
case console::user_message::level::ok:
root_map.gen("ok");
break;
case console::user_message::level::info:
root_map.gen("info");
break;
case console::user_message::level::warning:
root_map.gen("warning");
break;
case console::user_message::level::error:
root_map.gen("error");
break;
}
root_map.gen("message");
to_json(gen, um.um_message);
root_map.gen("reason");
to_json(gen, um.um_reason);
root_map.gen("snippets");
{
yajlpp_array snippet_array(gen);
for (const auto& snip : um.um_snippets) {
yajlpp_map snip_map(gen);
snip_map.gen("source");
snip_map.gen(snip.s_source);
snip_map.gen("line");
snip_map.gen(snip.s_line);
snip_map.gen("column");
snip_map.gen(snip.s_column);
snip_map.gen("content");
to_json(gen, snip.s_content);
}
}
root_map.gen("help");
to_json(gen, um.um_help);
}
return gen.to_string_fragment().to_string();
}
static int
read_string_attr_type(yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len)
{
auto* sa = (string_attr*) ypc->ypc_obj_stack.top();
auto type = std::string((const char*) str, len);
if (type == "role") {
sa->sa_type = &VC_ROLE;
} else {
ensure(false);
}
return 1;
}
static int
read_string_attr_int_value(yajlpp_parse_context* ypc, int64_t in)
{
auto sa = (string_attr*) ypc->ypc_obj_stack.top();
if (sa->sa_type == &VC_ROLE) {
sa->sa_value = (role_t) in;
}
return 1;
}
static const struct json_path_container string_attr_handlers = {
yajlpp::property_handler("start").for_field(&string_attr::sa_range,
&line_range::lr_start),
yajlpp::property_handler("end").for_field(&string_attr::sa_range,
&line_range::lr_end),
yajlpp::property_handler("type").add_cb(read_string_attr_type),
yajlpp::property_handler("value").add_cb(read_string_attr_int_value),
};
static const struct json_path_container attr_line_handlers = {
yajlpp::property_handler("str").for_field(&attr_line_t::al_string),
yajlpp::property_handler("attrs#")
.with_obj_provider<string_attr, attr_line_t>(
[](const yajlpp_provider_context& ypc, attr_line_t* root) {
root->al_attrs.resize(ypc.ypc_index + 1);
return &root->al_attrs[ypc.ypc_index];
})
.with_children(string_attr_handlers),
};
template<>
attr_line_t
from_json(const std::string& json)
{
yajlpp_parse_context ypc("string", &attr_line_handlers);
auto_mem<yajl_handle_t> handle(yajl_free);
attr_line_t retval;
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
ypc.with_handle(handle);
ypc.with_obj(retval);
ypc.parse(json);
ypc.complete_parse();
return retval;
}
static const json_path_container snippet_handlers = {
yajlpp::property_handler("source").for_field(&console::snippet::s_source),
yajlpp::property_handler("line").for_field(&console::snippet::s_line),
yajlpp::property_handler("column").for_field(&console::snippet::s_column),
yajlpp::property_handler("content")
.with_obj_provider<attr_line_t, console::snippet>(
[](const yajlpp_provider_context& ypc, console::snippet* snip) {
return &snip->s_content;
})
.with_children(attr_line_handlers),
};
static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
{"ok", lnav::console::user_message::level::ok},
{"info", lnav::console::user_message::level::info},
{"warning", lnav::console::user_message::level::warning},
{"error", lnav::console::user_message::level::error},
json_path_handler_base::ENUM_TERMINATOR,
};
static const struct json_path_container user_message_handlers = {
yajlpp::property_handler("level")
.with_enum_values(LEVEL_ENUM)
.for_field(&console::user_message::um_level),
yajlpp::property_handler("message")
.with_obj_provider<attr_line_t, console::user_message>(
[](const yajlpp_provider_context& ypc,
console::user_message* root) { return &root->um_message; })
.with_children(attr_line_handlers),
yajlpp::property_handler("reason")
.with_obj_provider<attr_line_t, console::user_message>(
[](const yajlpp_provider_context& ypc,
console::user_message* root) { return &root->um_reason; })
.with_children(attr_line_handlers),
yajlpp::property_handler("snippets#")
.with_obj_provider<console::snippet, console::user_message>(
[](const yajlpp_provider_context& ypc,
console::user_message* root) {
root->um_snippets.resize(ypc.ypc_index + 1);
return &root->um_snippets[ypc.ypc_index];
})
.with_children(snippet_handlers),
yajlpp::property_handler("help")
.with_obj_provider<attr_line_t, console::user_message>(
[](const yajlpp_provider_context& ypc,
console::user_message* root) { return &root->um_help; })
.with_children(attr_line_handlers),
};
template<>
lnav::console::user_message
from_json(const std::string& json)
{
yajlpp_parse_context ypc("string", &user_message_handlers);
auto_mem<yajl_handle_t> handle(yajl_free);
lnav::console::user_message retval;
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
ypc.with_handle(handle);
ypc.with_obj(retval);
ypc.parse(json);
ypc.complete_parse();
return retval;
}
} // namespace lnav

@ -48,6 +48,7 @@
#include <time.h>
#include "base/intern_string.hh"
#include "base/lnav.console.hh"
#include "base/result.h"
#include "byte_array.hh"
#include "config.h"
@ -229,6 +230,17 @@ finally(A act) // deduce action type
std::string ok_prefix(std::string msg);
std::string err_prefix(std::string msg);
Result<std::string, std::string> err_to_ok(std::string msg);
Result<std::string, lnav::console::user_message> err_to_ok(
lnav::console::user_message msg);
namespace lnav {
std::string to_json(const lnav::console::user_message& um);
std::string to_json(const attr_line_t& al);
template<typename T>
T from_json(const std::string& str);
} // namespace lnav
#endif

@ -638,7 +638,7 @@ bool
external_log_format::scan_for_partial(shared_buffer_ref& sbr,
size_t& len_out) const
{
if (this->elf_type != ELF_TYPE_TEXT) {
if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
return false;
}
@ -660,13 +660,26 @@ external_log_format::scan_for_partial(shared_buffer_ref& sbr,
return (int) len_out > pat->p_timestamp_end;
}
std::vector<lnav::console::snippet>
external_log_format::get_snippets() const
{
std::vector<lnav::console::snippet> retval;
for (const auto& src_pair : this->elf_format_sources) {
retval.emplace_back(lnav::console::snippet::from(src_pair.first, "")
.with_line(src_pair.second));
}
return retval;
}
log_format::scan_result_t
external_log_format::scan(logfile& lf,
std::vector<logline>& dst,
const line_info& li,
shared_buffer_ref& sbr)
{
if (this->elf_type == ELF_TYPE_JSON) {
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
yajlpp_parse_context& ypc = *(this->jlf_parse_context);
logline ll(li.li_file_range.fr_offset, 0, 0, LEVEL_INFO);
yajl_handle handle = this->jlf_yajl_handle.get();
@ -852,7 +865,7 @@ external_log_format::scan(logfile& lf,
= *mod_elf->elf_pattern_order[mod_pat_index];
if (mod_pat.p_pcre->match(mod_pc, mod_pi)) {
auto mod_level_cap
auto* mod_level_cap
= mod_pc[mod_pat.p_level_field_index];
level = mod_elf->convert_level(mod_pi, mod_level_cap);
@ -991,7 +1004,7 @@ external_log_format::annotate(uint64_t line_number,
struct line_range lr;
pcre_context::capture_t *cap, *body_cap, *module_cap = nullptr;
if (this->elf_type != ELF_TYPE_TEXT) {
if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
values = this->jlf_line_values;
sa = this->jlf_line_attrs;
return;
@ -1152,7 +1165,9 @@ external_log_format::rewrite(exec_context& ec,
}
auto _sg = ec.enter_source(
this->elf_name.to_string() + ":" + vd_iter->first.to_string(), 1);
this->elf_name.to_string() + ":" + vd_iter->first.to_string(),
1,
vd.vd_rewriter);
auto field_value
= execute_any(ec, vd.vd_rewriter).orElse(err_to_ok).unwrap();
struct line_range adj_origin
@ -1297,7 +1312,7 @@ external_log_format::get_subline(const logline& ll,
shared_buffer_ref& sbr,
bool full_message)
{
if (this->elf_type == ELF_TYPE_TEXT) {
if (this->elf_type == elf_type_t::ELF_TYPE_TEXT) {
return;
}
@ -1382,9 +1397,10 @@ external_log_format::get_subline(const logline& ll,
jfe.jfe_default_value.size());
break;
case JLF_VARIABLE:
lv_iter = find_if(this->jlf_line_values.begin(),
this->jlf_line_values.end(),
logline_value_cmp(&jfe.jfe_value));
lv_iter = find_if(
this->jlf_line_values.begin(),
this->jlf_line_values.end(),
logline_value_cmp(&jfe.jfe_value.pp_value));
if (lv_iter != this->jlf_line_values.end()) {
auto str = lv_iter->to_string();
size_t nl_pos = str.find('\n');
@ -1458,7 +1474,7 @@ external_log_format::get_subline(const logline& ll,
used_values[distance(this->jlf_line_values.begin(),
lv_iter)]
= true;
} else if (jfe.jfe_value == ts_field) {
} else if (jfe.jfe_value.pp_value == ts_field) {
struct line_range lr;
ssize_t ts_len;
char ts[64];
@ -1490,7 +1506,10 @@ external_log_format::get_subline(const logline& ll,
this->jlf_line_values.begin(), lv_iter)]
= true;
}
} else if (jfe.jfe_value == level_field) {
} else if (jfe.jfe_value.pp_value == level_field
|| jfe.jfe_value.pp_value
== this->elf_level_field)
{
this->json_append(jfe, ll.get_level_name(), -1);
} else {
this->json_append(jfe,
@ -1623,7 +1642,7 @@ external_log_format::get_subline(const logline& ll,
}
void
external_log_format::build(std::vector<std::string>& errors)
external_log_format::build(std::vector<lnav::console::user_message>& errors)
{
if (!this->lf_timestamp_field.empty()) {
auto& vd = this->elf_value_defs[this->lf_timestamp_field];
@ -1635,6 +1654,10 @@ external_log_format::build(std::vector<std::string>& errors)
vd->vd_meta.lvm_kind = value_kind_t::VALUE_TEXT;
vd->vd_internal = true;
}
if (startswith(this->elf_level_field.get(), "/")) {
this->elf_level_field
= intern_string::lookup(this->elf_level_field.get() + 1);
}
if (!this->elf_level_field.empty()
&& this->elf_value_defs.find(this->elf_level_field)
== this->elf_value_defs.end())
@ -1662,35 +1685,20 @@ external_log_format::build(std::vector<std::string>& errors)
if (!this->lf_timestamp_format.empty()) {
this->lf_timestamp_format.push_back(nullptr);
}
try {
this->elf_filename_pcre
= std::make_shared<pcrepp>(this->elf_file_pattern);
} catch (const pcrepp::error& e) {
errors.push_back("error:" + this->elf_name.to_string()
+ ".file-pattern:" + e.what());
}
for (auto iter = this->elf_patterns.begin();
iter != this->elf_patterns.end();
++iter)
{
pattern& pat = *iter->second;
if (pat.p_pcre == nullptr) {
continue;
}
if (pat.p_module_format) {
this->elf_has_module_format = true;
}
try {
pat.p_pcre = std::make_unique<pcrepp>(pat.p_string, PCRE_DOTALL);
} catch (const pcrepp::error& e) {
errors.push_back("error:" + this->elf_name.to_string() + ".regex["
+ iter->first + "]" + ":" + e.what());
errors.push_back("error:" + this->elf_name.to_string() + ".regex["
+ iter->first + "]" + ":" + pat.p_string);
errors.push_back("error:" + this->elf_name.to_string() + ".regex["
+ iter->first + "]" + ":"
+ std::string(e.e_offset, ' ') + "^");
continue;
}
for (pcre_named_capture::iterator name_iter = pat.p_pcre->named_begin();
name_iter != pat.p_pcre->named_end();
++name_iter)
@ -1772,12 +1780,18 @@ external_log_format::build(std::vector<std::string>& errors)
this->elf_pattern_order.push_back(iter->second);
}
if (this->elf_type != ELF_TYPE_TEXT) {
if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
if (!this->elf_patterns.empty()) {
errors.push_back("error:" + this->elf_name.to_string()
+ ": structured logs cannot have regexes");
}
if (this->elf_type == ELF_TYPE_JSON) {
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(
lnav::roles::symbol(this->elf_name.to_string()))
.append(" is not a valid log format"))
.with_reason("structured logs cannot have regexes")
.with_snippets(this->get_snippets()));
}
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(
this->elf_name.to_string());
this->jlf_yajl_handle.reset(
@ -1791,18 +1805,13 @@ external_log_format::build(std::vector<std::string>& errors)
} else {
if (this->elf_patterns.empty()) {
errors.push_back("error:" + this->elf_name.to_string()
+ ": no regexes specified for format");
}
}
for (auto& elf_level_pattern : this->elf_level_patterns) {
try {
elf_level_pattern.second.lp_pcre = std::make_shared<pcrepp>(
elf_level_pattern.second.lp_regex.c_str());
} catch (const pcrepp::error& e) {
errors.push_back("error:" + this->elf_name.to_string()
+ ".level:" + e.what());
errors.emplace_back(lnav::console::user_message::error(
attr_line_t()
.append_quoted(lnav::roles::symbol(
this->elf_name.to_string()))
.append(" is not a valid log format"))
.with_reason("no regexes specified")
.with_snippets(this->get_snippets()));
}
}
@ -1825,22 +1834,31 @@ external_log_format::build(std::vector<std::string>& errors)
{
if (this->lf_action_defs.find(*act_iter)
== this->lf_action_defs.end()) {
#if 0
errors.push_back("error:" + this->elf_name.to_string() + ":"
+ vd->vd_meta.lvm_name.get()
+ ": cannot find action -- " + (*act_iter));
#endif
}
}
}
if (this->elf_type == ELF_TYPE_TEXT && this->elf_samples.empty()) {
errors.push_back(
"error:" + this->elf_name.to_string()
+ ":no sample logs provided, all formats must have samples");
if (this->elf_type == elf_type_t::ELF_TYPE_TEXT
&& this->elf_samples.empty()) {
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(
lnav::roles::symbol(this->elf_name.to_string()))
.append(" is not a valid log format"))
.with_reason("log message samples must be included in a format "
"definition")
.with_snippets(this->get_snippets()));
}
for (auto& elf_sample : this->elf_samples) {
pcre_context_static<128> pc;
pcre_input pi(elf_sample.s_line);
pcre_input pi(elf_sample.s_line.pp_value);
bool found = false;
for (auto pat_iter = this->elf_pattern_order.begin();
@ -1853,100 +1871,136 @@ external_log_format::build(std::vector<std::string>& errors)
continue;
}
if (!pat.p_module_format
&& pat.p_pcre->name_index(this->lf_timestamp_field.to_string())
< 0)
{
errors.push_back("error:" + this->elf_name.to_string()
+ ":timestamp field '"
+ this->lf_timestamp_field.get()
+ "' not found in pattern -- " + pat.p_string);
if (!pat.p_pcre->match(pc, pi)) {
continue;
}
found = true;
if (pat.p_pcre->match(pc, pi)) {
if (pat.p_module_format) {
found = true;
continue;
}
pcre_context::capture_t* ts_cap
= pc[this->lf_timestamp_field.get()];
pcre_context::capture_t* level_cap
= pc[pat.p_level_field_index];
const char* ts = pi.get_substr_start(ts_cap);
ssize_t ts_len = pc[this->lf_timestamp_field.get()]->length();
const char* const* custom_formats
= this->get_timestamp_formats();
date_time_scanner dts;
struct timeval tv;
struct exttm tm;
if (ts_cap->c_begin == 0) {
pat.p_timestamp_end = ts_cap->c_end;
if (pat.p_module_format) {
continue;
}
if (pat.p_pcre->name_index(this->lf_timestamp_field.to_string())
< 0) {
attr_line_t notes;
bool first_note = true;
if (pat.p_pcre->p_named_count > 0) {
notes.append("the following captures are available:\n ");
}
found = true;
if (ts_len == -1
|| dts.scan(ts, ts_len, custom_formats, &tm, tv) == nullptr)
for (auto name_iter = pat.p_pcre->named_begin();
name_iter != pat.p_pcre->named_end();
++name_iter)
{
errors.push_back("error:" + this->elf_name.to_string()
+ ":invalid sample -- "
+ elf_sample.s_line);
errors.push_back("error:" + this->elf_name.to_string()
+ ":unrecognized timestamp format -- "
+ ts);
if (custom_formats == nullptr) {
for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr;
lpc++) {
off_t off = 0;
PTIMEC_FORMATS[lpc].pf_func(&tm, ts, off, ts_len);
errors.push_back(
" format: "
+ std::string(PTIMEC_FORMATS[lpc].pf_fmt)
+ "; matched: " + std::string(ts, off));
}
} else {
for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++)
{
off_t off = 0;
ptime_fmt(
custom_formats[lpc], &tm, ts, off, ts_len);
errors.push_back(
" format: " + std::string(custom_formats[lpc])
+ "; matched: " + std::string(ts, off));
}
if (!first_note) {
notes.append(", ");
}
notes.append(lnav::roles::symbol(name_iter->pnc_name));
first_note = false;
}
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("invalid value for property ")
.append_quoted(lnav::roles::symbol(
fmt::format(FMT_STRING("/{}/timestamp-field"),
this->elf_name))))
.with_reason(
attr_line_t()
.append_quoted(this->lf_timestamp_field)
.append(" was not found in the pattern at ")
.append(lnav::roles::symbol(pat.p_config_path)))
.with_note(notes)
.with_snippets(this->get_snippets()));
continue;
}
const auto* ts_cap = pc[this->lf_timestamp_field.get()];
const auto* level_cap = pc[pat.p_level_field_index];
const char* ts = pi.get_substr_start(ts_cap);
ssize_t ts_len = pc[this->lf_timestamp_field.get()]->length();
const char* const* custom_formats = this->get_timestamp_formats();
date_time_scanner dts;
struct timeval tv;
struct exttm tm;
log_level_t level = this->convert_level(pi, level_cap);
if (elf_sample.s_level != LEVEL_UNKNOWN) {
if (elf_sample.s_level != level) {
errors.push_back("error:" + this->elf_name.to_string()
+ ":invalid sample -- "
+ elf_sample.s_line);
errors.push_back(
"error:" + this->elf_name.to_string()
+ ":parsed level '" + level_names[level]
+ "' does not match expected level of '"
+ level_names[elf_sample.s_level] + "'");
if (ts_cap->c_begin == 0) {
pat.p_timestamp_end = ts_cap->c_end;
}
if (ts_len == -1
|| dts.scan(ts, ts_len, custom_formats, &tm, tv) == nullptr) {
attr_line_t notes;
notes.append("the following formats were tried:");
if (custom_formats == nullptr) {
for (int lpc = 0; PTIMEC_FORMATS[lpc].pf_fmt != nullptr;
lpc++) {
off_t off = 0;
PTIMEC_FORMATS[lpc].pf_func(&tm, ts, off, ts_len);
notes.append("\n ")
.append(ts, (size_t) ts_len)
.append("\n")
.append(2 + off, ' ')
.append(lnav::roles::comment("^ "))
.append_quoted(
lnav::roles::symbol(PTIMEC_FORMATS[lpc].pf_fmt))
.append(
lnav::roles::comment(" matched up to here"));
}
} else {
for (int lpc = 0; custom_formats[lpc] != nullptr; lpc++) {
off_t off = 0;
ptime_fmt(custom_formats[lpc], &tm, ts, off, ts_len);
notes.append("\n ")
.append(ts, (size_t) ts_len)
.append("\n")
.append(2 + off, ' ')
.append(lnav::roles::comment("^ "))
.append_quoted(
lnav::roles::symbol(custom_formats[lpc]))
.append(
lnav::roles::comment(" matched up to here"));
}
}
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("invalid sample log message ")
.append_quoted(elf_sample.s_line.pp_value))
.with_reason(attr_line_t("unrecognized timestamp -- ")
.append(ts, (size_t) ts_len))
.with_snippet(elf_sample.s_line.to_snippet())
.with_note(notes));
}
log_level_t level = this->convert_level(pi, level_cap);
if (elf_sample.s_level != LEVEL_UNKNOWN
&& elf_sample.s_level != level) {
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("invalid sample log message ")
.append_quoted(elf_sample.s_line.pp_value))
.with_reason(attr_line_t()
.append_quoted(lnav::roles::symbol(
level_names[level]))
.append(" does not match the expected "
"level of ")
.append_quoted(lnav::roles::symbol(
level_names[elf_sample.s_level])))
.with_snippet(elf_sample.s_line.to_snippet()));
}
}
if (!found) {
errors.push_back("error:" + this->elf_name.to_string()
+ ":invalid sample -- "
+ elf_sample.s_line);
if (!found && !this->elf_pattern_order.empty()) {
attr_line_t notes(
"the following shows how each pattern matched this sample:\n");
size_t max_name_width = 0;
for (auto pat_iter = this->elf_pattern_order.begin();
pat_iter != this->elf_pattern_order.end();
++pat_iter)
{
pattern& pat = *(*pat_iter);
notes.append(" ").append_quoted(elf_sample.s_line.pp_value);
for (const auto& pat_iter : this->elf_pattern_order) {
pattern& pat = *pat_iter;
if (!pat.p_pcre) {
continue;
@ -1954,19 +2008,42 @@ external_log_format::build(std::vector<std::string>& errors)
size_t partial_len = pat.p_pcre->match_partial(pi);
if (partial_len > 0) {
errors.push_back(
"error:" + this->elf_name.to_string()
+ ":partial sample matched -- "
+ elf_sample.s_line.substr(0, partial_len));
errors.push_back("error: against pattern "
+ (*pat_iter)->p_config_path + " -- "
+ (*pat_iter)->p_string);
} else {
errors.push_back("error:" + this->elf_name.to_string()
+ ":no partial match found");
notes.append("\n ")
.append(partial_len, ' ')
.append(lnav::roles::comment("^ "))
.append(lnav::roles::symbol(pat.p_name))
.append(lnav::roles::comment(" matched up to here"));
max_name_width = std::max(max_name_width, pat.p_name.size());
}
attr_line_t help;
for (const auto& pat_iter : this->elf_pattern_order) {
if (!pat_iter->p_pcre) {
help.append(
lnav::roles::symbol(fmt::format(FMT_STRING("{:{}}"),
pat_iter->p_name,
max_name_width)))
.append(" is invalid");
continue;
}
help
.append(lnav::roles::symbol(fmt::format(
FMT_STRING("{:{}}"), pat_iter->p_name, max_name_width)))
.append(" = ")
.append(pat_iter->p_pcre->get_pattern())
.append("\n");
}
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("invalid sample log message ")
.append_quoted(elf_sample.s_line.pp_value))
.with_reason("sample does not match any patterns")
.with_snippet(elf_sample.s_line.to_snippet())
.with_note(notes)
.with_help(help));
}
}
@ -2002,41 +2079,48 @@ external_log_format::build(std::vector<std::string>& errors)
= intern_string::lookup("__level__");
json_format_element& jfe = *iter;
if (startswith(jfe.jfe_value.get(), "/")) {
jfe.jfe_value = intern_string::lookup(jfe.jfe_value.get() + 1);
if (startswith(jfe.jfe_value.pp_value.get(), "/")) {
jfe.jfe_value.pp_value
= intern_string::lookup(jfe.jfe_value.pp_value.get() + 1);
}
if (!jfe.jfe_ts_format.empty()) {
if (!jfe.jfe_value.empty() && jfe.jfe_value != ts) {
if (!jfe.jfe_value.pp_value.empty() && jfe.jfe_value.pp_value != ts)
{
log_warning(
"%s:line-format[%d]:ignoring field '%s' since "
"timestamp-format was used",
this->elf_name.get(),
format_index,
jfe.jfe_value.get());
jfe.jfe_value.pp_value.get());
}
jfe.jfe_value = ts;
jfe.jfe_value.pp_value = ts;
}
switch (jfe.jfe_type) {
case JLF_VARIABLE: {
auto vd_iter = this->elf_value_defs.find(jfe.jfe_value);
if (jfe.jfe_value == ts) {
auto vd_iter
= this->elf_value_defs.find(jfe.jfe_value.pp_value);
if (jfe.jfe_value.pp_value == ts) {
this->elf_value_defs[this->lf_timestamp_field]
->vd_meta.lvm_hidden
= true;
} else if (jfe.jfe_value == level_field) {
} else if (jfe.jfe_value.pp_value == level_field) {
this->elf_value_defs[this->elf_level_field]
->vd_meta.lvm_hidden
= true;
} else if (vd_iter == this->elf_value_defs.end()) {
char index_str[32];
snprintf(index_str, sizeof(index_str), "%d", format_index);
errors.push_back(
"error:" + this->elf_name.to_string() + ":line-format["
+ index_str
+ "]:line format variable is not defined -- "
+ jfe.jfe_value.to_string());
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("invalid line format element ")
.append_quoted(lnav::roles::symbol(fmt::format(
FMT_STRING("/{}/line-format/{}/field"),
this->elf_name,
format_index))))
.with_reason(
attr_line_t()
.append_quoted(jfe.jfe_value.pp_value)
.append(" is not a defined value"))
.with_snippet(jfe.jfe_value.to_snippet()));
}
break;
}
@ -2053,30 +2137,44 @@ external_log_format::build(std::vector<std::string>& errors)
for (auto& hd_pair : this->elf_highlighter_patterns) {
external_log_format::highlighter_def& hd = hd_pair.second;
const std::string& pattern = hd.hd_pattern;
const char* errptr;
auto fg = styling::color_unit::make_empty();
auto bg = styling::color_unit::make_empty();
int eoff, attrs = 0;
if (!hd.hd_color.empty()) {
fg = styling::color_unit::from_str(hd.hd_color)
if (!hd.hd_color.pp_value.empty()) {
fg = styling::color_unit::from_str(hd.hd_color.pp_value)
.unwrapOrElse([&](const auto& msg) {
errors.push_back("error:" + this->elf_name.to_string()
+ ":highlighters/"
+ hd_pair.first.to_string()
+ "/color:" + msg);
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(hd.hd_color.pp_value)
.append(" is not a valid color value for "
"property ")
.append_quoted(lnav::roles::symbol(
hd.hd_color.pp_path.to_string())))
.with_reason(msg)
.with_snippet(hd.hd_color.to_snippet()));
return styling::color_unit::make_empty();
});
}
if (!hd.hd_background_color.empty()) {
bg = styling::color_unit::from_str(hd.hd_background_color)
if (!hd.hd_background_color.pp_value.empty()) {
bg = styling::color_unit::from_str(hd.hd_background_color.pp_value)
.unwrapOrElse([&](const auto& msg) {
errors.push_back("error:" + this->elf_name.to_string()
+ ":highlighters/"
+ hd_pair.first.to_string()
+ "/color:" + msg);
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(
hd.hd_background_color.pp_value)
.append(" is not a valid color value for "
"property ")
.append_quoted(lnav::roles::symbol(
hd.hd_background_color.pp_path
.to_string())))
.with_reason(msg)
.with_snippet(
hd.hd_background_color.to_snippet()));
return styling::color_unit::make_empty();
});
}
@ -2088,58 +2186,46 @@ external_log_format::build(std::vector<std::string>& errors)
attrs |= A_BLINK;
}
pcre* code = pcre_compile(
pattern.c_str(), PCRE_CASELESS, &errptr, &eoff, nullptr);
if (hd.hd_pattern != nullptr) {
pcre* code = pcre_compile(hd.hd_pattern->get_pattern().c_str(),
PCRE_CASELESS,
&errptr,
&eoff,
nullptr);
if (code == nullptr) {
errors.push_back("error:" + this->elf_name.to_string()
+ ":highlighters/" + hd_pair.first.to_string()
+ ":" + std::string(errptr));
errors.push_back("error:" + this->elf_name.to_string()
+ ":highlighters/" + hd_pair.first.to_string()
+ ":" + pattern);
errors.push_back("error:" + this->elf_name.to_string()
+ ":highlighters/" + hd_pair.first.to_string()
+ ":" + std::string(eoff, ' ') + "^");
} else {
this->lf_highlighters.emplace_back(code);
this->lf_highlighters.back()
.with_pattern(pattern)
.with_format_name(this->elf_name)
.with_color(fg, bg)
.with_attrs(attrs);
if (code == nullptr) {
log_error("unable to recompile highlighter pattern");
} else {
this->lf_highlighters.emplace_back(code);
this->lf_highlighters.back()
.with_pattern(hd.hd_pattern->get_pattern())
.with_format_name(this->elf_name)
.with_color(fg, bg)
.with_attrs(attrs);
}
}
}
}
void
external_log_format::register_vtabs(log_vtab_manager* vtab_manager,
std::vector<std::string>& errors)
external_log_format::register_vtabs(
log_vtab_manager* vtab_manager,
std::vector<lnav::console::user_message>& errors)
{
for (auto search_iter = this->elf_search_tables.begin();
search_iter != this->elf_search_tables.end();
++search_iter)
{
auto re_res = pcrepp::from_str(search_iter->second,
log_search_table::pattern_options());
if (re_res.isErr()) {
errors.emplace_back(fmt::format(
FMT_STRING("error:{}:{}:unable to compile regex '{}': {}"),
this->elf_name.get(),
search_iter->first.get(),
search_iter->second,
re_res.unwrapErr().ce_msg));
for (auto& elf_search_table : this->elf_search_tables) {
if (elf_search_table.second.std_pattern == nullptr) {
continue;
}
auto lst = std::make_shared<log_search_table>(re_res.unwrap(),
search_iter->first);
auto lst = std::make_shared<log_search_table>(
*elf_search_table.second.std_pattern, elf_search_table.first);
auto errmsg = vtab_manager->register_vtab(lst);
if (!errmsg.empty()) {
#if 0
errors.push_back("error:" + this->elf_name.to_string() + ":"
+ search_iter->first.to_string()
+ ":unable to register table -- " + errmsg);
#endif
}
}
}
@ -2156,7 +2242,7 @@ external_log_format::match_samples(const std::vector<sample>& samples) const
}
pcre_context_static<128> pc;
pcre_input pi(sample_iter.s_line);
pcre_input pi(sample_iter.s_line.pp_value);
if (pat.p_pcre->match(pc, pi)) {
return true;
@ -2317,7 +2403,7 @@ external_log_format::specialized(int fmt_lock)
retval->lf_pattern_locks.emplace_back(0, fmt_lock);
}
if (this->elf_type == ELF_TYPE_JSON) {
if (this->elf_type == elf_type_t::ELF_TYPE_JSON) {
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(
this->elf_name.to_string());
this->jlf_yajl_handle.reset(

@ -42,9 +42,9 @@ class module_format;
class external_log_format : public log_format {
public:
struct sample {
sample() : s_level(LEVEL_UNKNOWN){};
sample() : s_level(LEVEL_UNKNOWN) {}
std::string s_line;
positioned_property<std::string> s_line;
log_level_t s_level;
};
@ -87,9 +87,9 @@ public:
};
struct pattern {
std::string p_name;
std::string p_config_path;
std::string p_string;
std::unique_ptr<pcrepp> p_pcre;
std::shared_ptr<pcrepp> p_pcre;
std::vector<indexed_value_def> p_value_by_index;
std::vector<int> p_numeric_value_indexes;
int p_timestamp_field_index{-1};
@ -116,12 +116,8 @@ public:
};
external_log_format(const intern_string_t name)
: elf_column_count(0), elf_timestamp_divisor(1.0),
elf_level_field(intern_string::lookup("level", -1)),
: elf_level_field(intern_string::lookup("level", -1)),
elf_body_field(intern_string::lookup("body", -1)),
elf_container(false), elf_has_module_format(false),
elf_builtin_format(false), elf_type(ELF_TYPE_TEXT),
jlf_hide_extra(false), jlf_cached_offset(-1),
jlf_yajl_handle(nullptr, yajl_handle_deleter()), elf_name(name)
{
this->jlf_line_offsets.reserve(128);
@ -154,10 +150,10 @@ public:
string_attrs_t& sa,
std::string& value_out);
void build(std::vector<std::string>& errors);
void build(std::vector<lnav::console::user_message>& errors);
void register_vtabs(log_vtab_manager* vtab_manager,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
bool match_samples(const std::vector<sample>& samples) const;
@ -246,7 +242,7 @@ public:
jfe_text_transform(transform_t::NONE){};
json_log_field jfe_type;
intern_string_t jfe_value;
positioned_property<intern_string_t> jfe_value;
std::string jfe_default_value;
long long jfe_min_width;
long long jfe_max_width;
@ -263,7 +259,7 @@ public:
bool operator()(const json_format_element& jfe) const
{
return (this->jfc_type == jfe.jfe_type
&& this->jfc_field_name == jfe.jfe_value);
&& this->jfc_field_name == jfe.jfe_value.pp_value);
};
json_log_field jfc_type;
@ -273,9 +269,9 @@ public:
struct highlighter_def {
highlighter_def() : hd_underline(false), hd_blink(false) {}
std::string hd_pattern;
std::string hd_color;
std::string hd_background_color;
std::shared_ptr<pcrepp> hd_pattern;
positioned_property<std::string> hd_color;
positioned_property<std::string> hd_background_color;
bool hd_underline;
bool hd_blink;
};
@ -317,7 +313,7 @@ public:
std::string get_pattern_name(uint64_t line_number) const
{
if (this->elf_type != ELF_TYPE_TEXT) {
if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
return "structured";
}
int pat_index = this->pattern_index_for_line(line_number);
@ -326,15 +322,15 @@ public:
std::string get_pattern_regex(uint64_t line_number) const
{
if (this->elf_type != ELF_TYPE_TEXT) {
if (this->elf_type != elf_type_t::ELF_TYPE_TEXT) {
return "";
}
int pat_index = this->pattern_index_for_line(line_number);
return this->elf_pattern_order[pat_index]->p_string;
return this->elf_pattern_order[pat_index]->p_pcre->get_pattern();
}
log_level_t convert_level(const pcre_input& pi,
pcre_context::capture_t* level_cap) const
const pcre_context::capture_t* level_cap) const
{
log_level_t retval = LEVEL_INFO;
@ -360,12 +356,13 @@ public:
return retval;
}
typedef std::map<intern_string_t, module_format> mod_map_t;
using mod_map_t = std::map<intern_string_t, module_format>;
static mod_map_t MODULE_FORMATS;
static std::vector<std::shared_ptr<external_log_format>>
GRAPH_ORDERED_FORMATS;
std::set<std::string> elf_source_path;
std::unordered_map<std::string, int> elf_format_sources;
std::list<intern_string_t> elf_collision;
std::string elf_file_pattern;
std::set<file_format_t> elf_mime_types;
@ -377,8 +374,8 @@ public:
elf_value_defs;
std::vector<std::shared_ptr<value_def>> elf_value_def_order;
std::vector<std::shared_ptr<value_def>> elf_numeric_value_defs;
int elf_column_count;
double elf_timestamp_divisor;
int elf_column_count{0};
double elf_timestamp_divisor{1.0};
intern_string_t elf_level_field;
pcrepp elf_level_pointer;
intern_string_t elf_body_field;
@ -386,19 +383,24 @@ public:
intern_string_t elf_opid_field;
std::map<log_level_t, level_pattern> elf_level_patterns;
std::vector<std::pair<int64_t, log_level_t>> elf_level_pairs;
bool elf_container;
bool elf_has_module_format;
bool elf_builtin_format;
std::vector<std::pair<intern_string_t, std::string>> elf_search_tables;
bool elf_container{false};
bool elf_has_module_format{false};
bool elf_builtin_format{false};
struct search_table_def {
std::shared_ptr<pcrepp> std_pattern;
};
std::map<intern_string_t, search_table_def> elf_search_tables;
std::map<const intern_string_t, highlighter_def> elf_highlighter_patterns;
enum elf_type_t {
enum class elf_type_t {
ELF_TYPE_TEXT,
ELF_TYPE_JSON,
ELF_TYPE_CSV,
};
elf_type_t elf_type;
elf_type_t elf_type{elf_type_t::ELF_TYPE_TEXT};
void json_append_to_cache(const char* value, ssize_t len)
{
@ -455,12 +457,14 @@ public:
return lvm;
}
bool jlf_hide_extra;
std::vector<lnav::console::snippet> get_snippets() const;
bool jlf_hide_extra{false};
std::vector<json_format_element> jlf_line_format;
int jlf_line_format_init_count{0};
std::vector<logline_value> jlf_line_values;
off_t jlf_cached_offset;
off_t jlf_cached_offset{-1};
bool jlf_cached_full{false};
std::vector<off_t> jlf_line_offsets;
shared_buffer jlf_share_manager;

@ -34,7 +34,7 @@
#include <sys/types.h>
#include "attr_line.hh"
#include "base/string_attr_type.hh"
#include "byte_array.hh"
#include "log_level.hh"
#include "ptimec.hh"

@ -61,16 +61,17 @@ static void extract_metadata(const char* contents,
size_t len,
struct script_metadata& meta_out);
typedef std::map<intern_string_t, std::shared_ptr<external_log_format>>
log_formats_map_t;
using log_formats_map_t
= std::map<intern_string_t, std::shared_ptr<external_log_format>>;
static auto intern_lifetime = intern_string::get_table_lifetime();
static log_formats_map_t LOG_FORMATS;
struct userdata {
yajlpp_parse_context* ud_parse_context{nullptr};
ghc::filesystem::path ud_format_path;
std::vector<intern_string_t>* ud_format_names{nullptr};
std::vector<std::string>* ud_errors{nullptr};
std::vector<lnav::console::user_message>* ud_errors{nullptr};
};
static external_log_format*
@ -86,12 +87,19 @@ ensure_format(const yajlpp_provider_context& ypc, userdata* ud)
retval = LOG_FORMATS[name].get();
log_debug("Loading format -- %s", name.get());
}
retval->elf_source_path.insert(ud->ud_format_path.filename().string());
retval->elf_source_path.insert(ud->ud_format_path.parent_path().string());
if (find(formats->begin(), formats->end(), name) == formats->end()) {
formats->push_back(name);
}
auto srcs_iter
= retval->elf_format_sources.find(ud->ud_format_path.string());
if (srcs_iter == retval->elf_format_sources.end()) {
retval->elf_format_sources[ud->ud_format_path.string()]
= ud->ud_parse_context->get_line_number();
}
if (ud->ud_format_path.empty()) {
retval->elf_builtin_format = true;
}
@ -110,8 +118,9 @@ pattern_provider(const yajlpp_provider_context& ypc, external_log_format* elf)
}
if (pat->p_config_path.empty()) {
pat->p_config_path
= elf->get_name().to_string() + "/regex/" + regex_name;
pat->p_name = regex_name;
pat->p_config_path = fmt::format(
FMT_STRING("/{}/regex/{}"), elf->get_name(), regex_name);
}
return pat.get();
@ -172,16 +181,13 @@ read_format_bool(yajlpp_parse_context* ypc, int val)
auto elf = (external_log_format*) ypc->ypc_obj_stack.top();
auto field_name = ypc->get_path_fragment(1);
if (field_name == "convert-to-local-time")
if (field_name == "convert-to-local-time") {
elf->lf_date_time.dts_local_time = val;
else if (field_name == "json") {
} else if (field_name == "json") {
if (val) {
elf->elf_type = external_log_format::ELF_TYPE_JSON;
elf->elf_type = external_log_format::elf_type_t::ELF_TYPE_JSON;
}
} else if (field_name == "hide-extra")
elf->jlf_hide_extra = val;
else if (field_name == "multiline")
elf->lf_multiline = val;
}
return 1;
}
@ -194,11 +200,16 @@ read_format_double(yajlpp_parse_context* ypc, double val)
if (field_name == "timestamp-divisor") {
if (val <= 0) {
fprintf(stderr,
"error:%s: timestamp-divisor cannot be less "
"than or equal to zero\n",
ypc->get_path_fragment(0).c_str());
return 0;
ypc->report_error(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(fmt::to_string(val))
.append(" is not a valid value for ")
.append_quoted(lnav::roles::symbol(
ypc->get_full_path().to_string())))
.with_reason("value cannot be less than or equal to zero")
.with_snippet(ypc->get_snippet())
.with_help(ypc->ypc_current_handler->get_help_text(ypc)));
}
elf->elf_timestamp_divisor = val;
}
@ -214,11 +225,16 @@ read_format_int(yajlpp_parse_context* ypc, long long val)
if (field_name == "timestamp-divisor") {
if (val <= 0) {
fprintf(stderr,
"error:%s: timestamp-divisor cannot be less "
"than or equal to zero\n",
ypc->get_path_fragment(0).c_str());
return 0;
ypc->report_error(
lnav::console::user_message::error(
attr_line_t()
.append_quoted(fmt::to_string(val))
.append(" is not a valid value for ")
.append_quoted(lnav::roles::symbol(
ypc->get_full_path().to_string())))
.with_reason("value cannot be less than or equal to zero")
.with_snippet(ypc->get_snippet())
.with_help(ypc->ypc_current_handler->get_help_text(ypc)));
}
elf->elf_timestamp_divisor = val;
}
@ -238,37 +254,33 @@ read_format_field(yajlpp_parse_context* ypc,
auto field_name = ypc->get_path_fragment(1);
if (field_name == "file-pattern") {
elf->elf_file_pattern = value;
} else if (field_name == "level-field") {
elf->elf_level_field = intern_string::lookup(value);
try {
elf->elf_file_pattern = value;
elf->elf_filename_pcre
= std::make_shared<pcrepp>(elf->elf_file_pattern);
} catch (const pcrepp::error& e) {
pcrepp::compile_error ce;
ce.ce_msg = e.what();
ce.ce_offset = e.e_offset;
ypc->ypc_current_handler->report_regex_value_error(ypc, value, ce);
}
} else if (field_name == "level-pointer") {
auto pcre_res = pcrepp::from_str(value);
if (pcre_res.isErr()) {
ypc->ypc_error_reporter(
*ypc,
lnav_log_level_t::ERROR,
fmt::format(
FMT_STRING("error:{}:{}:invalid regular expression for "
"level-pointer -- {}"),
ypc->ypc_source,
ypc->get_line_number(),
pcre_res.unwrapErr().ce_msg)
.c_str());
auto pcre_error = pcre_res.unwrapErr();
ypc->ypc_current_handler->report_regex_value_error(
ypc, value, pcre_error);
} else {
elf->elf_level_pointer = pcre_res.unwrap();
}
} else if (field_name == "timestamp-field") {
elf->lf_timestamp_field = intern_string::lookup(value);
} else if (field_name == "body-field") {
elf->elf_body_field = intern_string::lookup(value);
} else if (field_name == "timestamp-format") {
elf->lf_timestamp_format.push_back(intern_string::lookup(value)->get());
} else if (field_name == "module-field") {
elf->elf_module_id_field = intern_string::lookup(value);
elf->elf_container = true;
} else if (field_name == "opid-field") {
elf->elf_opid_field = intern_string::lookup(value);
} else if (field_name == "mime-types") {
auto value_opt = ypc->ypc_current_handler->to_enum_value(value);
if (value_opt) {
@ -288,6 +300,17 @@ read_levels(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
log_level_t level = string2level(level_name_or_number.c_str());
elf->elf_level_patterns[level].lp_regex = regex;
try {
elf->elf_level_patterns[level].lp_pcre
= std::make_shared<pcrepp>(regex);
} catch (const pcrepp::error& e) {
pcrepp::compile_error ce;
ce.ce_msg = e.what();
ce.ce_offset = e.e_offset;
ypc->ypc_current_handler->report_regex_value_error(ypc, regex, ce);
}
return 1;
}
@ -312,8 +335,9 @@ read_action_def(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
auto val = std::string((const char*) str, len);
elf->lf_action_defs[action_name].ad_name = action_name;
if (field_name == "label")
if (field_name == "label") {
elf->lf_action_defs[action_name].ad_label = val;
}
return 1;
}
@ -376,39 +400,27 @@ read_json_constant(yajlpp_parse_context* ypc,
return 1;
}
static int
create_search_table(yajlpp_parse_context* ypc,
const unsigned char* str,
size_t len)
{
auto elf = (external_log_format*) ypc->ypc_obj_stack.top();
const intern_string_t table_name = ypc->get_path_fragment_i(2);
auto regex = std::string((const char*) str, len);
elf->elf_search_tables.emplace_back(table_name, regex);
return 1;
}
static struct json_path_container pattern_handlers = {
yajlpp::property_handler("pattern")
.with_synopsis("<message-regex>")
.with_description(
"The regular expression to match a log message and capture fields.")
.with_min_length(1)
.FOR_FIELD(external_log_format::pattern, p_string),
.for_field(&external_log_format::pattern::p_pcre),
yajlpp::property_handler("module-format")
.with_synopsis("<bool>")
.with_description(
"If true, this pattern will only be used to parse message bodies "
"of container formats, like syslog")
.for_field(&external_log_format::pattern::p_module_format)};
.for_field(&external_log_format::pattern::p_module_format),
};
static const json_path_handler_base::enum_value_t ALIGN_ENUM[]
= {{"left", external_log_format::json_format_element::align_t::LEFT},
{"right", external_log_format::json_format_element::align_t::RIGHT},
static const json_path_handler_base::enum_value_t ALIGN_ENUM[] = {
{"left", external_log_format::json_format_element::align_t::LEFT},
{"right", external_log_format::json_format_element::align_t::RIGHT},
json_path_handler_base::ENUM_TERMINATOR};
json_path_handler_base::ENUM_TERMINATOR,
};
static const json_path_handler_base::enum_value_t OVERFLOW_ENUM[] = {
{"abbrev", external_log_format::json_format_element::overflow_t::ABBREV},
@ -416,18 +428,20 @@ static const json_path_handler_base::enum_value_t OVERFLOW_ENUM[] = {
external_log_format::json_format_element::overflow_t::TRUNCATE},
{"dot-dot", external_log_format::json_format_element::overflow_t::DOTDOT},
json_path_handler_base::ENUM_TERMINATOR};
json_path_handler_base::ENUM_TERMINATOR,
};
static const json_path_handler_base::enum_value_t TRANSFORM_ENUM[]
= {{"none", external_log_format::json_format_element::transform_t::NONE},
{"uppercase",
external_log_format::json_format_element::transform_t::UPPERCASE},
{"lowercase",
external_log_format::json_format_element::transform_t::LOWERCASE},
{"capitalize",
external_log_format::json_format_element::transform_t::CAPITALIZE},
static const json_path_handler_base::enum_value_t TRANSFORM_ENUM[] = {
{"none", external_log_format::json_format_element::transform_t::NONE},
{"uppercase",
external_log_format::json_format_element::transform_t::UPPERCASE},
{"lowercase",
external_log_format::json_format_element::transform_t::LOWERCASE},
{"capitalize",
external_log_format::json_format_element::transform_t::CAPITALIZE},
json_path_handler_base::ENUM_TERMINATOR};
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container line_format_handlers = {
yajlpp::property_handler("field")
@ -435,78 +449,83 @@ static struct json_path_container line_format_handlers = {
.with_description(
"The name of the field to substitute at this position")
.with_min_length(1)
.FOR_FIELD(external_log_format::json_format_element, jfe_value),
.for_field(&external_log_format::json_format_element::jfe_value),
yajlpp::property_handler("default-value")
.with_synopsis("<string>")
.with_description(
"The default value for this position if the field is null")
.FOR_FIELD(external_log_format::json_format_element, jfe_default_value),
.for_field(
&external_log_format::json_format_element::jfe_default_value),
yajlpp::property_handler("timestamp-format")
.with_synopsis("<string>")
.with_min_length(1)
.with_description("The strftime(3) format for this field")
.FOR_FIELD(external_log_format::json_format_element, jfe_ts_format),
.for_field(&external_log_format::json_format_element::jfe_ts_format),
yajlpp::property_handler("min-width")
.with_min_value(0)
.with_synopsis("<size>")
.with_description("The minimum width of the field")
.FOR_FIELD(external_log_format::json_format_element, jfe_min_width),
.for_field(&external_log_format::json_format_element::jfe_min_width),
yajlpp::property_handler("max-width")
.with_min_value(0)
.with_synopsis("<size>")
.with_description("The maximum width of the field")
.FOR_FIELD(external_log_format::json_format_element, jfe_max_width),
.for_field(&external_log_format::json_format_element::jfe_max_width),
yajlpp::property_handler("align")
.with_synopsis("left|right")
.with_description(
"Align the text in the column to the left or right side")
.with_enum_values(ALIGN_ENUM)
.FOR_FIELD(external_log_format::json_format_element, jfe_align),
.for_field(&external_log_format::json_format_element::jfe_align),
yajlpp::property_handler("overflow")
.with_synopsis("abbrev|truncate|dot-dot")
.with_description("Overflow style")
.with_enum_values(OVERFLOW_ENUM)
.FOR_FIELD(external_log_format::json_format_element, jfe_overflow),
.for_field(&external_log_format::json_format_element::jfe_overflow),
yajlpp::property_handler("text-transform")
.with_synopsis("none|uppercase|lowercase|capitalize")
.with_description("Text transformation")
.with_enum_values(TRANSFORM_ENUM)
.FOR_FIELD(external_log_format::json_format_element,
jfe_text_transform)};
static const json_path_handler_base::enum_value_t KIND_ENUM[]
= {{"string", value_kind_t::VALUE_TEXT},
{"integer", value_kind_t::VALUE_INTEGER},
{"float", value_kind_t::VALUE_FLOAT},
{"boolean", value_kind_t::VALUE_BOOLEAN},
{"json", value_kind_t::VALUE_JSON},
{"struct", value_kind_t::VALUE_STRUCT},
{"quoted", value_kind_t::VALUE_QUOTED},
{"xml", value_kind_t::VALUE_XML},
.for_field(
&external_log_format::json_format_element::jfe_text_transform),
};
json_path_handler_base::ENUM_TERMINATOR};
static const json_path_handler_base::enum_value_t KIND_ENUM[] = {
{"string", value_kind_t::VALUE_TEXT},
{"integer", value_kind_t::VALUE_INTEGER},
{"float", value_kind_t::VALUE_FLOAT},
{"boolean", value_kind_t::VALUE_BOOLEAN},
{"json", value_kind_t::VALUE_JSON},
{"struct", value_kind_t::VALUE_STRUCT},
{"quoted", value_kind_t::VALUE_QUOTED},
{"xml", value_kind_t::VALUE_XML},
json_path_handler_base::ENUM_TERMINATOR,
};
static const json_path_handler_base::enum_value_t SCALE_OP_ENUM[]
= {{"identity", scale_op_t::SO_IDENTITY},
{"multiply", scale_op_t::SO_MULTIPLY},
{"divide", scale_op_t::SO_DIVIDE},
static const json_path_handler_base::enum_value_t SCALE_OP_ENUM[] = {
{"identity", scale_op_t::SO_IDENTITY},
{"multiply", scale_op_t::SO_MULTIPLY},
{"divide", scale_op_t::SO_DIVIDE},
json_path_handler_base::ENUM_TERMINATOR};
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container scaling_factor_handlers
= {yajlpp::pattern_property_handler("op")
.with_enum_values(SCALE_OP_ENUM)
.FOR_FIELD(scaling_factor, sf_op),
static struct json_path_container scaling_factor_handlers = {
yajlpp::pattern_property_handler("op")
.with_enum_values(SCALE_OP_ENUM)
.for_field(&scaling_factor::sf_op),
yajlpp::pattern_property_handler("value").FOR_FIELD(scaling_factor,
sf_value)};
yajlpp::pattern_property_handler("value").FOR_FIELD(scaling_factor,
sf_value),
};
static struct json_path_container scale_handlers = {
yajlpp::pattern_property_handler("(?<scale>[^/]+)")
@ -519,7 +538,7 @@ static struct json_path_container unit_handlers = {
.with_synopsis("<field-name>")
.with_description(
"The name of the field that contains the units for this field")
.FOR_FIELD(external_log_format::value_def, vd_unit_field),
.for_field(&external_log_format::value_def::vd_unit_field),
yajlpp::property_handler("scaling-factor")
.with_description("Transforms the numeric value by the given factor")
@ -528,7 +547,7 @@ static struct json_path_container unit_handlers = {
static struct json_path_container value_def_handlers = {
yajlpp::property_handler("kind")
.with_synopsis("string|integer|float|boolean|json|quoted")
.with_synopsis("<data-type>")
.with_description("The type of data in the field")
.with_enum_values(KIND_ENUM)
.for_field(&external_log_format::value_def::vd_meta,
@ -537,7 +556,7 @@ static struct json_path_container value_def_handlers = {
yajlpp::property_handler("collate")
.with_synopsis("<function>")
.with_description("The collating function to use for this column")
.FOR_FIELD(external_log_format::value_def, vd_collate),
.for_field(&external_log_format::value_def::vd_collate),
yajlpp::property_handler("unit")
.with_description("Unit definitions for this field")
@ -572,30 +591,31 @@ static struct json_path_container value_def_handlers = {
.with_synopsis("<command>")
.with_description(
"A command that will rewrite this field when pretty-printing")
.FOR_FIELD(external_log_format::value_def, vd_rewriter),
.for_field(&external_log_format::value_def::vd_rewriter),
yajlpp::property_handler("description")
.with_synopsis("<string>")
.with_description("A description of the field")
.FOR_FIELD(external_log_format::value_def, vd_description)};
.for_field(&external_log_format::value_def::vd_description),
};
static struct json_path_container highlighter_def_handlers = {
yajlpp::property_handler("pattern")
.with_synopsis("<regex>")
.with_description(
"A regular expression to highlight in logs of this format.")
.FOR_FIELD(external_log_format::highlighter_def, hd_pattern),
.for_field(&external_log_format::highlighter_def::hd_pattern),
yajlpp::property_handler("color")
.with_synopsis("#<hex>|<name>")
.with_description("The color to use when highlighting this pattern.")
.FOR_FIELD(external_log_format::highlighter_def, hd_color),
.for_field(&external_log_format::highlighter_def::hd_color),
yajlpp::property_handler("background-color")
.with_synopsis("#<hex>|<name>")
.with_description(
"The background color to use when highlighting this pattern.")
.FOR_FIELD(external_log_format::highlighter_def, hd_background_color),
.for_field(&external_log_format::highlighter_def::hd_background_color),
yajlpp::property_handler("underline")
.with_synopsis("<enabled>")
@ -605,43 +625,47 @@ static struct json_path_container highlighter_def_handlers = {
yajlpp::property_handler("blink")
.with_synopsis("<enabled>")
.with_description("Highlight this pattern by blinking.")
.for_field(&external_log_format::highlighter_def::hd_blink)};
static const json_path_handler_base::enum_value_t LEVEL_ENUM[]
= {{level_names[LEVEL_TRACE], LEVEL_TRACE},
{level_names[LEVEL_DEBUG5], LEVEL_DEBUG5},
{level_names[LEVEL_DEBUG4], LEVEL_DEBUG4},
{level_names[LEVEL_DEBUG3], LEVEL_DEBUG3},
{level_names[LEVEL_DEBUG2], LEVEL_DEBUG2},
{level_names[LEVEL_DEBUG], LEVEL_DEBUG},
{level_names[LEVEL_INFO], LEVEL_INFO},
{level_names[LEVEL_STATS], LEVEL_STATS},
{level_names[LEVEL_NOTICE], LEVEL_NOTICE},
{level_names[LEVEL_WARNING], LEVEL_WARNING},
{level_names[LEVEL_ERROR], LEVEL_ERROR},
{level_names[LEVEL_CRITICAL], LEVEL_CRITICAL},
{level_names[LEVEL_FATAL], LEVEL_FATAL},
json_path_handler_base::ENUM_TERMINATOR};
static struct json_path_container sample_handlers
= {yajlpp::property_handler("line")
.with_synopsis("<log-line>")
.with_description(
"A sample log line that should match a pattern in this format.")
.FOR_FIELD(external_log_format::sample, s_line),
yajlpp::property_handler("level")
.with_enum_values(LEVEL_ENUM)
.with_description("The expected level for this sample log line.")
.FOR_FIELD(external_log_format::sample, s_level)};
static const json_path_handler_base::enum_value_t TYPE_ENUM[]
= {{"text", external_log_format::elf_type_t::ELF_TYPE_TEXT},
{"json", external_log_format::elf_type_t::ELF_TYPE_JSON},
{"csv", external_log_format::elf_type_t::ELF_TYPE_CSV},
json_path_handler_base::ENUM_TERMINATOR};
.for_field(&external_log_format::highlighter_def::hd_blink),
};
static const json_path_handler_base::enum_value_t LEVEL_ENUM[] = {
{level_names[LEVEL_TRACE], LEVEL_TRACE},
{level_names[LEVEL_DEBUG5], LEVEL_DEBUG5},
{level_names[LEVEL_DEBUG4], LEVEL_DEBUG4},
{level_names[LEVEL_DEBUG3], LEVEL_DEBUG3},
{level_names[LEVEL_DEBUG2], LEVEL_DEBUG2},
{level_names[LEVEL_DEBUG], LEVEL_DEBUG},
{level_names[LEVEL_INFO], LEVEL_INFO},
{level_names[LEVEL_STATS], LEVEL_STATS},
{level_names[LEVEL_NOTICE], LEVEL_NOTICE},
{level_names[LEVEL_WARNING], LEVEL_WARNING},
{level_names[LEVEL_ERROR], LEVEL_ERROR},
{level_names[LEVEL_CRITICAL], LEVEL_CRITICAL},
{level_names[LEVEL_FATAL], LEVEL_FATAL},
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container sample_handlers = {
yajlpp::property_handler("line")
.with_synopsis("<log-line>")
.with_description(
"A sample log line that should match a pattern in this format.")
.for_field(&external_log_format::sample::s_line),
yajlpp::property_handler("level")
.with_enum_values(LEVEL_ENUM)
.with_description("The expected level for this sample log line.")
.for_field(&external_log_format::sample::s_level),
};
static const json_path_handler_base::enum_value_t TYPE_ENUM[] = {
{"text", external_log_format::elf_type_t::ELF_TYPE_TEXT},
{"json", external_log_format::elf_type_t::ELF_TYPE_JSON},
{"csv", external_log_format::elf_type_t::ELF_TYPE_CSV},
json_path_handler_base::ENUM_TERMINATOR,
};
static struct json_path_container regex_handlers = {
yajlpp::pattern_property_handler(R"((?<pattern_name>[^/]+))")
@ -659,14 +683,16 @@ static struct json_path_container level_handlers = {
.with_description("The regular expression used to match the log text "
"for this level. "
"For JSON logs with numeric levels, this should be "
"the number for the corresponding level.")};
"the number for the corresponding level."),
};
static struct json_path_container value_handlers
= {yajlpp::pattern_property_handler("(?<value_name>[^/]+)")
.with_description(
"The set of values captured by the log message patterns")
.with_obj_provider(value_def_provider)
.with_children(value_def_handlers)};
static struct json_path_container value_handlers = {
yajlpp::pattern_property_handler("(?<value_name>[^/]+)")
.with_description(
"The set of values captured by the log message patterns")
.with_obj_provider(value_def_provider)
.with_children(value_def_handlers),
};
static struct json_path_container highlight_handlers = {
yajlpp::pattern_property_handler(R"((?<highlight_name>[^/]+))")
@ -674,38 +700,54 @@ static struct json_path_container highlight_handlers = {
.with_obj_provider<external_log_format::highlighter_def,
external_log_format>(
[](const yajlpp_provider_context& ypc, external_log_format* root) {
return &(root->elf_highlighter_patterns[ypc.get_substr_i(0)]);
auto* retval
= &(root->elf_highlighter_patterns[ypc.get_substr_i(0)]);
return retval;
})
.with_children(highlighter_def_handlers)};
.with_children(highlighter_def_handlers),
};
static struct json_path_container action_def_handlers
= {json_path_handler("label", read_action_def),
json_path_handler("capture-output", read_action_bool),
json_path_handler("cmd#", read_action_cmd)};
static struct json_path_container action_def_handlers = {
json_path_handler("label", read_action_def),
json_path_handler("capture-output", read_action_bool),
json_path_handler("cmd#", read_action_cmd),
};
static struct json_path_container action_handlers
= {json_path_handler(pcrepp("(?<action_name>\\w+)"), read_action_def)
.with_children(action_def_handlers)};
static struct json_path_container action_handlers = {
json_path_handler(pcrepp("(?<action_name>\\w+)"), read_action_def)
.with_children(action_def_handlers),
};
static struct json_path_container search_table_def_handlers = {
json_path_handler("pattern", create_search_table)
json_path_handler("pattern")
.with_synopsis("<regex>")
.with_description("The regular expression for this search table."),
.with_description("The regular expression for this search table.")
.for_field(&external_log_format::search_table_def::std_pattern),
};
static struct json_path_container search_table_handlers
= {yajlpp::pattern_property_handler("\\w+")
.with_description(
"The set of search tables to be automatically defined")
.with_children(search_table_def_handlers)};
static struct json_path_container search_table_handlers = {
yajlpp::pattern_property_handler("(?<table_name>\\w+)")
.with_description(
"The set of search tables to be automatically defined")
.with_obj_provider<external_log_format::search_table_def,
external_log_format>(
[](const yajlpp_provider_context& ypc, external_log_format* root) {
auto* retval = &(root->elf_search_tables[ypc.get_substr_i(0)]);
return retval;
})
.with_children(search_table_def_handlers),
};
static const json_path_handler_base::enum_value_t MIME_TYPE_ENUM[]
= {{
"application/vnd.tcpdump.pcap",
file_format_t::PCAP,
},
static const json_path_handler_base::enum_value_t MIME_TYPE_ENUM[] = {
{
"application/vnd.tcpdump.pcap",
file_format_t::PCAP,
},
json_path_handler_base::ENUM_TERMINATOR};
json_path_handler_base::ENUM_TERMINATOR,
};
struct json_path_container format_handlers = {
yajlpp::property_handler("regex")
@ -721,10 +763,11 @@ struct json_path_container format_handlers = {
"automatically be converted to local time"),
json_path_handler("hide-extra", read_format_bool)
.with_description(
"Specifies whether extra values in JSON logs should be displayed"),
"Specifies whether extra values in JSON logs should be displayed")
.for_field(&external_log_format::jlf_hide_extra),
json_path_handler("multiline", read_format_bool)
.with_description(
"Indicates that log messages can span multiple lines"),
.with_description("Indicates that log messages can span multiple lines")
.for_field(&log_format::lf_multiline),
json_path_handler("timestamp-divisor", read_format_double)
.add_cb(read_format_int)
.with_synopsis("<number>")
@ -738,16 +781,19 @@ struct json_path_container format_handlers = {
.with_enum_values(MIME_TYPE_ENUM),
json_path_handler("level-field", read_format_field)
.with_description(
"The name of the level field in the log message pattern"),
"The name of the level field in the log message pattern")
.for_field(&external_log_format::elf_level_field),
json_path_handler("level-pointer", read_format_field)
.with_description("A regular-expression that matches the JSON-pointer "
"of the level property"),
json_path_handler("timestamp-field", read_format_field)
.with_description(
"The name of the timestamp field in the log message pattern"),
"The name of the timestamp field in the log message pattern")
.for_field(&log_format::lf_timestamp_field),
json_path_handler("body-field", read_format_field)
.with_description(
"The name of the body field in the log message pattern"),
"The name of the body field in the log message pattern")
.for_field(&external_log_format::elf_body_field),
json_path_handler("url", pcrepp("url#?"))
.add_cb(read_format_field)
.with_description("A URL with more information about this log format"),
@ -762,7 +808,8 @@ struct json_path_container format_handlers = {
"The name of the module field in the log message pattern"),
json_path_handler("opid-field", read_format_field)
.with_description(
"The name of the operation-id field in the log message pattern"),
"The name of the operation-id field in the log message pattern")
.for_field(&external_log_format::elf_opid_field),
yajlpp::property_handler("ordered-by-time")
.with_synopsis("<bool>")
.with_description(
@ -789,7 +836,7 @@ struct json_path_container format_handlers = {
.with_obj_provider(line_format_provider)
.add_cb(read_json_constant)
.with_children(line_format_handlers),
json_path_handler("search-table", create_search_table)
json_path_handler("search-table")
.with_description(
"Search tables to automatically define for this log format")
.with_children(search_table_handlers),
@ -802,7 +849,8 @@ struct json_path_container format_handlers = {
.with_synopsis("text|json|csv")
.with_description("The type of file that contains the log messages")
.with_enum_values(TYPE_ENUM)
.FOR_FIELD(external_log_format, elf_type)};
.for_field(&external_log_format::elf_type),
};
static int
read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
@ -814,12 +862,21 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
file_id)
== SUPPORTED_FORMAT_SCHEMAS.end())
{
fprintf(stderr,
"%s:%d: error: unsupported format $schema -- %s\n",
ypc->ypc_source.c_str(),
ypc->get_line_number(),
file_id.c_str());
return 0;
const auto* handler = ypc->ypc_current_handler;
attr_line_t notes{"expecting one of the following $schema values:"};
for (const auto& schema : SUPPORTED_FORMAT_SCHEMAS) {
notes.append("\n").append(
lnav::roles::symbol(fmt::format(FMT_STRING(" {}"), schema)));
}
ypc->report_error(
lnav::console::user_message::error(
attr_line_t("'")
.append(lnav::roles::symbol(file_id))
.append("' is not a supported log format $schema version"))
.with_snippet(ypc->get_snippet())
.with_note(notes)
.with_help(handler->get_help_text(ypc)));
}
return 1;
@ -910,38 +967,35 @@ write_sample_file()
static void
format_error_reporter(const yajlpp_parse_context& ypc,
lnav_log_level_t level,
const char* msg)
const lnav::console::user_message& msg)
{
if (level >= lnav_log_level_t::ERROR) {
struct userdata* ud = (userdata*) ypc.ypc_userdata;
struct userdata* ud = (userdata*) ypc.ypc_userdata;
ud->ud_errors->push_back(msg);
} else {
fprintf(stderr, "warning:%s\n", msg);
}
ud->ud_errors->emplace_back(msg);
}
std::vector<intern_string_t>
load_format_file(const ghc::filesystem::path& filename,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
std::vector<intern_string_t> retval;
struct userdata ud;
auto_fd fd;
log_info("loading formats from file: %s", filename.c_str());
yajlpp_parse_context ypc(filename, &root_format_handler);
ud.ud_parse_context = &ypc;
ud.ud_format_path = filename;
ud.ud_format_names = &retval;
ud.ud_errors = &errors;
yajlpp_parse_context ypc(filename, &root_format_handler);
ypc.ypc_userdata = &ud;
ypc.with_obj(ud);
if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) == -1) {
errors.emplace_back(fmt::format(
FMT_STRING("error: unable to open format file '{}' -- {}"),
filename.string(),
strerror(errno)));
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("unable to open format file: ")
.append(lnav::roles::file(filename.string())))
.with_errno_reason());
} else {
auto_mem<yajl_handle_t> handle(yajl_free);
char buffer[2048];
@ -955,11 +1009,13 @@ load_format_file(const ghc::filesystem::path& filename,
rc = read(fd, buffer, sizeof(buffer));
if (rc == 0) {
break;
} else if (rc == -1) {
errors.emplace_back(fmt::format(
FMT_STRING("error:{}:unable to read file -- {}"),
filename.string(),
strerror(errno)));
}
if (rc == -1) {
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("unable to read format file: ")
.append(lnav::roles::file(filename.string())))
.with_errno_reason());
break;
}
if (offset == 0 && (rc > 2) && (buffer[0] == '#')
@ -983,7 +1039,7 @@ load_format_file(const ghc::filesystem::path& filename,
static void
load_from_path(const ghc::filesystem::path& path,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
auto format_path = path / "formats/*/*.json";
static_root_mem<glob_t, globfree> gl;
@ -1015,11 +1071,9 @@ load_from_path(const ghc::filesystem::path& path,
void
load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
auto default_source = lnav::paths::dotlnav() / "default";
yajlpp_parse_context ypc_builtin(default_source.string(),
&root_format_handler);
std::vector<intern_string_t> retval;
struct userdata ud;
yajl_handle handle;
@ -1028,7 +1082,9 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
log_debug("Loading default formats");
for (const auto& bsf : lnav_format_json) {
yajlpp_parse_context ypc_builtin(bsf.get_name(), &root_format_handler);
handle = yajl_alloc(&ypc_builtin.ypc_callbacks, nullptr, &ypc_builtin);
ud.ud_parse_context = &ypc_builtin;
ud.ud_format_names = &retval;
ud.ud_errors = &errors;
ypc_builtin.with_obj(ud)
@ -1043,8 +1099,10 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
handle, 1, (const unsigned char*) sf.data(), sf.length());
errors.emplace_back(
fmt::format(FMT_STRING("builtin: invalid json -- {}"),
reinterpret_cast<char*>(msg)));
lnav::console::user_message::error("invalid json")
.with_snippet(lnav::console::snippet::from(
ypc_builtin.ypc_source, attr_line_t((const char*) msg)))
.with_errno_reason());
yajl_free_error(handle, msg);
}
ypc_builtin.complete_parse();
@ -1154,7 +1212,7 @@ load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
static void
exec_sql_in_path(sqlite3* db,
const ghc::filesystem::path& path,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
auto format_path = path / "formats/*/*.sql";
static_root_mem<glob_t, globfree> gl;
@ -1172,10 +1230,11 @@ exec_sql_in_path(sqlite3* db,
sql_execute_script(
db, filename.c_str(), content.c_str(), errors);
} else {
errors.emplace_back(fmt::format(
FMT_STRING("error:unable to read file '{}' -- {}"),
filename.string(),
read_res.unwrapErr()));
errors.emplace_back(
lnav::console::user_message::error(
attr_line_t("unable to read format file: ")
.append(lnav::roles::file(filename.string())))
.with_reason(read_res.unwrapErr()));
}
}
}
@ -1184,7 +1243,7 @@ exec_sql_in_path(sqlite3* db,
void
load_format_extra(sqlite3* db,
const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
for (const auto& extra_path : extra_paths) {
exec_sql_in_path(db, extra_path, errors);
@ -1275,7 +1334,7 @@ find_format_scripts(const std::vector<ghc::filesystem::path>& extra_paths,
void
load_format_vtabs(log_vtab_manager* vtab_manager,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
auto& root_formats = LOG_FORMATS;

@ -38,22 +38,24 @@
#include <sqlite3.h>
#include "base/intern_string.hh"
#include "base/lnav.console.hh"
#include "ghc/filesystem.hpp"
class log_vtab_manager;
std::vector<intern_string_t> load_format_file(
const ghc::filesystem::path& filename, std::vector<std::string>& errors);
const ghc::filesystem::path& filename,
std::vector<lnav::console::user_message>& errors);
void load_formats(const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
void load_format_vtabs(log_vtab_manager* vtab_manager,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
void load_format_extra(sqlite3* db,
const std::vector<ghc::filesystem::path>& extra_paths,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
struct script_metadata {
ghc::filesystem::path sm_path;

@ -39,8 +39,8 @@ public:
int start,
int end,
chtype& ch,
view_colors::role_t& role_out,
view_colors::role_t& bar_role_out)
role_t& role_out,
role_t& bar_role_out)
{
textview_curses* tc = (textview_curses*) &lv;
vis_bookmarks& bm = tc->get_bookmarks();
@ -63,13 +63,13 @@ public:
}
next = bm[&logfile_sub_source::BM_ERRORS].next(vis_line_t(start));
if (next != -1 && next <= end) {
role_out = view_colors::VCR_ERROR;
bar_role_out = view_colors::VCR_SCROLLBAR_ERROR;
role_out = role_t::VCR_ERROR;
bar_role_out = role_t::VCR_SCROLLBAR_ERROR;
} else {
next = bm[&logfile_sub_source::BM_WARNINGS].next(vis_line_t(start));
if (next != -1 && next <= end) {
role_out = view_colors::VCR_WARNING;
bar_role_out = view_colors::VCR_SCROLLBAR_WARNING;
role_out = role_t::VCR_WARNING;
bar_role_out = role_t::VCR_SCROLLBAR_WARNING;
}
}
};

@ -32,17 +32,20 @@
#include "base/lnav_log.hh"
#include "base/string_util.hh"
#include "config.h"
#include "lnav_util.hh"
#include "logfile_sub_source.hh"
#include "sql_util.hh"
#include "vtab_module.hh"
#include "yajlpp/json_op.hh"
#include "yajlpp/yajlpp_def.hh"
using namespace lnav::roles::literals;
static auto intern_lifetime = intern_string::get_table_lifetime();
static struct log_cursor log_cursor_latest;
struct _log_vtab_data log_vtab_data;
thread_local _log_vtab_data log_vtab_data;
static const char* LOG_COLUMNS = R"( (
log_line INTEGER PRIMARY KEY, -- The line number for the log message
@ -947,12 +950,13 @@ vt_best_index(sqlite3_vtab* tab, sqlite3_index_info* p_info)
return SQLITE_OK;
}
static struct json_path_container tags_handler
= {json_path_handler("#")
.with_synopsis("<tag>")
.with_description("A tag for the log line")
.with_pattern(R"(^#[^\s]+$)")
.FOR_FIELD(bookmark_metadata, bm_tags)};
static struct json_path_container tags_handler = {
json_path_handler("#")
.with_synopsis("tag")
.with_description("A tag for the log line")
.with_pattern(R"(^#[^\s]+$)")
.FOR_FIELD(bookmark_metadata, bm_tags),
};
static int
vt_update(sqlite3_vtab* tab,
@ -972,17 +976,17 @@ vt_update(sqlite3_vtab* tab,
std::map<content_line_t, bookmark_metadata>& bm
= vt->lss->get_user_bookmark_metadata();
const unsigned char* part_name
= sqlite3_value_text(argv[2 + VT_COL_PARTITION]);
const unsigned char* log_comment
const auto* part_name = sqlite3_value_text(argv[2 + VT_COL_PARTITION]);
const auto* log_comment
= sqlite3_value_text(argv[2 + VT_COL_LOG_COMMENT]);
const unsigned char* log_tags
= sqlite3_value_text(argv[2 + VT_COL_LOG_TAGS]);
const auto* log_tags = sqlite3_value_text(argv[2 + VT_COL_LOG_TAGS]);
bookmark_metadata tmp_bm;
if (log_tags) {
std::vector<std::string> errors;
yajlpp_parse_context ypc(log_vtab_data.lvd_source, &tags_handler);
std::vector<lnav::console::user_message> errors;
yajlpp_parse_context ypc(
fmt::format(FMT_STRING("{}.log_tags"), vt->vi->get_name()),
&tags_handler);
auto_mem<yajl_handle_t> handle(yajl_free);
handle = yajl_alloc(&ypc.ypc_callbacks, nullptr, &ypc);
@ -990,19 +994,31 @@ vt_update(sqlite3_vtab* tab,
ypc.ypc_line_number = log_vtab_data.lvd_line_number;
ypc.with_handle(handle)
.with_error_reporter([](const yajlpp_parse_context& ypc,
lnav_log_level_t level,
const char* msg) {
auto& errors
= *((std::vector<std::string>*) ypc.ypc_userdata);
auto msg) {
auto& errors = *((std::vector<lnav::console::user_message>*)
ypc.ypc_userdata);
errors.emplace_back(msg);
})
.with_obj(tmp_bm);
ypc.parse(log_tags, strlen((const char*) log_tags));
ypc.complete_parse();
ypc.parse_doc(string_fragment{log_tags});
if (!errors.empty()) {
auto all_errors
= fmt::format(FMT_STRING("{}"), fmt::join(errors, "\n"));
tab->zErrMsg = sqlite3_mprintf("%s", all_errors.c_str());
auto top_error
= lnav::console::user_message::error(
attr_line_t("invalid value for ")
.append_quoted("log_tags"_symbol)
.append(" column of table ")
.append_quoted(lnav::roles::symbol(
vt->vi->get_name().to_string())))
.with_reason(errors[0].to_attr_line({}))
.with_snippet(
lnav::console::snippet::from(
log_vtab_data.lvd_source,
log_vtab_data.lvd_content)
.with_line(log_vtab_data.lvd_line_number));
auto json_error = lnav::to_json(top_error);
tab->zErrMsg
= sqlite3_mprintf("lnav-error:%s", json_error.c_str());
log_debug("dump %s", json_error.c_str());
return SQLITE_ERROR;
}
}

@ -207,25 +207,30 @@ protected:
typedef int (*sql_progress_callback_t)(const log_cursor& lc);
typedef void (*sql_progress_finished_callback_t)();
extern struct _log_vtab_data {
struct _log_vtab_data {
sql_progress_callback_t lvd_progress;
sql_progress_finished_callback_t lvd_finished;
std::string lvd_source;
int lvd_line_number{0};
} log_vtab_data;
attr_line_t lvd_content;
};
extern thread_local _log_vtab_data log_vtab_data;
class sql_progress_guard {
public:
sql_progress_guard(sql_progress_callback_t cb,
sql_progress_finished_callback_t fcb,
const std::string& source,
int line_number)
int line_number,
const attr_line_t& content)
{
log_vtab_data.lvd_progress = cb;
log_vtab_data.lvd_finished = fcb;
log_vtab_data.lvd_source = source;
log_vtab_data.lvd_line_number = line_number;
};
log_vtab_data.lvd_content = content;
}
~sql_progress_guard()
{
@ -236,7 +241,8 @@ public:
log_vtab_data.lvd_finished = nullptr;
log_vtab_data.lvd_source.clear();
log_vtab_data.lvd_line_number = 0;
};
log_vtab_data.lvd_content.clear();
}
};
class log_vtab_manager {

@ -34,7 +34,7 @@
#include <sqlite3.h>
#include "ansi_scrubber.hh"
#include "base/ansi_scrubber.hh"
#include "base/humanize.time.hh"
#include "base/string_util.hh"
#include "command_executor.hh"
@ -371,7 +371,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
lr.lr_start = time_offset_end;
lr.lr_end = -1;
value_out.emplace_back(lr, view_curses::VC_STYLE.value(attrs));
value_out.emplace_back(lr, VC_STYLE.value(attrs));
if (this->lss_token_line->get_msg_level() == log_level_t::LEVEL_INVALID) {
for (auto& token_attr : this->lss_token_attrs) {
@ -381,7 +381,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
value_out.emplace_back(
token_attr.sa_range,
view_curses::VC_ROLE.value(view_colors::VCR_INVALID_MSG));
VC_ROLE.value(role_t::VCR_INVALID_MSG));
}
}
@ -411,7 +411,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
value_out.emplace_back(
ident_range,
view_curses::VC_ROLE.value(view_colors::VCR_IDENTIFIER));
VC_ROLE.value(role_t::VCR_IDENTIFIER));
}
if (this->lss_token_shift_size) {
@ -441,7 +441,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
} else if (is_last_for_file) {
graph = ACS_LLCORNER;
}
value_out.emplace_back(lr, view_curses::VC_GRAPHIC.value(graph));
value_out.emplace_back(lr, VC_GRAPHIC.value(graph));
if (!(this->lss_token_flags & RF_FULL)) {
bookmark_vector<vis_line_t>& bv_search
@ -453,13 +453,13 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
lr.lr_start = 0;
lr.lr_end = 1;
value_out.emplace_back(lr,
view_curses::VC_STYLE.value(A_REVERSE));
VC_STYLE.value(A_REVERSE));
}
}
}
value_out.emplace_back(lr,
view_curses::VC_STYLE.value(vc.attrs_for_ident(
VC_STYLE.value(vc.attrs_for_ident(
this->lss_token_file->get_filename())));
if (this->lss_flags & F_FILENAME || this->lss_flags & F_BASENAME) {
@ -472,7 +472,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
lr.lr_start = 0;
lr.lr_end = file_offset_end + 1;
value_out.emplace_back(lr,
view_curses::VC_STYLE.value(vc.attrs_for_ident(
VC_STYLE.value(vc.attrs_for_ident(
this->lss_token_file->get_filename())));
}
@ -484,25 +484,25 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
shift_string_attrs(value_out, 0, time_offset_end);
value_out.emplace_back(
lr, view_curses::VC_ROLE.value(view_colors::VCR_OFFSET_TIME));
lr, VC_ROLE.value(role_t::VCR_OFFSET_TIME));
value_out.emplace_back(line_range(12, 13),
view_curses::VC_GRAPHIC.value(ACS_VLINE));
VC_GRAPHIC.value(ACS_VLINE));
view_colors::role_t bar_role = view_colors::VCR_NONE;
role_t bar_role = role_t::VCR_NONE;
switch (this->get_line_accel_direction(vis_line_t(row))) {
case log_accel::A_STEADY:
break;
case log_accel::A_DECEL:
bar_role = view_colors::VCR_DIFF_DELETE;
bar_role = role_t::VCR_DIFF_DELETE;
break;
case log_accel::A_ACCEL:
bar_role = view_colors::VCR_DIFF_ADD;
bar_role = role_t::VCR_DIFF_ADD;
break;
}
if (bar_role != view_colors::VCR_NONE) {
if (bar_role != role_t::VCR_NONE) {
value_out.emplace_back(line_range(12, 13),
view_curses::VC_ROLE.value(bar_role));
VC_ROLE.value(bar_role));
}
}
@ -550,7 +550,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
if (time_range.lr_end != -1) {
value_out.emplace_back(
time_range,
view_curses::VC_ROLE.value(view_colors::VCR_ADJUSTED_TIME));
VC_ROLE.value(role_t::VCR_ADJUSTED_TIME));
}
}
@ -561,7 +561,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
if (time_range.lr_end != -1) {
value_out.emplace_back(
time_range,
view_curses::VC_ROLE.value(view_colors::VCR_SKEWED_TIME));
VC_ROLE.value(role_t::VCR_SKEWED_TIME));
}
}
@ -584,11 +584,11 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
} else {
color = COLOR_RED;
value_out.emplace_back(
line_range{0, 1}, view_curses::VC_STYLE.value(A_BLINK));
line_range{0, 1}, VC_STYLE.value(A_BLINK));
}
}
value_out.emplace_back(line_range{0, 1},
view_curses::VC_BACKGROUND.value(color));
VC_BACKGROUND.value(color));
}
auto sql_filter_opt = this->get_sql_filter();
@ -605,7 +605,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
auto color = COLOR_YELLOW;
value_out.emplace_back(line_range{0, -1}, SA_ERROR.value(msg));
value_out.emplace_back(line_range{0, 1},
view_curses::VC_BACKGROUND.value(color));
VC_BACKGROUND.value(color));
}
}
}

@ -35,7 +35,7 @@
#include <string.h>
#include <sys/socket.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "config.h"
#include "sqlite-extension-func.hh"
#include "sqlite3.h"

@ -45,8 +45,8 @@
# include <sys/wait.h>
# include <unistd.h>
# include "auto_mem.hh"
# include "base/auto_fd.hh"
# include "base/auto_mem.hh"
# include "curl_looper.hh"
# include "line_buffer.hh"
# include "yajlpp/yajlpp.hh"

@ -59,7 +59,7 @@
#include <stdio.h>
#include <string.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/intern_string.hh"
#include "base/result.h"

@ -33,7 +33,7 @@
#include <string>
#include <vector>
#include "attr_line.hh"
#include "base/attr_line.hh"
#include "textview_curses.hh"
class plain_text_source
@ -55,18 +55,18 @@ public:
this->tds_lines.emplace_back(text.substr(start));
}
this->tds_longest_line = this->compute_longest_line();
};
}
plain_text_source(const std::vector<std::string>& text_lines)
{
this->replace_with(text_lines);
};
}
plain_text_source(const std::vector<attr_line_t>& text_lines)
{
this->tds_lines = text_lines;
this->tds_longest_line = this->compute_longest_line();
};
}
plain_text_source& replace_with(const attr_line_t& text_lines)
{
@ -74,23 +74,23 @@ public:
text_lines.split_lines(this->tds_lines);
this->tds_longest_line = this->compute_longest_line();
return *this;
};
}
plain_text_source& replace_with(const std::vector<std::string>& text_lines)
{
for (auto& str : text_lines) {
for (const auto& str : text_lines) {
this->tds_lines.emplace_back(str);
}
this->tds_longest_line = this->compute_longest_line();
return *this;
};
}
void clear()
{
this->tds_lines.clear();
this->tds_longest_line = 0;
this->tds_text_format = text_format_t::TF_UNKNOWN;
};
}
plain_text_source& truncate_to(size_t max_lines)
{
@ -98,22 +98,22 @@ public:
this->tds_lines.pop_back();
}
return *this;
};
}
size_t text_line_count()
{
return this->tds_lines.size();
};
}
bool empty() const
{
return this->tds_lines.empty();
};
}
size_t text_line_width(textview_curses& curses)
{
return this->tds_longest_line;
};
}
void text_value_for_line(textview_curses& tc,
int row,
@ -121,30 +121,35 @@ public:
line_flags_t flags)
{
value_out = this->tds_lines[row].get_string();
};
}
void text_attrs_for_line(textview_curses& tc,
int line,
string_attrs_t& value_out)
{
value_out = this->tds_lines[line].get_attrs();
};
}
size_t text_size_for_line(textview_curses& tc, int row, line_flags_t flags)
{
return this->tds_lines[row].length();
};
}
text_format_t get_text_format() const
{
return this->tds_text_format;
};
}
const std::vector<attr_line_t>& get_lines() const
{
return this->tds_lines;
}
plain_text_source& set_text_format(text_format_t format)
{
this->tds_text_format = format;
return *this;
};
}
nonstd::optional<location_history*> get_location_history()
{

@ -37,7 +37,7 @@
#include <sys/types.h>
#include "attr_line.hh"
#include "base/attr_line.hh"
#include "data_scanner.hh"
class pretty_printer {

@ -50,12 +50,12 @@ public:
static const char TOGGLE_MSG[] = "Press CTRL+P to show/hide";
this->tss_fields[TSF_TITLE].set_width(14);
this->tss_fields[TSF_TITLE].set_role(view_colors::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_role(role_t::VCR_STATUS_TITLE);
this->tss_fields[TSF_TITLE].set_value(" Preview Data ");
this->tss_fields[TSF_STITCH_TITLE].set_width(2);
this->tss_fields[TSF_STITCH_TITLE].set_stitch_value(
view_colors::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
view_colors::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
role_t::VCR_STATUS_STITCH_TITLE_TO_NORMAL,
role_t::VCR_STATUS_STITCH_NORMAL_TO_TITLE);
this->tss_fields[TSF_DESCRIPTION].set_share(1);
this->tss_fields[TSF_TOGGLE].set_width(strlen(TOGGLE_MSG) + 1);
this->tss_fields[TSF_TOGGLE].set_value(TOGGLE_MSG);

@ -49,6 +49,8 @@
#include "vtab_module.hh"
#include "yajlpp/yajlpp.hh"
using namespace std::chrono_literals;
#define ABORT_MSG "(Press " ANSI_BOLD("CTRL+]") " to abort)"
#define STR_HELPER(x) #x
@ -126,7 +128,7 @@ rl_set_help()
}
case LNM_SQL: {
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
auto lss = (logfile_sub_source*) log_view.get_sub_source();
auto* lss = (logfile_sub_source*) log_view.get_sub_source();
attr_line_t example_al;
if (log_view.get_inner_height() > 0) {
@ -241,6 +243,7 @@ rl_change(readline_curses* rc)
tc->get_highlights().erase({highlight_source_t::PREVIEW, "preview"});
tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"});
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
lnav_data.ld_user_message_source.clear();
lnav_data.ld_preview_source.clear();
lnav_data.ld_preview_status_source.get_description()
.set_cylon(false)
@ -266,9 +269,9 @@ rl_change(readline_curses* rc)
generation += 1;
}
auto os = tc->get_overlay_source();
auto* os = tc->get_overlay_source();
if (!args.empty() && os != nullptr) {
auto fos = dynamic_cast<field_overlay_source*>(os);
auto* fos = dynamic_cast<field_overlay_source*>(os);
if (fos != nullptr) {
if (generation == 0) {
@ -400,6 +403,7 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"});
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
tc->reload_data();
lnav_data.ld_user_message_source.clear();
switch (mode) {
case LNM_SEARCH:
@ -436,7 +440,8 @@ rl_search_internal(readline_curses* rc, ln_mode_t mode, bool complete = false)
}
} else {
lnav_data.ld_bottom_source.set_prompt("");
lnav_data.ld_bottom_source.grep_error(result.unwrapErr());
lnav_data.ld_bottom_source.grep_error(
result.unwrapErr().um_message.get_string());
}
lnav_data.ld_preview_view.reload_data();
@ -518,7 +523,7 @@ lnav_rl_abort(readline_curses* rc)
tc->get_highlights().erase({highlight_source_t::PREVIEW, "bodypreview"});
lnav_data.ld_log_source.set_preview_sql_filter(nullptr);
std::vector<std::string> errors;
std::vector<lnav::console::user_message> errors;
lnav_config = rollback_lnav_config;
reload_config(errors);
@ -577,13 +582,30 @@ rl_callback_int(readline_curses* rc, bool is_alt)
require(0);
break;
case LNM_COMMAND:
case LNM_COMMAND: {
rc->set_alt_value("");
rc->set_value(execute_command(ec, rc->get_value())
.map(ok_prefix)
.orElse(err_to_ok)
.unwrap());
ec.ec_source.top().s_content
= fmt::format(FMT_STRING(":{}"), rc->get_value());
auto exec_res = execute_command(ec, rc->get_value());
if (exec_res.isOk()) {
rc->set_value(exec_res.unwrap());
} else {
auto um = exec_res.unwrapErr();
lnav_data.ld_user_message_source.replace_with(
um.to_attr_line().rtrim());
for (const auto& line :
lnav_data.ld_user_message_source.get_lines()) {
log_debug("line -- %s", lnav::to_json(line).c_str());
}
lnav_data.ld_user_message_view.reload_data();
lnav_data.ld_user_message_expiration
= std::chrono::steady_clock::now() + 20s;
rc->set_value("");
}
ec.ec_source.top().s_content.clear();
break;
}
case LNM_USER:
rc->set_alt_value("");
@ -645,6 +667,7 @@ rl_callback_int(readline_curses* rc, bool is_alt)
break;
case LNM_SQL: {
ec.ec_source.top().s_content = rc->get_value();
auto result = execute_sql(ec, rc->get_value(), alt_msg);
db_label_source& dls = lnav_data.ld_db_row_source;
std::string prompt;
@ -659,8 +682,14 @@ rl_callback_int(readline_curses* rc, bool is_alt)
}
}
} else {
prompt = result.orElse(err_to_ok).unwrap();
auto um = result.unwrapErr();
lnav_data.ld_user_message_source.replace_with(
um.to_attr_line().rtrim());
lnav_data.ld_user_message_view.reload_data();
lnav_data.ld_user_message_expiration
= std::chrono::steady_clock::now() + 20s;
}
ec.ec_source.top().s_content.clear();
rc->set_value(prompt);
rc->set_alt_value(alt_msg);
@ -766,7 +795,7 @@ rl_display_matches(readline_curses* rc)
add_nl = false;
}
if (match == current_match) {
al.append(match, view_curses::VC_STYLE.value(A_REVERSE));
al.append(match, VC_STYLE.value(A_REVERSE));
} else {
al.append(match);
}

@ -37,6 +37,7 @@
#include <readline/history.h>
#include "base/lnav.console.hh"
#include "base/result.h"
#include "help_text.hh"
@ -52,7 +53,7 @@ typedef void (*readline_highlighter_t)(attr_line_t& line, int x);
*/
class readline_context {
public:
typedef Result<std::string, std::string> (*command_func_t)(
typedef Result<std::string, lnav::console::user_message> (*command_func_t)(
exec_context& ec, std::string cmdline, std::vector<std::string>& args);
typedef std::string (*prompt_func_t)(exec_context& ec,
const std::string& cmdline);

@ -54,8 +54,8 @@
#include <string>
#include <utility>
#include "ansi_scrubber.hh"
#include "auto_mem.hh"
#include "base/ansi_scrubber.hh"
#include "base/auto_mem.hh"
#include "base/lnav_log.hh"
#include "base/paths.hh"
#include "base/string_util.hh"
@ -1381,7 +1381,7 @@ readline_curses::do_update()
view_colors& vc = view_colors::singleton();
wmove(this->vc_window, this->get_actual_y(), this->vc_left);
wattron(this->vc_window, vc.attrs_for_role(view_colors::VCR_TEXT));
wattron(this->vc_window, vc.attrs_for_role(role_t::VCR_TEXT));
whline(this->vc_window, ' ', this->vc_width);
if (time(nullptr) > this->rc_value_expiration) {

@ -90,44 +90,53 @@ public:
void add_context(int id, readline_context& rc)
{
this->rc_contexts[id] = &rc;
};
}
void set_focus_action(const action& va)
{
this->rc_focus = va;
};
}
void set_change_action(const action& va)
{
this->rc_change = va;
};
}
void set_perform_action(const action& va)
{
this->rc_perform = va;
};
}
void set_alt_perform_action(const action& va)
{
this->rc_alt_perform = va;
};
}
void set_timeout_action(const action& va)
{
this->rc_timeout = va;
};
}
void set_abort_action(const action& va)
{
this->rc_abort = va;
};
}
void set_display_match_action(const action& va)
{
this->rc_display_match = va;
};
}
void set_display_next_action(const action& va)
{
this->rc_display_next = va;
};
}
void set_blur_action(const action& va)
{
this->rc_blur = va;
};
}
void set_completion_request_action(const action& va)
{
this->rc_completion_request = va;
@ -141,32 +150,34 @@ public:
}
this->rc_value_expiration = time(nullptr) + VALUE_EXPIRATION;
this->set_needs_update();
};
}
std::string get_value() const
{
return this->rc_value;
};
}
std::string get_line_buffer() const
{
return this->rc_line_buffer;
};
}
void set_alt_value(const std::string& value)
{
this->rc_alt_value = value;
};
}
std::string get_alt_value() const
{
return this->rc_alt_value;
};
}
void update_poll_set(std::vector<struct pollfd>& pollfds)
{
pollfds.push_back((struct pollfd){this->rc_pty[RCF_MASTER], POLLIN, 0});
pollfds.push_back(
(struct pollfd){this->rc_command_pipe[RCF_MASTER], POLLIN, 0});
};
}
void handle_key(int ch);
@ -190,7 +201,7 @@ public:
std::map<int, readline_context*>::const_iterator iter;
iter = this->rc_contexts.find(this->rc_active_context);
return iter->second;
};
}
void abort();
@ -208,7 +219,7 @@ public:
if (ioctl(this->rc_pty[RCF_MASTER], TIOCSWINSZ, &ws) == -1) {
throw error(errno);
}
};
}
void line_ready(const char* line);
@ -229,7 +240,7 @@ public:
for (int lpc = 0; values[lpc]; lpc++) {
this->add_possibility(context, type, values[lpc]);
}
};
}
void add_possibility(int context,
const std::string& type,
@ -239,7 +250,7 @@ public:
for (; first < last; first++) {
this->add_possibility(context, type, *first);
}
};
}
template<template<typename...> class Container>
void add_possibility(int context,
@ -249,7 +260,7 @@ public:
for (const auto& str : values) {
this->add_possibility(context, type, str);
}
};
}
void rem_possibility(int context,
const std::string& type,
@ -259,7 +270,7 @@ public:
const std::vector<std::string>& get_matches() const
{
return this->rc_matches;
};
}
int get_match_start() const
{
@ -271,7 +282,7 @@ public:
int get_max_match_length() const
{
return this->rc_max_match_length;
};
}
bool consume_ready_for_input()
{
@ -327,4 +338,5 @@ private:
action rc_blur;
action rc_completion_request;
};
#endif

@ -37,6 +37,7 @@
#include "shlex.hh"
#include "sql_help.hh"
#include "sql_util.hh"
#include "view_curses.hh"
static void readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip);
@ -72,9 +73,9 @@ find_matching_bracket(attr_line_t& al, int x, char left, char right)
{
view_colors& vc = view_colors::singleton();
int matching_bracket_attrs
= A_BOLD | A_REVERSE | vc.attrs_for_role(view_colors::VCR_OK);
= A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_OK);
int missing_bracket_attrs
= A_BOLD | A_REVERSE | vc.attrs_for_role(view_colors::VCR_ERROR);
= A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_ERROR);
bool is_lit = (left == 'Q');
const std::string& line = al.get_string();
int depth = 0;
@ -91,7 +92,7 @@ find_matching_bracket(attr_line_t& al, int x, char left, char right)
if (depth == 0) {
al.get_attrs().emplace_back(
line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(matching_bracket_attrs));
VC_STYLE.value(matching_bracket_attrs));
break;
} else {
depth -= 1;
@ -108,7 +109,7 @@ find_matching_bracket(attr_line_t& al, int x, char left, char right)
if (depth == 0) {
al.get_attrs().emplace_back(
line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(matching_bracket_attrs));
VC_STYLE.value(matching_bracket_attrs));
break;
} else {
depth -= 1;
@ -133,7 +134,7 @@ find_matching_bracket(attr_line_t& al, int x, char left, char right)
} else {
al.get_attrs().emplace_back(
line_range(is_lit ? lpc - 1 : lpc, lpc + 1),
view_curses::VC_STYLE.value(missing_bracket_attrs));
VC_STYLE.value(missing_bracket_attrs));
}
}
}
@ -141,7 +142,7 @@ find_matching_bracket(attr_line_t& al, int x, char left, char right)
if (depth > 0) {
al.get_attrs().emplace_back(
line_range(is_lit ? first_left - 1 : first_left, first_left + 1),
view_curses::VC_STYLE.value(missing_bracket_attrs));
VC_STYLE.value(missing_bracket_attrs));
}
}
@ -159,20 +160,21 @@ static void
readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
{
view_colors& vc = view_colors::singleton();
int special_char
= (A_BOLD | vc.attrs_for_role(view_colors::VCR_RE_SPECIAL));
int class_attrs = (A_BOLD | vc.attrs_for_role(view_colors::VCR_SYMBOL));
int repeated_char_attrs = vc.attrs_for_role(view_colors::VCR_RE_REPEAT);
int bracket_attrs = vc.attrs_for_role(view_colors::VCR_OK);
int special_char = (A_BOLD | vc.attrs_for_role(role_t::VCR_RE_SPECIAL));
int class_attrs = (A_BOLD | vc.attrs_for_role(role_t::VCR_SYMBOL));
int repeated_char_attrs = vc.attrs_for_role(role_t::VCR_RE_REPEAT);
int bracket_attrs = vc.attrs_for_role(role_t::VCR_OK);
int error_attrs
= (A_BOLD | A_REVERSE | vc.attrs_for_role(view_colors::VCR_ERROR));
= (A_BOLD | A_REVERSE | vc.attrs_for_role(role_t::VCR_ERROR));
static const char* brackets[] = {"[]",
"{}",
"()",
"QE",
static const char* brackets[] = {
"[]",
"{}",
"()",
"QE",
nullptr};
nullptr,
};
auto& line = al.get_string();
bool backslash_is_quoted = false;
@ -186,15 +188,14 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
case '+':
case '|':
case '.':
al.get_attrs().emplace_back(
line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(special_char));
al.get_attrs().emplace_back(line_range(lpc, lpc + 1),
VC_STYLE.value(special_char));
if ((line[lpc] == '*' || line[lpc] == '+')
&& check_re_prev(line, lpc)) {
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc),
view_curses::VC_STYLE.value(repeated_char_attrs));
VC_STYLE.value(repeated_char_attrs));
}
break;
case '?': {
@ -211,16 +212,15 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
break;
}
al.get_attrs().emplace_back(
lr, view_curses::VC_STYLE.value(bracket_attrs));
lr, VC_STYLE.value(bracket_attrs));
} else {
al.get_attrs().emplace_back(
lr, view_curses::VC_STYLE.value(special_char));
lr, VC_STYLE.value(special_char));
if (check_re_prev(line, lpc)) {
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc),
view_curses::VC_STYLE.value(
repeated_char_attrs));
VC_STYLE.value(repeated_char_attrs));
}
}
break;
@ -232,9 +232,8 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
case '}':
case '[':
case ']':
al.get_attrs().emplace_back(
line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(bracket_attrs));
al.get_attrs().emplace_back(line_range(lpc, lpc + 1),
VC_STYLE.value(bracket_attrs));
break;
}
}
@ -246,9 +245,8 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
switch (line[lpc]) {
case '\\':
backslash_is_quoted = true;
al.with_attr(
string_attr(line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(special_char)));
al.with_attr(string_attr(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(special_char)));
break;
case 'd':
case 'D':
@ -270,44 +268,38 @@ readline_regex_highlighter_int(attr_line_t& al, int x, int skip)
case 'G':
case 'Z':
case 'z':
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(class_attrs));
al.get_attrs().emplace_back(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(class_attrs));
break;
case ' ':
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(error_attrs));
al.get_attrs().emplace_back(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(error_attrs));
break;
case '0':
case 'x':
if (safe_read(line, lpc + 1) == '{') {
al.with_attr(string_attr(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(special_char)));
al.with_attr(string_attr(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(special_char)));
} else if (isdigit(safe_read(line, lpc + 1))
&& isdigit(safe_read(line, lpc + 2)))
{
al.with_attr(string_attr(
line_range(lpc - 1, lpc + 3),
view_curses::VC_STYLE.value(special_char)));
al.with_attr(string_attr(line_range(lpc - 1, lpc + 3),
VC_STYLE.value(special_char)));
} else {
al.with_attr(string_attr(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(error_attrs)));
al.with_attr(string_attr(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(error_attrs)));
}
break;
case 'Q':
case 'E':
al.with_attr(string_attr(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(bracket_attrs)));
al.with_attr(string_attr(line_range(lpc - 1, lpc + 1),
VC_STYLE.value(bracket_attrs)));
break;
default:
if (isdigit(line[lpc])) {
al.get_attrs().emplace_back(
line_range(lpc - 1, lpc + 1),
view_curses::VC_STYLE.value(special_char));
VC_STYLE.value(special_char));
}
break;
}
@ -338,7 +330,7 @@ readline_command_highlighter(attr_line_t& al, int x)
static const pcrepp COLOR_RE("(#(?:[a-fA-F0-9]{6}|[a-fA-F0-9]{3}))");
view_colors& vc = view_colors::singleton();
int keyword_attrs = (A_BOLD | vc.attrs_for_role(view_colors::VCR_KEYWORD));
int keyword_attrs = (A_BOLD | vc.attrs_for_role(role_t::VCR_KEYWORD));
const auto& line = al.get_string();
pcre_context_static<30> pc;
@ -349,7 +341,7 @@ readline_command_highlighter(attr_line_t& al, int x)
auto command = line.substr(0, ws_index);
if (ws_index != std::string::npos) {
al.get_attrs().emplace_back(line_range(1, ws_index),
view_curses::VC_STYLE.value(keyword_attrs));
VC_STYLE.value(keyword_attrs));
}
if (RE_PREFIXES.match(pc, pi)) {
readline_regex_highlighter_int(al, x, 1 + pc[0]->length());
@ -373,8 +365,7 @@ readline_command_highlighter(attr_line_t& al, int x)
.then([&](const auto& rgb_fg) {
al.get_attrs().emplace_back(
line_range{cap->c_begin, cap->c_begin + 1},
view_curses::VC_ROLE.value(
view_colors::VCR_COLOR_HINT));
VC_ROLE.value(role_t::VCR_COLOR_HINT));
});
}
}
@ -402,7 +393,7 @@ readline_command_highlighter(attr_line_t& al, int x)
value = "#" + value;
}
al.get_attrs().emplace_back(
lr, view_curses::VC_STYLE.value(vc.attrs_for_ident(value)));
lr, VC_STYLE.value(vc.attrs_for_ident(value)));
}
start = last;
@ -426,10 +417,10 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
auto& vc = view_colors::singleton();
int keyword_attrs = vc.attrs_for_role(view_colors::VCR_KEYWORD);
int symbol_attrs = vc.attrs_for_role(view_colors::VCR_SYMBOL);
int string_attrs = vc.attrs_for_role(view_colors::VCR_STRING);
int error_attrs = vc.attrs_for_role(view_colors::VCR_ERROR) | A_REVERSE;
int keyword_attrs = vc.attrs_for_role(role_t::VCR_KEYWORD);
int symbol_attrs = vc.attrs_for_role(role_t::VCR_SYMBOL);
int string_attrs = vc.attrs_for_role(role_t::VCR_STRING);
int error_attrs = vc.attrs_for_role(role_t::VCR_ERROR) | A_REVERSE;
pcre_context_static<30> pc;
pcre_input pi(al.get_string(), skip);
@ -444,8 +435,7 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
if (space != std::string::npos) {
lr.lr_end = space;
}
al.get_attrs().emplace_back(lr,
view_curses::VC_STYLE.value(keyword_attrs));
al.get_attrs().emplace_back(lr, VC_STYLE.value(keyword_attrs));
return;
}
@ -456,7 +446,7 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
if (line[cap->c_end] == '(') {
} else if (!lr.contains(x) && !lr.contains(x - 1)) {
al.get_attrs().emplace_back(lr, view_curses::VC_STYLE.value(attrs));
al.get_attrs().emplace_back(lr, VC_STYLE.value(attrs));
}
}
@ -466,7 +456,7 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
pcre_context::capture_t* cap = pc.all();
al.get_attrs().emplace_back(line_range(cap->c_begin, cap->c_end),
view_curses::VC_STYLE.value(keyword_attrs));
VC_STYLE.value(keyword_attrs));
}
for (size_t lpc = skip; lpc < line.length(); lpc++) {
@ -478,9 +468,8 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
case '!':
case '-':
case '+':
al.get_attrs().emplace_back(
line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(symbol_attrs));
al.get_attrs().emplace_back(line_range(lpc, lpc + 1),
VC_STYLE.value(symbol_attrs));
break;
}
}
@ -496,10 +485,10 @@ readline_sqlite_highlighter_int(attr_line_t& al, int x, int skip)
if (line[cap->c_end - 1] != '\'') {
sa.emplace_back(line_range(cap->c_begin, cap->c_begin + 1),
view_curses::VC_STYLE.value(error_attrs));
VC_STYLE.value(error_attrs));
lr.lr_start += 1;
}
sa.emplace_back(lr, view_curses::VC_STYLE.value(string_attrs));
sa.emplace_back(lr, VC_STYLE.value(string_attrs));
}
for (int lpc = 0; brackets[lpc]; lpc++) {
@ -517,9 +506,9 @@ void
readline_shlex_highlighter(attr_line_t& al, int x)
{
view_colors& vc = view_colors::singleton();
int special_char = (A_BOLD | vc.attrs_for_role(view_colors::VCR_SYMBOL));
int error_attrs = vc.attrs_for_role(view_colors::VCR_ERROR) | A_REVERSE;
int string_attrs = vc.attrs_for_role(view_colors::VCR_STRING);
int special_char = (A_BOLD | vc.attrs_for_role(role_t::VCR_SYMBOL));
int error_attrs = vc.attrs_for_role(role_t::VCR_ERROR) | A_REVERSE;
int string_attrs = vc.attrs_for_role(role_t::VCR_STRING);
const auto& str = al.get_string();
pcre_context::capture_t cap;
shlex_token_t token;
@ -529,15 +518,13 @@ readline_shlex_highlighter(attr_line_t& al, int x)
while (lexer.tokenize(cap, token)) {
switch (token) {
case shlex_token_t::ST_ERROR:
al.with_attr(
string_attr(line_range(cap.c_begin, cap.c_end),
view_curses::VC_STYLE.value(error_attrs)));
al.with_attr(string_attr(line_range(cap.c_begin, cap.c_end),
VC_STYLE.value(error_attrs)));
break;
case shlex_token_t::ST_TILDE:
case shlex_token_t::ST_ESCAPE:
al.with_attr(
string_attr(line_range(cap.c_begin, cap.c_end),
view_curses::VC_STYLE.value(special_char)));
al.with_attr(string_attr(line_range(cap.c_begin, cap.c_end),
VC_STYLE.value(special_char)));
break;
case shlex_token_t::ST_DOUBLE_QUOTE_START:
case shlex_token_t::ST_SINGLE_QUOTE_START:
@ -545,9 +532,8 @@ readline_shlex_highlighter(attr_line_t& al, int x)
break;
case shlex_token_t::ST_DOUBLE_QUOTE_END:
case shlex_token_t::ST_SINGLE_QUOTE_END:
al.with_attr(
string_attr(line_range(quote_start, cap.c_end),
view_curses::VC_STYLE.value(string_attrs)));
al.with_attr(string_attr(line_range(quote_start, cap.c_end),
VC_STYLE.value(string_attrs)));
quote_start = -1;
break;
case shlex_token_t::ST_VARIABLE_REF:
@ -559,16 +545,16 @@ readline_shlex_highlighter(attr_line_t& al, int x)
al.with_attr(string_attr(
line_range(cap.c_begin, cap.c_begin + 1 + extra),
view_curses::VC_STYLE.value(special_char)));
VC_STYLE.value(special_char)));
al.with_attr(string_attr(
line_range(cap.c_begin + 1 + extra, cap.c_end - extra),
view_curses::VC_STYLE.value(
x == cap.c_end || cap.contains(x) ? special_char
: attrs)));
VC_STYLE.value(x == cap.c_end || cap.contains(x)
? special_char
: attrs)));
if (extra) {
al.with_attr(
string_attr(line_range(cap.c_end - 1, cap.c_end),
view_curses::VC_STYLE.value(special_char)));
VC_STYLE.value(special_char)));
}
break;
}
@ -579,6 +565,6 @@ readline_shlex_highlighter(attr_line_t& al, int x)
if (quote_start != -1) {
al.with_attr(string_attr(line_range(quote_start, quote_start + 1),
view_curses::VC_STYLE.value(error_attrs)));
VC_STYLE.value(error_attrs)));
}
}

@ -32,7 +32,7 @@
#ifndef readline_highlighters_hh
#define readline_highlighters_hh
#include "view_curses.hh"
#include "base/attr_line.hh"
void readline_regex_highlighter(attr_line_t& line, int x);

@ -114,7 +114,7 @@ relative_time::from_str(const char* str, size_t len)
parse_error pe_out;
std::unordered_set<int> seen_tokens;
pe_out.pe_column = -1;
pe_out.pe_column = 0;
pe_out.pe_msg.clear();
while (true) {

@ -913,7 +913,7 @@ static struct json_path_container view_handlers
static struct json_path_container file_state_handlers = {
yajlpp::property_handler("visible")
.with_description("Indicates whether the file is visible or not")
.FOR_FIELD(file_state, fs_is_visible),
.for_field(&file_state::fs_is_visible),
};
static struct json_path_container file_states_handlers = {
@ -928,9 +928,9 @@ static struct json_path_container file_states_handlers = {
static struct json_path_container view_info_handlers = {
yajlpp::property_handler("save-time")
.FOR_FIELD(session_data_t, sd_save_time),
.for_field(&session_data_t::sd_save_time),
yajlpp::property_handler("time-offset")
.FOR_FIELD(session_data_t, sd_time_offset),
.for_field(&session_data_t::sd_time_offset),
json_path_handler("files#", read_files),
yajlpp::property_handler("file-states").with_children(file_states_handlers),
yajlpp::property_handler("views").with_children(view_handlers),

@ -33,6 +33,7 @@
#define lnav_session_data_hh
#include <map>
#include <set>
#include <string>
#include "view_helpers.hh"

@ -39,7 +39,7 @@
#include <string.h>
#include <sys/types.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/lnav_log.hh"
class shared_buffer;

@ -31,6 +31,7 @@
#include "spectro_source.hh"
#include "base/ansi_scrubber.hh"
#include "base/math_util.hh"
#include "config.h"
@ -155,7 +156,7 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
if (this->ss_cached_line_count == 0) {
value_out.with_ansi_string(ANSI_ROLE("error: no log data"),
view_colors::VCR_ERROR);
role_t::VCR_ERROR);
return true;
}
@ -169,12 +170,12 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
sizeof(buf),
ANSI_ROLE(" ") " 1-%'d " ANSI_ROLE(" ") " %'d-%'d " ANSI_ROLE(
" ") " %'d+",
view_colors::VCR_LOW_THRESHOLD,
role_t::VCR_LOW_THRESHOLD,
st.st_green_threshold - 1,
view_colors::VCR_MED_THRESHOLD,
role_t::VCR_MED_THRESHOLD,
st.st_green_threshold,
st.st_yellow_threshold - 1,
view_colors::VCR_HIGH_THRESHOLD,
role_t::VCR_HIGH_THRESHOLD,
st.st_yellow_threshold);
line.append(width / 2 - strlen(buf) / 3 - line.length(), ' ');
line.append(buf);
@ -185,7 +186,7 @@ spectrogram_source::list_value_for_overlay(const listview_curses& lv,
line.append(buf);
value_out.with_attr(string_attr(line_range(0, -1),
view_curses::VC_STYLE.value(A_UNDERLINE)));
VC_STYLE.value(A_UNDERLINE)));
return true;
}
@ -321,7 +322,7 @@ spectrogram_source::text_attrs_for_line(textview_curses& tc,
color = COLOR_RED;
}
value_out.emplace_back(line_range(lpc, lpc + 1),
view_curses::VC_STYLE.value(
VC_STYLE.value(
vc.ansi_color_pair(COLOR_BLACK, color)));
}
}

@ -38,7 +38,6 @@
#include <math.h>
#include <time.h>
#include "ansi_scrubber.hh"
#include "textview_curses.hh"
struct spectrogram_bounds {

@ -27,7 +27,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/fs_util.hh"
#include "base/injector.bind.hh"
#include "base/lnav_log.hh"
@ -40,7 +40,7 @@
#include "sqlitepp.hh"
#include "view_helpers.hh"
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
sql_cmd_dump(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -80,7 +80,7 @@ sql_cmd_dump(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
sql_cmd_read(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -143,7 +143,7 @@ sql_cmd_read(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
sql_cmd_schema(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
@ -159,7 +159,7 @@ sql_cmd_schema(exec_context& ec,
return Ok(retval);
}
static Result<std::string, std::string>
static Result<std::string, lnav::console::user_message>
sql_cmd_generic(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)

@ -34,7 +34,7 @@
#include <map>
#include "attr_line.hh"
#include "base/attr_line.hh"
#include "help_text.hh"
extern string_attr_type<void> SQL_COMMAND_ATTR;

@ -39,7 +39,7 @@
#include <stdio.h>
#include <string.h>
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/injector.hh"
#include "base/lnav_log.hh"
#include "base/string_util.hh"
@ -655,11 +655,11 @@ sql_compile_script(sqlite3* db,
const char* src_name,
const char* script_orig,
std::vector<sqlite3_stmt*>& stmts,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
const char* script = script_orig;
while (script != NULL && script[0]) {
while (script != nullptr && script[0]) {
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
int line_number = 1;
const char* tail;
@ -678,23 +678,23 @@ sql_compile_script(sqlite3* db,
log_debug("retcode %d %p %p", retcode, script, tail);
if (retcode != SQLITE_OK) {
const char* errmsg = sqlite3_errmsg(db);
auto_mem<char> full_msg;
if (asprintf(full_msg.out(),
"error:%s:%d:%s",
src_name,
line_number,
errmsg)
== -1)
{
log_error("unable to allocate error message");
break;
attr_line_t sql_content;
if (tail != nullptr) {
sql_content.append(script, (size_t) (tail - script));
} else {
sql_content.append(script);
}
errors.emplace_back(full_msg.in());
errors.emplace_back(lnav::console::user_message::error(
"failed to compile SQL statement")
.with_reason(errmsg)
.with_snippet(lnav::console::snippet::from(
src_name, sql_content)
.with_line(line_number)));
break;
} else if (script == tail) {
break;
} else if (stmt == NULL) {
} else if (stmt == nullptr) {
} else {
stmts.push_back(stmt.release());
}
@ -703,10 +703,11 @@ sql_compile_script(sqlite3* db,
}
}
void
static void
sql_execute_script(sqlite3* db,
const char* src_name,
const std::vector<sqlite3_stmt*>& stmts,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
std::map<std::string, std::string> lvars;
@ -766,7 +767,13 @@ sql_execute_script(sqlite3* db,
const char* errmsg;
errmsg = sqlite3_errmsg(db);
errors.emplace_back(errmsg);
errors.emplace_back(
lnav::console::user_message::error(
"failed to execute SQL statement")
.with_reason(errmsg)
.with_snippet(lnav::console::snippet::from(
src_name, sqlite3_sql(stmt))));
done = true;
break;
}
}
@ -780,13 +787,14 @@ void
sql_execute_script(sqlite3* db,
const char* src_name,
const char* script,
std::vector<std::string>& errors)
std::vector<lnav::console::user_message>& errors)
{
std::vector<sqlite3_stmt*> stmts;
auto init_error_count = errors.size();
sql_compile_script(db, src_name, script, stmts, errors);
if (errors.empty()) {
sql_execute_script(db, stmts, errors);
if (errors.size() == init_error_count) {
sql_execute_script(db, src_name, stmts, errors);
}
for (sqlite3_stmt* stmt : stmts) {

@ -41,6 +41,7 @@
#include <time.h>
#include "base/intern_string.hh"
#include "base/lnav.console.hh"
#include "base/time_util.hh"
#include "sqlitepp.hh"
@ -94,16 +95,12 @@ void sql_compile_script(sqlite3* db,
const char* src_name,
const char* script,
std::vector<sqlite3_stmt*>& stmts,
std::vector<std::string>& errors);
void sql_execute_script(sqlite3* db,
const std::vector<sqlite3_stmt*>& stmts,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
void sql_execute_script(sqlite3* db,
const char* src_name,
const char* script,
std::vector<std::string>& errors);
std::vector<lnav::console::user_message>& errors);
int guess_type_from_pcre(const std::string& pattern, std::string& collator);

@ -31,7 +31,7 @@
#include "sqlite-extension-func.hh"
#include "auto_mem.hh"
#include "base/auto_mem.hh"
#include "base/lnav_log.hh"
#include "base/string_util.hh"
#include "config.h"
@ -942,7 +942,11 @@ register_sqlite_funcs(sqlite3* db, sqlite_registration_func_t* reg_funcs)
help_text("SELECT",
"Query the database and return zero or more rows of data.")
.sql_keyword()
.with_parameter(help_text("result-column", "").one_or_more())
.with_parameter(
help_text(
"result-column",
"The expression used to generate a result for this column.")
.one_or_more())
.with_parameter(help_text("table", "The table(s) to query for data")
.with_flag_name("FROM")
.zero_or_more())
@ -962,7 +966,7 @@ register_sqlite_funcs(sqlite3* db, sqlite_registration_func_t* reg_funcs)
.with_flag_name("ORDER BY")
.zero_or_more())
.with_parameter(
help_text("limit-expr", "The maximum number of rows to return")
help_text("limit-expr", "The maximum number of rows to return.")
.with_flag_name("LIMIT")
.zero_or_more())
.with_example(

@ -34,6 +34,7 @@
#include "statusview_curses.hh"
#include "base/ansi_scrubber.hh"
#include "config.h"
void
@ -56,15 +57,15 @@ status_field::do_cylon()
{
string_attrs_t& sa = this->sf_value.get_attrs();
remove_string_attr(sa, &view_curses::VC_STYLE);
remove_string_attr(sa, &VC_STYLE);
struct line_range lr(this->sf_cylon_pos, this->sf_width);
view_colors& vc = view_colors::singleton();
sa.emplace_back(
lr,
view_curses::VC_STYLE.value(
vc.attrs_for_role(view_colors::VCR_ACTIVE_STATUS) | A_REVERSE));
VC_STYLE.value(
vc.attrs_for_role(role_t::VCR_ACTIVE_STATUS) | A_REVERSE));
this->sf_cylon_pos += 1;
if (this->sf_cylon_pos > this->sf_width) {
@ -73,18 +74,18 @@ status_field::do_cylon()
}
void
status_field::set_stitch_value(view_colors::role_t left,
view_colors::role_t right)
status_field::set_stitch_value(role_t left,
role_t right)
{
string_attrs_t& sa = this->sf_value.get_attrs();
struct line_range lr(0, 1);
this->sf_value.get_string() = "::";
sa.clear();
sa.emplace_back(lr, view_curses::VC_ROLE.value(left));
sa.emplace_back(lr, VC_ROLE.value(left));
lr.lr_start = 1;
lr.lr_end = 2;
sa.emplace_back(lr, view_curses::VC_ROLE.value(right));
sa.emplace_back(lr, VC_ROLE.value(right));
}
void
@ -104,8 +105,8 @@ statusview_curses::do_update()
top = this->sc_top < 0 ? height + this->sc_top : this->sc_top;
right = width;
attrs = vc.attrs_for_role(this->sc_enabled
? view_colors::VCR_STATUS
: view_colors::VCR_INACTIVE_STATUS);
? role_t::VCR_STATUS
: role_t::VCR_INACTIVE_STATUS);
wattron(this->sc_window, attrs);
wmove(this->sc_window, top, 0);
@ -125,16 +126,16 @@ statusview_curses::do_update()
val = sf.get_value();
if (!this->sc_enabled) {
for (auto& sa : val.get_attrs()) {
if (sa.sa_type == &view_curses::VC_STYLE) {
if (sa.sa_type == &VC_STYLE) {
sa.sa_value = sa.sa_value.get<int64_t>()
& ~(A_REVERSE | A_COLOR);
} else if (sa.sa_type == &view_curses::VC_ROLE) {
if (sa.sa_value.get<int64_t>()
== view_colors::VCR_ALERT_STATUS) {
sa.sa_value.get<int64_t>()
= view_colors::VCR_INACTIVE_ALERT_STATUS;
} else if (sa.sa_type == &VC_ROLE) {
if (sa.sa_value.get<role_t>()
== role_t::VCR_ALERT_STATUS) {
sa.sa_value.get<role_t>()
= role_t::VCR_INACTIVE_ALERT_STATUS;
} else {
sa.sa_value = view_colors::VCR_NONE;
sa.sa_value = role_t::VCR_NONE;
}
}
}
@ -171,10 +172,10 @@ statusview_curses::do_update()
auto default_role = sf.get_role();
if (!this->sc_enabled) {
if (default_role == view_colors::VCR_ALERT_STATUS) {
default_role = view_colors::VCR_INACTIVE_ALERT_STATUS;
if (default_role == role_t::VCR_ALERT_STATUS) {
default_role = role_t::VCR_INACTIVE_ALERT_STATUS;
} else {
default_role = view_colors::VCR_INACTIVE_STATUS;
default_role = role_t::VCR_INACTIVE_STATUS;
}
}

@ -35,7 +35,6 @@
#include <string>
#include <vector>
#include "ansi_scrubber.hh"
#include "view_curses.hh"
/**
@ -48,7 +47,7 @@ public:
* @param role The color role for this field, defaults to VCR_STATUS.
*/
status_field(int width = 1,
view_colors::role_t role = view_colors::VCR_STATUS)
role_t role = role_t::VCR_STATUS)
: sf_width(width), sf_role(role){};
virtual ~status_field() = default;
@ -75,7 +74,7 @@ public:
return *this;
};
void set_stitch_value(view_colors::role_t left, view_colors::role_t right);
void set_stitch_value(role_t left, role_t right);
void set_left_pad(size_t val)
{
@ -125,12 +124,12 @@ public:
};
/** @param role The color role for this field. */
void set_role(view_colors::role_t role)
void set_role(role_t role)
{
this->sf_role = role;
};
/** @return The color role for this field. */
view_colors::role_t get_role() const
role_t get_role() const
{
return this->sf_role;
};
@ -173,7 +172,7 @@ protected:
bool sf_cylon{false};
ssize_t sf_cylon_pos{0};
attr_line_t sf_value; /*< The value to display for this field. */
view_colors::role_t sf_role; /*< The color role for this field. */
role_t sf_role; /*< The color role for this field. */
int sf_share{0};
size_t sf_left_pad{0};
};

@ -443,6 +443,12 @@ sql_gzip(sqlite3_value* val)
return nonstd::nullopt;
}
std::string
sql_humanize_file_size(file_ssize_t value)
{
return humanize::file_size(value, humanize::alignment::columnar);
}
int
string_extension_functions(struct FuncDef** basic_funcs,
struct FuncDefAgg** agg_funcs)
@ -511,8 +517,8 @@ string_extension_functions(struct FuncDef** basic_funcs,
"SELECT regexp_replace('123 abc', '(\\w+)', '<\\1>')",
})),
sqlite_func_adapter<decltype(&humanize::file_size),
humanize::file_size>::
sqlite_func_adapter<decltype(&sql_humanize_file_size),
sql_humanize_file_size>::
builder(help_text(
"humanize_file_size",
"Format the given file size as a human-friendly string")

@ -1,94 +0,0 @@
/**
* Copyright (c) 2020, 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_string_attr_type_hh
#define lnav_string_attr_type_hh
#include <string>
#include <utility>
#include <stdint.h>
#include "base/intern_string.hh"
#include "mapbox/variant.hpp"
class logfile;
struct bookmark_metadata;
using string_attr_value = mapbox::util::variant<int64_t,
const intern_string_t,
std::string,
std::shared_ptr<logfile>,
bookmark_metadata*>;
class string_attr_type_base {
public:
explicit string_attr_type_base(const char* name) noexcept : sat_name(name)
{
}
const char* const sat_name;
};
template<typename T>
class string_attr_type : public string_attr_type_base {
public:
using value_type = T;
explicit string_attr_type(const char* name) noexcept
: string_attr_type_base(name)
{
}
template<typename U = T>
std::enable_if_t<!std::is_void<U>::value,
std::pair<const string_attr_type_base*, string_attr_value>>
value(const U& val) const
{
return std::make_pair(this, val);
}
template<typename U = T>
std::enable_if_t<std::is_void<U>::value,
std::pair<const string_attr_type_base*, string_attr_value>>
value() const
{
return std::make_pair(this, string_attr_value{});
}
};
extern string_attr_type<void> SA_ORIGINAL_LINE;
extern string_attr_type<void> SA_BODY;
extern string_attr_type<void> SA_HIDDEN;
extern string_attr_type<const intern_string_t> SA_FORMAT;
extern string_attr_type<void> SA_REMOVED;
extern string_attr_type<std::string> SA_INVALID;
extern string_attr_type<std::string> SA_ERROR;
#endif

@ -39,15 +39,15 @@
#include "yajlpp/yajlpp_def.hh"
static const struct json_path_container term_color_rgb_handler = {
yajlpp::property_handler("r").FOR_FIELD(rgb_color, rc_r),
yajlpp::property_handler("g").FOR_FIELD(rgb_color, rc_g),
yajlpp::property_handler("b").FOR_FIELD(rgb_color, rc_b),
yajlpp::property_handler("r").for_field(&rgb_color::rc_r),
yajlpp::property_handler("g").for_field(&rgb_color::rc_g),
yajlpp::property_handler("b").for_field(&rgb_color::rc_b),
};
static const struct json_path_container term_color_handler = {
yajlpp::property_handler("colorId").FOR_FIELD(term_color, xc_id),
yajlpp::property_handler("name").FOR_FIELD(term_color, xc_name),
yajlpp::property_handler("hexString").FOR_FIELD(term_color, xc_hex),
yajlpp::property_handler("colorId").for_field(&term_color::xc_id),
yajlpp::property_handler("name").for_field(&term_color::xc_name),
yajlpp::property_handler("hexString").for_field(&term_color::xc_hex),
yajlpp::property_handler("rgb")
.with_obj_provider<rgb_color, term_color>(
[](const auto& pc, term_color* xc) { return &xc->xc_color; })

@ -206,6 +206,7 @@ struct lnav_theme {
style_config lt_style_inactive_status;
style_config lt_style_inactive_alert_status;
style_config lt_style_file;
style_config lt_style_header[6];
std::map<log_level_t, style_config> lt_level_styles;
std::map<std::string, highlighter_config> lt_highlights;
};

@ -29,6 +29,8 @@
#include "tailerpp.hh"
#include <unistd.h>
namespace tailer {
int

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save