[:set-file-timezone] flesh out this command some more

pull/1205/head
Tim Stack 9 months ago
parent d0c4530a8b
commit 893abf77ec

@ -705,11 +705,11 @@ operator|(const std::map<K, V>& in,
template<typename T, typename F>
auto
operator|(const T& in, const lnav::itertools::details::mapper<F>& mapper)
-> std::vector<
std::remove_const_t<decltype(((*in.begin()).*mapper.m_func)())>>
-> std::vector<std::remove_const_t<
std::remove_reference_t<decltype(((*in.begin()).*mapper.m_func)())>>>
{
using return_type = std::vector<
std::remove_const_t<decltype(((*in.begin()).*mapper.m_func)())>>;
using return_type = std::vector<std::remove_const_t<
std::remove_reference_t<decltype(((*in.begin()).*mapper.m_func)())>>>;
return_type retval;
retval.reserve(in.size());

@ -211,6 +211,11 @@ field_overlay_source::build_field_lines(const listview_curses& lv,
time_line.append(" Format: ")
.append(lnav::roles::symbol(
ts_formats[format->lf_date_time.dts_fmt_lock]));
if (format->lf_date_time.dts_default_zone != nullptr) {
time_line.append(" Default Zone: ")
.append(lnav::roles::symbol(
format->lf_date_time.dts_default_zone->name()));
}
}
if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show)

@ -40,15 +40,26 @@ namespace lnav {
static const typed_json_path_container<file_options> options_handlers = {
yajlpp::property_handler("default-zone")
.with_synopsis("<zone>")
.with_description("The default zone")
.with_example("America/Los_Angeles"),
.with_description("The default zone for log messages if the timestamp "
"does not include a zone.")
.with_example("America/Los_Angeles")
.for_field(&file_options::fo_default_zone),
};
static const typed_json_path_container<file_options_collection>
pattern_to_options_handlers = {
yajlpp::pattern_property_handler("(?<path>[^/]+)")
.with_description("Path or glob pattern")
.with_children(options_handlers)
.for_field(&file_options_collection::foc_pattern_to_options),
};
static const typed_json_path_container<file_options_collection>
collection_handlers = {
yajlpp::pattern_property_handler("(.*)")
.with_description("Path pattern")
.with_children(options_handlers),
yajlpp::property_handler("paths")
.with_description("Mapping of file paths or glob patterns to the "
"associated options")
.with_children(pattern_to_options_handlers),
};
bool
@ -57,19 +68,40 @@ file_options::operator==(const lnav::file_options& rhs) const
return this->fo_default_zone == rhs.fo_default_zone;
}
nonstd::optional<file_options>
json_string
file_options::to_json_string() const
{
return options_handlers.to_json_string(*this);
}
Result<file_options_collection, std::vector<lnav::console::user_message>>
file_options_collection::from_json(intern_string_t src,
const string_fragment& frag)
{
return collection_handlers.parser_for(src).of(frag);
}
std::string
file_options_collection::to_json() const
{
return collection_handlers.formatter_for(*this)
.with_config(yajl_gen_beautify, true)
.to_string();
}
nonstd::optional<std::pair<std::string, file_options>>
file_options_collection::match(const std::string& path) const
{
auto iter = this->foc_pattern_to_options.find(path);
if (iter != this->foc_pattern_to_options.end()) {
return iter->second;
return *iter;
}
for (const auto& pair : this->foc_pattern_to_options) {
auto rc = fnmatch(pair.first.c_str(), path.c_str(), FNM_PATHNAME);
if (rc == 0) {
return pair.second;
return pair;
}
if (rc != FNM_NOMATCH) {
log_error("fnmatch('%s', '%s') failed -- %s",
@ -82,7 +114,7 @@ file_options_collection::match(const std::string& path) const
return nonstd::nullopt;
}
nonstd::optional<file_options>
nonstd::optional<std::pair<std::string, file_options>>
file_options_hier::match(const ghc::filesystem::path& path) const
{
auto lookup_path = path.parent_path();

@ -38,20 +38,31 @@
#include "ghc/filesystem.hpp"
#include "mapbox/variant.hpp"
#include "safe/safe.h"
#include "yajlpp/yajlpp.hh"
namespace lnav {
struct file_options {
intern_string_t fo_default_zone_name;
const date::time_zone* fo_default_zone{nullptr};
bool empty() const { return this->fo_default_zone == nullptr; }
json_string to_json_string() const;
bool operator==(const file_options& rhs) const;
};
struct file_options_collection {
static Result<file_options_collection,
std::vector<lnav::console::user_message>>
from_json(intern_string_t src, const string_fragment& frag);
std::map<std::string, file_options> foc_pattern_to_options;
nonstd::optional<file_options> match(const std::string& path) const;
nonstd::optional<std::pair<std::string, file_options>> match(
const std::string& path) const;
std::string to_json() const;
};
struct file_options_hier {
@ -59,7 +70,7 @@ struct file_options_hier {
foh_path_to_collection;
size_t foh_generation{0};
nonstd::optional<file_options> match(
nonstd::optional<std::pair<std::string, file_options>> match(
const ghc::filesystem::path& path) const;
};

@ -40,6 +40,7 @@
#include "logfile.hh"
#include "session_data.hh"
#include "vtab_module.hh"
#include "vtab_module_json.hh"
struct lnav_file : public tvt_iterator_cursor<lnav_file> {
using iterator = std::vector<std::shared_ptr<logfile>>::iterator;
@ -56,6 +57,8 @@ CREATE TABLE lnav_file (
format text, -- The log file format for the file.
lines integer, -- The number of lines in the file.
time_offset integer, -- The millisecond offset for timestamps.
options_path TEXT, -- The matched path for the file options.
options TEXT, -- The effective options for the file.
content BLOB HIDDEN -- The contents of the file.
);
@ -112,6 +115,32 @@ CREATE TABLE lnav_file (
return SQLITE_OK;
}
auto opts = lf->get_file_options();
if (opts) {
to_sqlite(ctx, opts.value().first);
} else {
sqlite3_result_null(ctx);
}
break;
}
case 9: {
if (sqlite3_vtab_nochange(ctx)) {
return SQLITE_OK;
}
auto opts = lf->get_file_options();
if (opts) {
to_sqlite(ctx, opts.value().second.to_json_string());
} else {
sqlite3_result_null(ctx);
}
break;
}
case 10: {
if (sqlite3_vtab_nochange(ctx)) {
return SQLITE_OK;
}
auto& cfg = injector::get<const file_vtab::config&>();
auto lf_stat = lf->get_stat();
@ -186,6 +215,8 @@ CREATE TABLE lnav_file (
const char* format,
int64_t lines,
int64_t time_offset,
const char* options_path,
const char* options,
const char* content)
{
auto lf = this->lf_collection.fc_files[rowid];

@ -114,6 +114,20 @@
----
.. _clear_file_timezone:
:clear-file-timezone *pattern*
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Clear the timezone setting for the focused file or the given glob pattern.
**Parameters**
* **pattern\*** --- The glob pattern to match against files that should no longer use this timezone
----
.. _clear_filter_expr:
:clear-filter-expr

@ -2205,6 +2205,26 @@ main(int argc, char* argv[])
{
auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
auto opt_path = lnav::paths::dotlnav() / "file-options.json";
auto read_res = lnav::filesystem::read_file(opt_path);
auto curr_tz = date::get_tzdb().current_zone();
auto options_coll = lnav::file_options_collection{};
if (read_res.isOk()) {
intern_string_t opt_path_src = intern_string::lookup(opt_path);
auto parse_res = lnav::file_options_collection::from_json(
opt_path_src, read_res.unwrap());
if (parse_res.isErr()) {
for (const auto& um : parse_res.unwrapErr()) {
lnav::console::print(stderr, um);
}
return EXIT_FAILURE;
}
options_coll = parse_res.unwrap();
}
safe::WriteAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
@ -2212,12 +2232,9 @@ main(int argc, char* argv[])
auto_mem<char> var_path;
var_path = realpath("/var/log", nullptr);
auto curr_tz = date::get_tzdb().current_zone();
auto options_coll = lnav::file_options_collection{};
options_coll.foc_pattern_to_options[fmt::format(FMT_STRING("{}/*"),
var_path.in())]
= lnav::file_options{
intern_string::lookup(curr_tz->name()),
curr_tz,
};
options_hier->foh_path_to_collection.emplace(ghc::filesystem::path("/"),

@ -59,6 +59,7 @@
#include "field_overlay_source.hh"
#include "fmt/printf.h"
#include "hasher.hh"
#include "itertools.similar.hh"
#include "lnav.indexing.hh"
#include "lnav_commands.hh"
#include "lnav_config.hh"
@ -100,6 +101,12 @@ const char* curl_url_strerror(CURLUcode error);
using namespace lnav::roles::literals;
inline attr_line_t&
symbol_reducer(const std::string& elem, attr_line_t& accum)
{
return accum.append("\n ").append(lnav::roles::symbol(elem));
}
static std::string
remaining_args(const std::string& cmdline,
const std::vector<std::string>& args,
@ -331,6 +338,7 @@ com_set_file_timezone(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
static const intern_string_t SRC = intern_string::lookup("args");
std::string retval;
if (args.empty()) {
@ -355,8 +363,29 @@ com_set_file_timezone(exec_context& ec,
return ec.make_error(FMT_STRING("cannot find line: {}"),
(int) tc->get_selection());
}
shlex lexer(cmdline);
auto split_res = lexer.split(ec.create_resolver());
if (split_res.isErr()) {
auto split_err = split_res.unwrapErr();
auto um = lnav::console::user_message::error(
"unable to parse arguments")
.with_reason(split_err.te_msg)
.with_snippet(lnav::console::snippet::from(
SRC, lexer.to_attr_line(split_err)));
return Err(um);
}
auto split_args
= split_res.unwrap() | lnav::itertools::map([](const auto& elem) {
return elem.se_value;
});
try {
auto* tz = date::locate_zone(args[1]);
auto* tz = date::locate_zone(split_args[1]);
auto pattern = split_args.size() == 2
? line_pair->first->get_filename()
: split_args[2];
if (!ec.ec_dry_run) {
static auto& safe_options_hier
@ -369,18 +398,29 @@ com_set_file_timezone(exec_context& ec,
auto& coll = options_hier->foh_path_to_collection["/"];
log_info("setting timezone for %s to %s",
line_pair->first->get_filename().c_str(),
pattern.c_str(),
args[1].c_str());
coll.foc_pattern_to_options[line_pair->first->get_filename()]
= lnav::file_options{
intern_string::lookup(args[1]),
tz,
};
coll.foc_pattern_to_options[pattern] = lnav::file_options{
tz,
};
auto opt_path = lnav::paths::dotlnav() / "file-options.json";
auto coll_str = coll.to_json();
lnav::filesystem::write_file(opt_path, coll_str);
}
} catch (const std::runtime_error& e) {
return ec.make_error(FMT_STRING("Unable to get timezone: {} -- {}"),
args[1],
e.what());
auto note = (date::get_tzdb().zones
| lnav::itertools::map(&date::time_zone::name)
| lnav::itertools::similar_to(split_args[1])
| lnav::itertools::fold(symbol_reducer, attr_line_t{}))
.add_header("did you mean one of the following?");
auto um = lnav::console::user_message::error(
attr_line_t()
.append_quoted(split_args[1])
.append(" is not a valid timezone"))
.with_reason(e.what())
.with_note(note);
return Err(um);
}
} else {
return ec.make_error(
@ -390,6 +430,95 @@ com_set_file_timezone(exec_context& ec,
return Ok(retval);
}
static std::string
com_set_file_timezone_prompt(exec_context& ec, const std::string& cmdline)
{
std::string retval;
auto* tc = *lnav_data.ld_view_stack.top();
auto* lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
if (lss != nullptr && lss->text_line_count() > 0) {
auto line_pair = lss->find_line_with_file(lss->at(tc->get_selection()));
if (line_pair) {
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
safe::ReadAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
auto file_zone = date::get_tzdb().current_zone()->name();
auto pattern_arg = line_pair->first->get_filename();
auto match_res
= options_hier->match(line_pair->first->get_filename());
if (match_res) {
file_zone = match_res->second.fo_default_zone->name();
pattern_arg = match_res->first;
}
retval = fmt::format(FMT_STRING("{} {} {}"),
trim(cmdline),
date::get_tzdb().current_zone()->name(),
line_pair->first->get_filename());
}
}
return retval;
}
static Result<std::string, lnav::console::user_message>
com_clear_file_timezone(exec_context& ec,
std::string cmdline,
std::vector<std::string>& args)
{
std::string retval;
if (args.empty()) {
args.emplace_back("file-with-zone");
return Ok(retval);
}
if (args.size() != 2) {
return ec.make_error("expecting a single file path or pattern");
}
auto* tc = *lnav_data.ld_view_stack.top();
auto* lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
if (lss != nullptr) {
if (!ec.ec_dry_run) {
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
safe::WriteAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
options_hier->foh_generation += 1;
auto& coll = options_hier->foh_path_to_collection["/"];
const auto iter = coll.foc_pattern_to_options.find(args[1]);
if (iter == coll.foc_pattern_to_options.end()) {
return ec.make_error(FMT_STRING("no timezone set for: {}"),
args[1]);
}
log_info("clearing timezone for %s", args[1].c_str());
iter->second.fo_default_zone = nullptr;
if (iter->second.empty()) {
coll.foc_pattern_to_options.erase(iter);
}
auto opt_path = lnav::paths::dotlnav() / "file-options.json";
auto coll_str = coll.to_json();
lnav::filesystem::write_file(opt_path, coll_str);
}
} else {
return ec.make_error(
":clear-file-timezone is only supported for the LOG view");
}
return Ok(retval);
}
static Result<std::string, lnav::console::user_message>
com_convert_time_to(exec_context& ec,
std::string cmdline,
@ -5517,6 +5646,18 @@ readline_context::command_t STD_COMMANDS[] = {
.with_parameter(help_text{"pattern",
"The glob pattern to match against "
"files that should use this timezone"}),
com_set_file_timezone_prompt,
},
{
"clear-file-timezone",
com_clear_file_timezone,
help_text(":clear-file-timezone")
.with_summary("Clear the timezone setting for the focused file or "
"the given glob pattern.")
.with_parameter(
help_text{"pattern",
"The glob pattern to match against files that should "
"no longer use this timezone"}),
},
{"current-time",
com_current_time,

@ -1065,7 +1065,8 @@ external_log_format::scan(logfile& lf,
auto file_options = lf.get_file_options();
if (file_options) {
this->lf_date_time.dts_default_zone = file_options->fo_default_zone;
this->lf_date_time.dts_default_zone
= file_options->second.fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}

@ -112,7 +112,7 @@ class generic_log_format : public log_format {
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
= file_options->second.fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
@ -551,7 +551,7 @@ public:
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
= file_options->second.fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
@ -1227,7 +1227,7 @@ public:
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
= file_options->second.fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}
@ -1751,7 +1751,7 @@ public:
if (file_options) {
this->lf_date_time.dts_default_zone
= file_options->fo_default_zone;
= file_options->second.fo_default_zone;
} else {
this->lf_date_time.dts_default_zone = nullptr;
}

@ -181,6 +181,8 @@ logfile::file_options_have_changed()
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
bool tz_changed = false;
{
safe::ReadAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
@ -197,13 +199,25 @@ logfile::file_options_have_changed()
this->lf_file_options = new_options;
log_info("%s: file options have changed", this->lf_filename.c_str());
if (this->lf_file_options) {
log_info(" tz=%s",
this->lf_file_options->fo_default_zone->name().c_str());
log_info(
" tz=%s",
this->lf_file_options->second.fo_default_zone->name().c_str());
if (this->lf_file_options->second.fo_default_zone != nullptr
&& this->lf_format != nullptr
&& !(this->lf_format->lf_timestamp_flags & ETF_ZONE_SET))
{
tz_changed = true;
}
} else if (this->lf_format != nullptr
&& !(this->lf_format->lf_timestamp_flags & ETF_ZONE_SET)
&& this->lf_format->lf_date_time.dts_default_zone != nullptr)
{
tz_changed = true;
}
this->lf_file_options_generation = options_hier->foh_generation;
}
return true;
return tz_changed;
}
bool

@ -401,7 +401,8 @@ public:
return this->lf_embedded_metadata;
}
nonstd::optional<lnav::file_options> get_file_options() const
nonstd::optional<std::pair<std::string, lnav::file_options>>
get_file_options() const
{
return this->lf_file_options;
}
@ -467,7 +468,8 @@ private:
std::vector<std::shared_ptr<format_tag_def>> lf_applicable_taggers;
std::map<std::string, metadata> lf_embedded_metadata;
size_t lf_file_options_generation{0};
nonstd::optional<lnav::file_options> lf_file_options;
nonstd::optional<std::pair<std::string, lnav::file_options>>
lf_file_options;
};
class logline_observer {

@ -515,10 +515,24 @@ add_tz_possibilities(ln_mode_t context)
{
auto* rc = lnav_data.ld_rl_view;
log_info("BEGIN tz poss");
rc->clear_possibilities(context, "timezone");
for (const auto& tz : date::get_tzdb().zones) {
rc->add_possibility(context, "timezone", tz.name());
}
log_info("END tz poss");
{
static auto& safe_options_hier
= injector::get<lnav::safe_file_options_hier&>();
safe::ReadAccess<lnav::safe_file_options_hier> options_hier(
safe_options_hier);
rc->clear_possibilities(context, "file-with-zone");
for (const auto& hier_pair : options_hier->foh_path_to_collection) {
for (const auto& coll_pair :
hier_pair.second.foc_pattern_to_options)
{
rc->add_possibility(context, "file-with-zone", coll_pair.first);
}
}
}
}

@ -54,6 +54,7 @@ LDADD = \
libyajlpp.a \
$(top_builddir)/src/base/libbase.a \
$(top_builddir)/src/fmtlib/libcppfmt.a \
$(top_builddir)/src/third-party/date/src/libdatepp.a \
$(top_builddir)/src/third-party/scnlib/src/libscnlib.a \
$(top_builddir)/src/pcrepp/libpcrepp.a \
$(top_builddir)/src/yajl/libyajl.a

@ -233,7 +233,7 @@ json_ptr::encode_str(const char* src, size_t src_len)
src_len = strlen(src);
}
char retval[src_len + 1];
char retval[src_len * 2 + 1];
auto rc = encode(retval, sizeof(retval), src, src_len);
return std::string(retval, rc);

@ -212,9 +212,6 @@ json_path_handler_base::gen(yajlpp_gen_context& ygc, yajl_gen handle) const
if (this->jph_children) {
for (const auto& lpath : local_paths) {
std::string full_path = json_ptr::encode_str(lpath);
if (this->jph_path_provider) {
full_path += "/";
}
int start_depth = ygc.ygc_depth;
yajl_gen_string(handle, lpath);
@ -1380,6 +1377,29 @@ json_path_handler_base::report_pattern_error(yajlpp_parse_context* ypc,
.with_help(this->get_help_text(ypc)));
}
void
json_path_handler_base::report_tz_error(yajlpp_parse_context* ypc,
const std::string& value_str,
const char* msg) const
{
auto help_al = attr_line_t()
.append(lnav::roles::h2("Available time zones"))
.append("\n");
for (const auto& tz : date::get_tzdb().zones) {
help_al.append(" ")
.append(lnav::roles::symbol(tz.name()))
.append("\n");
}
ypc->report_error(lnav::console::user_message::error(
attr_line_t().append_quoted(value_str).append(
" is not a valid timezone"))
.with_snippet(ypc->get_snippet())
.with_reason(msg)
.with_help(help_al));
}
attr_line_t
json_path_handler_base::get_help_text(const std::string& full_path) const
{

@ -316,6 +316,9 @@ struct json_path_handler_base {
void report_error(yajlpp_parse_context* ypc,
const std::string& value_str,
lnav::console::user_message um) const;
void report_tz_error(yajlpp_parse_context* ypc,
const std::string& value_str,
const char* msg) const;
attr_line_t get_help_text(const std::string& full_path) const;
attr_line_t get_help_text(yajlpp_parse_context* ypc) const;

@ -416,18 +416,28 @@ struct json_path_handler : public json_path_handler_base {
template<typename T, typename... Args>
struct LastIsMap {
using key_type = typename LastIsMap<Args...>::key_type;
using value_type = typename LastIsMap<Args...>::value_type;
static constexpr bool value = LastIsMap<Args...>::value;
};
template<typename T, typename K, typename U>
struct LastIsMap<std::shared_ptr<std::map<K, U>> T::*> {
using key_type = K;
using value_type = U;
static constexpr bool value = true;
};
template<typename T, typename K, typename U>
struct LastIsMap<std::map<K, U> T::*> {
using key_type = K;
using value_type = U;
static constexpr bool value = true;
};
template<typename T, typename U>
struct LastIsMap<U T::*> {
using key_type = void;
using value_type = void;
static constexpr bool value = false;
};
@ -658,7 +668,12 @@ struct json_path_handler : public json_path_handler_base {
}
template<typename... Args,
std::enable_if_t<LastIsMap<Args...>::value, bool> = true>
std::enable_if_t<LastIsMap<Args...>::value, bool> = true,
std::enable_if_t<
std::is_same<intern_string_t,
typename LastIsMap<Args...>::key_type>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->jph_path_provider =
@ -678,6 +693,44 @@ struct json_path_handler : public json_path_handler_base {
return *this;
}
template<
typename... Args,
std::enable_if_t<LastIsMap<Args...>::value, bool> = true,
std::enable_if_t<
std::is_same<std::string,
typename LastIsMap<Args...>::key_type>::value,
bool>
= true,
std::enable_if_t<
!std::is_same<json_any_t,
typename LastIsMap<Args...>::value_type>::value
&& !std::is_same<std::string,
typename LastIsMap<Args...>::value_type>::value
&& !std::is_same<
nonstd::optional<std::string>,
typename LastIsMap<Args...>::value_type>::value,
bool>
= true>
json_path_handler& for_field(Args... args)
{
this->jph_path_provider =
[args...](void* root, std::vector<std::string>& paths_out) {
const auto& field = json_path_handler::get_field(root, args...);
for (const auto& pair : field) {
paths_out.emplace_back(pair.first);
}
};
this->jph_obj_provider
= [args...](const yajlpp_provider_context& ypc, void* root) {
auto& field = json_path_handler::get_field(root, args...);
auto key = ypc.get_substr(0);
return &(field[key]);
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<std::map<std::string, nonstd::optional<std::string>>,
@ -1070,6 +1123,54 @@ struct json_path_handler : public json_path_handler_base {
return *this;
}
template<
typename... Args,
std::enable_if_t<LastIs<const date::time_zone*, Args...>::value, bool>
= true>
json_path_handler& for_field(Args... args)
{
this->add_cb(str_field_cb2);
this->jph_str_cb = [args...](yajlpp_parse_context* ypc,
const string_fragment& value_str) {
auto* obj = ypc->ypc_obj_stack.top();
const auto* jph = ypc->ypc_current_handler;
try {
const auto* tz
= date::get_tzdb().locate_zone(value_str.to_string());
json_path_handler::get_field(obj, args...) = tz;
} catch (const std::runtime_error& e) {
jph->report_tz_error(ypc, value_str.to_string(), e.what());
}
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context& ygc,
const json_path_handler_base& jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(
ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(
ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field->name());
};
return *this;
}
template<typename... Args,
std::enable_if_t<
LastIs<positioned_property<intern_string_t>, Args...>::value,
@ -1507,6 +1608,34 @@ private:
T yp_obj;
};
template<typename T>
struct typed_json_path_container;
template<typename T>
struct yajlpp_formatter {
const T& yf_obj;
const typed_json_path_container<T>& yf_container;
yajlpp_gen yf_gen;
template<typename... Args>
yajlpp_formatter<T> with_config(Args... args) &&
{
yajl_gen_config(this->yf_gen.get_handle(), args...);
return std::move(*this);
}
std::string to_string() &&
{
yajlpp_gen_context ygc(this->yf_gen, this->yf_container);
ygc.template with_obj(this->yf_obj);
ygc.ygc_depth = 1;
ygc.gen();
return this->yf_gen.to_string_fragment().to_string();
}
};
template<typename T>
struct typed_json_path_container : public json_path_container {
typed_json_path_container(std::initializer_list<json_path_handler> children)
@ -1531,6 +1660,11 @@ struct typed_json_path_container : public json_path_container {
return yajlpp_parser<T>{src, this};
}
yajlpp_formatter<T> formatter_for(const T& obj) const
{
return yajlpp_formatter<T>{obj, *this};
}
std::string to_string(const T& obj) const
{
yajlpp_gen gen;

@ -346,6 +346,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_json_format.sh_f740026626ab554dacb249762d8be7d6539b8c6e.out \
$(srcdir)/%reldir%/test_json_format.sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.err \
$(srcdir)/%reldir%/test_json_format.sh_fe19b7ebd349cd689b3f5c22618eab5ce995e68e.out \
$(srcdir)/%reldir%/test_logfile.sh_00877d2e9dadab916a02005a068410dfbd85ec74.err \
$(srcdir)/%reldir%/test_logfile.sh_00877d2e9dadab916a02005a068410dfbd85ec74.out \
$(srcdir)/%reldir%/test_logfile.sh_05d1505168bf34b89fc0d1a39f1409cfe798119e.err \
$(srcdir)/%reldir%/test_logfile.sh_05d1505168bf34b89fc0d1a39f1409cfe798119e.out \
$(srcdir)/%reldir%/test_logfile.sh_08d731a04c877a34819b35de185e30a74c9fd497.err \
@ -358,6 +360,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_logfile.sh_218ecb88b4753010c4264b3ac351260b4811612f.out \
$(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.err \
$(srcdir)/%reldir%/test_logfile.sh_290a3c49e53c2229a7400c107338fa0bb38375e2.out \
$(srcdir)/%reldir%/test_logfile.sh_2e72d848a51ac1eb476b973c61fe43a65d579ba5.err \
$(srcdir)/%reldir%/test_logfile.sh_2e72d848a51ac1eb476b973c61fe43a65d579ba5.out \
$(srcdir)/%reldir%/test_logfile.sh_341e491abcf8772422bafb8b0eaea6492da230f6.err \
$(srcdir)/%reldir%/test_logfile.sh_341e491abcf8772422bafb8b0eaea6492da230f6.out \
$(srcdir)/%reldir%/test_logfile.sh_3fc6bfd8a6160817211f3e14fde957af75b9dbe7.err \
@ -376,6 +380,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_logfile.sh_a7037efd0c4bbf51940137a44e57d94e9307e83e.out \
$(srcdir)/%reldir%/test_logfile.sh_c18e14a26d8261c9f72747118a469266121d5459.err \
$(srcdir)/%reldir%/test_logfile.sh_c18e14a26d8261c9f72747118a469266121d5459.out \
$(srcdir)/%reldir%/test_logfile.sh_cc368d4b4bb6a9b9c79bd5a70ffa1f2d9d01e286.err \
$(srcdir)/%reldir%/test_logfile.sh_cc368d4b4bb6a9b9c79bd5a70ffa1f2d9d01e286.out \
$(srcdir)/%reldir%/test_logfile.sh_ccb0d31813367c8d9dc5b5df383fac5b780711c1.err \
$(srcdir)/%reldir%/test_logfile.sh_ccb0d31813367c8d9dc5b5df383fac5b780711c1.out \
$(srcdir)/%reldir%/test_logfile.sh_d14f6d8888652321206549df8a9535399f0fd372.err \

@ -745,6 +745,15 @@ For support questions, email:
See Also
:annotate, :comment, :tag
:clear-file-timezone pattern
══════════════════════════════════════════════════════════════════════
Clear the timezone setting for the focused file or the given glob
pattern.
Parameter
pattern The glob pattern to match against files that
should no longer use this timezone
:clear-filter-expr
══════════════════════════════════════════════════════════════════════
Clear the filter expression

@ -0,0 +1,4 @@
Nov 03 16:23:38 veridian automount[7998]: lookup(file): lookup for foobar failed
Nov 03 16:23:38 veridian automount[16442]: attempting to mount entry /auto/opt
Nov 03 16:23:38 veridian automount[7999]: lookup(file): lookup for opt failed
Nov 03 16:47:02 veridian sudo: timstack : TTY=pts/6 ; PWD=/auto/wstimstack/rpms/lbuild/test ; USER=root ; COMMAND=/usr/bin/tail /var/log/messages

@ -0,0 +1,2 @@
 options_path  options 
{test_dir}/logfile_syslog.0 {"default-zone":"America/Los_Angeles"}

@ -0,0 +1,15 @@
✘ error: “bad” is not a valid timezone
reason: bad not found in timezone database
 --> command-option:1
 | :set-file-timezone bad 
 = note: did you mean one of the following?
America/Bahia_Banderas
Asia/Ashkhabad
Brazil/DeNoronha
America/Barbados
Asia/Baghdad
 = help: :set-file-timezone zone pattern
══════════════════════════════════════════════════════════════════════
Set the timezone to use for log messages that do not include a
timezone. The timezone is applied to the focused file or the given
glob pattern.

@ -711,10 +711,26 @@ run_cap_test ${lnav_test} -n \
-c ':filter-in Air Mob' \
${test_dir}/logfile_ansi.1
export HOME="./file-tz"
rm -rf "./file-tz"
mkdir -p $HOME
run_cap_test ${lnav_test} -n \
-c ':set-file-timezone America/Los_Angeles' \
${test_dir}/logfile_syslog.0
# make sure the file options were saved and restored
run_cap_test ${lnav_test} -n \
${test_dir}/logfile_syslog.0
run_cap_test ${lnav_test} -n \
-c ";SELECT options_path, options FROM lnav_file" \
${test_dir}/logfile_syslog.0
run_cap_test ${lnav_test} -n \
-c ':set-file-timezone America/New_York' \
${test_dir}/logfile_syslog.0
run_cap_test ${lnav_test} -n \
-c ':set-file-timezone bad' \
${test_dir}/logfile_syslog.0

Loading…
Cancel
Save