mirror of
https://github.com/tstack/lnav.git
synced 2024-09-11 13:05:51 +03:00
[build] some more compiler profiling
Defect Number: Reviewed By: Testing Done:
This commit is contained in:
parent
6fe2f552d5
commit
8e629b166a
@ -238,12 +238,14 @@ add_library(diag STATIC
|
||||
column_namer.cc
|
||||
command_executor.cc
|
||||
curl_looper.cc
|
||||
base/date_time_scanner.cc
|
||||
db_sub_source.cc
|
||||
elem_to_json.cc
|
||||
environ_vtab.cc
|
||||
extension-functions.cc
|
||||
field_overlay_source.cc
|
||||
file_collection.cc
|
||||
file_format.cc
|
||||
file_vtab.cc
|
||||
files_sub_source.cc
|
||||
filter_observer.cc
|
||||
@ -253,6 +255,7 @@ add_library(diag STATIC
|
||||
fstat_vtab.cc
|
||||
fts_fuzzy_match.cc
|
||||
grep_proc.cc
|
||||
help_text.cc
|
||||
help_text_formatter.cc
|
||||
highlighter.cc
|
||||
hist_source.cc
|
||||
@ -301,17 +304,21 @@ add_library(diag STATIC
|
||||
sysclip.cc
|
||||
pcrepp/pcrepp.cc
|
||||
piper_proc.cc
|
||||
spectro_source.cc
|
||||
sql_util.cc
|
||||
state-extension-functions.cc
|
||||
styling.cc
|
||||
base/string_util.cc
|
||||
string_attr_type.cc
|
||||
strnatcmp.c
|
||||
text_format.cc
|
||||
textfile_highlighters.cc
|
||||
textfile_sub_source.cc
|
||||
textview_curses.cc
|
||||
base/time_util.cc
|
||||
time-extension-functions.cc
|
||||
timer.cc
|
||||
unique_path.cc
|
||||
unique_path.hh
|
||||
view_curses.cc
|
||||
view_helpers.cc
|
||||
@ -350,6 +357,7 @@ add_library(diag STATIC
|
||||
command_executor.hh
|
||||
column_namer.hh
|
||||
curl_looper.hh
|
||||
base/date_time_scanner.hh
|
||||
doc_status_source.hh
|
||||
elem_to_json.hh
|
||||
base/enum_util.hh
|
||||
@ -357,6 +365,7 @@ add_library(diag STATIC
|
||||
base/future_util.hh
|
||||
field_overlay_source.hh
|
||||
file_collection.hh
|
||||
file_format.hh
|
||||
file_vtab.hh
|
||||
files_sub_source.hh
|
||||
filter_observer.hh
|
||||
@ -378,6 +387,7 @@ add_library(diag STATIC
|
||||
log_data_helper.hh
|
||||
log_data_table.hh
|
||||
log_format.hh
|
||||
log_format_ext.hh
|
||||
log_format_fwd.hh
|
||||
log_format_impls.cc
|
||||
log_gutter_source.hh
|
||||
@ -386,6 +396,7 @@ add_library(diag STATIC
|
||||
logfile.hh
|
||||
logfile_fwd.hh
|
||||
logfile_stats.hh
|
||||
base/math_util.hh
|
||||
optional.hpp
|
||||
papertrail_proc.hh
|
||||
plain_text_source.hh
|
||||
@ -409,12 +420,15 @@ add_library(diag STATIC
|
||||
simdutf8check.h
|
||||
spectro_source.hh
|
||||
strong_int.hh
|
||||
string_attr_type.hh
|
||||
sysclip.hh
|
||||
term_extra.hh
|
||||
termios_guard.hh
|
||||
text_format.hh
|
||||
textfile_highlighters.hh
|
||||
textfile_sub_source.hh
|
||||
textview_curses.hh
|
||||
textview_curses_fwd.hh
|
||||
time_T.hh
|
||||
timer.hh
|
||||
top_status_source.hh
|
||||
|
@ -269,6 +269,7 @@ noinst_HEADERS = \
|
||||
environ_vtab.hh \
|
||||
field_overlay_source.hh \
|
||||
file_collection.hh \
|
||||
file_format.hh \
|
||||
file_vtab.hh \
|
||||
files_sub_source.hh \
|
||||
filter_observer.hh \
|
||||
@ -298,6 +299,7 @@ noinst_HEADERS = \
|
||||
log_data_helper.hh \
|
||||
log_data_table.hh \
|
||||
log_format.hh \
|
||||
log_format_ext.hh \
|
||||
log_format_fwd.hh \
|
||||
log_format_loader.hh \
|
||||
log_gutter_source.hh \
|
||||
@ -340,6 +342,7 @@ noinst_HEADERS = \
|
||||
sql_util.hh \
|
||||
sqlite-extension-func.hh \
|
||||
statusview_curses.hh \
|
||||
string_attr_type.hh \
|
||||
strnatcmp.h \
|
||||
strong_int.hh \
|
||||
sysclip.hh \
|
||||
@ -349,6 +352,7 @@ noinst_HEADERS = \
|
||||
textfile_highlighters.hh \
|
||||
textfile_sub_source.hh \
|
||||
textview_curses.hh \
|
||||
textview_curses_fwd.hh \
|
||||
time_T.hh \
|
||||
timer.hh \
|
||||
top_status_source.hh \
|
||||
@ -403,6 +407,7 @@ libdiag_a_SOURCES = \
|
||||
extension-functions.cc \
|
||||
field_overlay_source.cc \
|
||||
file_collection.cc \
|
||||
file_format.cc \
|
||||
file_vtab.cc \
|
||||
files_sub_source.cc \
|
||||
filter_observer.cc \
|
||||
@ -412,6 +417,7 @@ libdiag_a_SOURCES = \
|
||||
fs-extension-functions.cc \
|
||||
fts_fuzzy_match.cc \
|
||||
grep_proc.cc \
|
||||
help_text.cc \
|
||||
help_text_formatter.cc \
|
||||
highlighter.cc \
|
||||
hist_source.cc \
|
||||
@ -449,9 +455,11 @@ libdiag_a_SOURCES = \
|
||||
sequence_matcher.cc \
|
||||
shared_buffer.cc \
|
||||
shlex.cc \
|
||||
spectro_source.cc \
|
||||
sqlite-extension-func.cc \
|
||||
statusview_curses.cc \
|
||||
string-extension-functions.cc \
|
||||
string_attr_type.cc \
|
||||
styling.cc \
|
||||
text_format.cc \
|
||||
textfile_sub_source.cc \
|
||||
@ -464,6 +472,7 @@ libdiag_a_SOURCES = \
|
||||
textfile_highlighters.cc \
|
||||
textview_curses.cc \
|
||||
time-extension-functions.cc \
|
||||
unique_path.cc \
|
||||
view_curses.cc \
|
||||
view_helpers.cc \
|
||||
views_vtab.cc \
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "all_logs_vtab.hh"
|
||||
#include "string_attr_type.hh"
|
||||
|
||||
all_logs_vtab::all_logs_vtab()
|
||||
: log_vtab_impl(intern_string::lookup("all_logs")),
|
||||
@ -57,7 +58,7 @@ void all_logs_vtab::extract(std::shared_ptr<logfile> lf, uint64_t line_number,
|
||||
this->vi_attrs.clear();
|
||||
format->annotate(line_number, line, this->vi_attrs, sub_values, false);
|
||||
|
||||
auto body = find_string_attr_range(this->vi_attrs, &textview_curses::SA_BODY);
|
||||
auto body = find_string_attr_range(this->vi_attrs, &SA_BODY);
|
||||
if (body.lr_start == -1) {
|
||||
body.lr_start = 0;
|
||||
body.lr_end = line.length();
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "base/intern_string.hh"
|
||||
#include "string_attr_type.hh"
|
||||
|
||||
/**
|
||||
* Encapsulates a range in a string.
|
||||
@ -145,16 +146,6 @@ typedef union {
|
||||
int64_t sav_int;
|
||||
} string_attr_value_t;
|
||||
|
||||
class string_attr_type {
|
||||
public:
|
||||
explicit string_attr_type(const char *name = nullptr) noexcept
|
||||
: sat_name(name) {
|
||||
};
|
||||
|
||||
const char *sat_name;
|
||||
};
|
||||
typedef string_attr_type *string_attr_type_t;
|
||||
|
||||
struct string_attr {
|
||||
string_attr(const struct line_range &lr, string_attr_type_t type, void *val)
|
||||
: sa_range(lr), sa_type(type) {
|
||||
|
@ -7,6 +7,7 @@ AM_CPPFLAGS = \
|
||||
noinst_LIBRARIES = libbase.a
|
||||
|
||||
noinst_HEADERS = \
|
||||
date_time_scanner.hh \
|
||||
enum_util.hh \
|
||||
file_range.hh \
|
||||
func_util.hh \
|
||||
@ -15,14 +16,17 @@ noinst_HEADERS = \
|
||||
intern_string.hh \
|
||||
is_utf8.hh \
|
||||
lnav_log.hh \
|
||||
math_util.hh \
|
||||
opt_util.hh \
|
||||
result.h \
|
||||
string_util.hh \
|
||||
time_util.hh
|
||||
|
||||
libbase_a_SOURCES = \
|
||||
date_time_scanner.cc \
|
||||
humanize.cc \
|
||||
intern_string.cc \
|
||||
is_utf8.cc \
|
||||
lnav_log.cc \
|
||||
string_util.cc
|
||||
string_util.cc \
|
||||
time_util.cc
|
||||
|
210
src/base/date_time_scanner.cc
Normal file
210
src/base/date_time_scanner.cc
Normal file
@ -0,0 +1,210 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file date_time_scanner.cc
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "date_time_scanner.hh"
|
||||
#include "ptimec.hh"
|
||||
|
||||
size_t date_time_scanner::ftime(char *dst, size_t len, const exttm &tm)
|
||||
{
|
||||
off_t off = 0;
|
||||
|
||||
PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm);
|
||||
|
||||
return (size_t) off;
|
||||
}
|
||||
|
||||
bool next_format(const char * const fmt[], int &index, int &locked_index)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
if (locked_index == -1) {
|
||||
index += 1;
|
||||
if (fmt[index] == nullptr) {
|
||||
retval = false;
|
||||
}
|
||||
}
|
||||
else if (index == locked_index) {
|
||||
retval = false;
|
||||
}
|
||||
else {
|
||||
index = locked_index;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
const char *date_time_scanner::scan(const char *time_dest,
|
||||
size_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct exttm *tm_out,
|
||||
struct timeval &tv_out,
|
||||
bool convert_local)
|
||||
{
|
||||
int curr_time_fmt = -1;
|
||||
bool found = false;
|
||||
const char *retval = nullptr;
|
||||
|
||||
if (!time_fmt) {
|
||||
time_fmt = PTIMEC_FORMAT_STR;
|
||||
}
|
||||
|
||||
while (next_format(time_fmt,
|
||||
curr_time_fmt,
|
||||
this->dts_fmt_lock)) {
|
||||
*tm_out = this->dts_base_tm;
|
||||
tm_out->et_flags = 0;
|
||||
if (time_len > 1 &&
|
||||
time_dest[0] == '+' &&
|
||||
isdigit(time_dest[1])) {
|
||||
char time_cp[time_len + 1];
|
||||
int gmt_int, off;
|
||||
|
||||
retval = nullptr;
|
||||
memcpy(time_cp, time_dest, time_len);
|
||||
time_cp[time_len] = '\0';
|
||||
if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
|
||||
time_t gmt = gmt_int;
|
||||
|
||||
if (convert_local && this->dts_local_time) {
|
||||
localtime_r(&gmt, &tm_out->et_tm);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
#endif
|
||||
tm_out->et_tm.tm_isdst = 0;
|
||||
gmt = tm2sec(&tm_out->et_tm);
|
||||
}
|
||||
tv_out.tv_sec = gmt;
|
||||
tv_out.tv_usec = 0;
|
||||
tm_out->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = off;
|
||||
retval = time_dest + off;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (time_fmt == PTIMEC_FORMAT_STR) {
|
||||
ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
|
||||
off_t off = 0;
|
||||
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (!this->dts_keep_base_tz) {
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (func(tm_out, time_dest, off, time_len)) {
|
||||
retval = &time_dest[off];
|
||||
|
||||
if (tm_out->et_tm.tm_year < 70) {
|
||||
tm_out->et_tm.tm_year = 80;
|
||||
}
|
||||
if (convert_local &&
|
||||
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
||||
time_t gmt = tm2sec(&tm_out->et_tm);
|
||||
|
||||
this->to_localtime(gmt, *tm_out);
|
||||
}
|
||||
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
secs2wday(tv_out, &tm_out->et_tm);
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = retval - time_dest;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
off_t off = 0;
|
||||
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (!this->dts_keep_base_tz) {
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (ptime_fmt(time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) &&
|
||||
(time_dest[off] == '.' || time_dest[off] == ',' || off == (off_t)time_len)) {
|
||||
retval = &time_dest[off];
|
||||
if (tm_out->et_tm.tm_year < 70) {
|
||||
tm_out->et_tm.tm_year = 80;
|
||||
}
|
||||
if (convert_local &&
|
||||
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
||||
time_t gmt = tm2sec(&tm_out->et_tm);
|
||||
|
||||
this->to_localtime(gmt, *tm_out);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
#endif
|
||||
tm_out->et_tm.tm_isdst = 0;
|
||||
}
|
||||
|
||||
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
secs2wday(tv_out, &tm_out->et_tm);
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = retval - time_dest;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
retval = nullptr;
|
||||
}
|
||||
|
||||
if (retval != nullptr) {
|
||||
/* Try to pull out the milli/micro-second value. */
|
||||
if (retval[0] == '.' || retval[0] == ',') {
|
||||
off_t off = (retval - time_dest) + 1;
|
||||
|
||||
if (ptime_f(tm_out, time_dest, off, time_len)) {
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
this->dts_fmt_len += 7;
|
||||
retval += 7;
|
||||
}
|
||||
else if (ptime_L(tm_out, time_dest, off, time_len)) {
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
this->dts_fmt_len += 4;
|
||||
retval += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
154
src/base/date_time_scanner.hh
Normal file
154
src/base/date_time_scanner.hh
Normal file
@ -0,0 +1,154 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file date_time_scanner.hh
|
||||
*/
|
||||
|
||||
#ifndef lnav_date_time_scanner_hh
|
||||
#define lnav_date_time_scanner_hh
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "time_util.hh"
|
||||
|
||||
extern const char *std_time_fmt[];
|
||||
|
||||
struct date_time_scanner {
|
||||
date_time_scanner() : dts_keep_base_tz(false),
|
||||
dts_local_time(false),
|
||||
dts_local_offset_cache(0),
|
||||
dts_local_offset_valid(0),
|
||||
dts_local_offset_expiry(0) {
|
||||
this->clear();
|
||||
};
|
||||
|
||||
void clear() {
|
||||
this->dts_base_time = 0;
|
||||
memset(&this->dts_base_tm, 0, sizeof(this->dts_base_tm));
|
||||
this->dts_fmt_lock = -1;
|
||||
this->dts_fmt_len = -1;
|
||||
};
|
||||
|
||||
void unlock() {
|
||||
this->dts_fmt_lock = -1;
|
||||
this->dts_fmt_len = -1;
|
||||
}
|
||||
|
||||
void set_base_time(time_t base_time) {
|
||||
this->dts_base_time = base_time;
|
||||
localtime_r(&base_time, &this->dts_base_tm.et_tm);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a timestamp to local time.
|
||||
*
|
||||
* Calling localtime_r is slow since it wants to lookup the timezone on
|
||||
* every call, so we cache the result and only call it again if the
|
||||
* requested time falls outside of a fifteen minute range.
|
||||
*/
|
||||
void to_localtime(time_t t, struct exttm &tm_out) {
|
||||
if (t < (24 * 60 * 60)) {
|
||||
// Don't convert and risk going past the epoch.
|
||||
return;
|
||||
}
|
||||
|
||||
if (t < this->dts_local_offset_valid ||
|
||||
t >= this->dts_local_offset_expiry) {
|
||||
time_t new_gmt;
|
||||
|
||||
localtime_r(&t, &tm_out.et_tm);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out.et_tm.tm_zone = NULL;
|
||||
#endif
|
||||
tm_out.et_tm.tm_isdst = 0;
|
||||
|
||||
new_gmt = tm2sec(&tm_out.et_tm);
|
||||
this->dts_local_offset_cache = t - new_gmt;
|
||||
this->dts_local_offset_valid = t;
|
||||
this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
|
||||
this->dts_local_offset_expiry -=
|
||||
this->dts_local_offset_expiry % EXPIRE_TIME;
|
||||
}
|
||||
else {
|
||||
time_t adjust_gmt = t - this->dts_local_offset_cache;
|
||||
gmtime_r(&adjust_gmt, &tm_out.et_tm);
|
||||
}
|
||||
};
|
||||
|
||||
bool dts_keep_base_tz;
|
||||
bool dts_local_time;
|
||||
time_t dts_base_time;
|
||||
struct exttm dts_base_tm;
|
||||
int dts_fmt_lock;
|
||||
int dts_fmt_len;
|
||||
time_t dts_local_offset_cache;
|
||||
time_t dts_local_offset_valid;
|
||||
time_t dts_local_offset_expiry;
|
||||
|
||||
static const int EXPIRE_TIME = 15 * 60;
|
||||
|
||||
const char *scan(const char *time_src,
|
||||
size_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct exttm *tm_out,
|
||||
struct timeval &tv_out,
|
||||
bool convert_local = true);
|
||||
|
||||
size_t ftime(char *dst, size_t len, const struct exttm &tm);;
|
||||
|
||||
bool convert_to_timeval(const char *time_src,
|
||||
ssize_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct timeval &tv_out) {
|
||||
struct exttm tm;
|
||||
|
||||
if (time_len == -1) {
|
||||
time_len = strlen(time_src);
|
||||
}
|
||||
if (this->scan(time_src, time_len, time_fmt, &tm, tv_out) != NULL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool convert_to_timeval(const std::string &time_src,
|
||||
struct timeval &tv_out) {
|
||||
struct exttm tm;
|
||||
|
||||
if (this->scan(time_src.c_str(), time_src.size(),
|
||||
NULL, &tm, tv_out) != NULL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
63
src/base/math_util.hh
Normal file
63
src/base/math_util.hh
Normal file
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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_math_util_hh
|
||||
#define lnav_math_util_hh
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#undef rounddown
|
||||
|
||||
/**
|
||||
* Round down a number based on a given granularity.
|
||||
*
|
||||
* @param
|
||||
* @param step The granularity.
|
||||
*/
|
||||
template<typename Size, typename Step>
|
||||
inline int rounddown(Size size, Step step)
|
||||
{
|
||||
return size - (size % step);
|
||||
}
|
||||
|
||||
inline int rounddown_offset(size_t size, int step, int offset)
|
||||
{
|
||||
return size - ((size - offset) % step);
|
||||
}
|
||||
|
||||
inline size_t roundup_size(size_t size, int step)
|
||||
{
|
||||
size_t retval = size + step;
|
||||
|
||||
retval -= (retval % step);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif
|
@ -29,6 +29,9 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
|
||||
#include "lnav_log.hh"
|
||||
#include "is_utf8.hh"
|
||||
#include "string_util.hh"
|
||||
@ -102,3 +105,49 @@ void truncate_to(std::string &str, size_t len)
|
||||
str.erase(half_width, str.length() - (half_width * 2));
|
||||
str.insert(half_width, ELLIPSIS);
|
||||
}
|
||||
|
||||
bool is_url(const char *fn)
|
||||
{
|
||||
static auto url_re = std::regex("^(file|https?|ftps?|scp|sftp):.*");
|
||||
|
||||
return std::regex_match(fn, url_re);
|
||||
}
|
||||
|
||||
size_t abbreviate_str(char *str, size_t len, size_t max_len)
|
||||
{
|
||||
size_t last_start = 1;
|
||||
|
||||
if (len < max_len) {
|
||||
return len;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < len; index++) {
|
||||
switch (str[index]) {
|
||||
case '.':
|
||||
case '-':
|
||||
case '/':
|
||||
case ':':
|
||||
memmove(&str[last_start], &str[index], len - index);
|
||||
len -= (index - last_start);
|
||||
index = last_start + 1;
|
||||
last_start = index + 1;
|
||||
|
||||
if (len < max_len) {
|
||||
return len;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
void split_ws(const std::string &str, std::vector<std::string> &toks_out)
|
||||
{
|
||||
std::stringstream ss(str);
|
||||
std::string buf;
|
||||
|
||||
while (ss >> buf) {
|
||||
toks_out.push_back(buf);
|
||||
}
|
||||
}
|
||||
|
@ -134,4 +134,10 @@ inline ssize_t utf8_char_to_byte_index(const std::string &str, ssize_t ch_index)
|
||||
return retval;
|
||||
}
|
||||
|
||||
bool is_url(const char *fn);
|
||||
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
184
src/base/time_util.cc
Normal file
184
src/base/time_util.cc
Normal file
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file time_util.cc
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "time_util.hh"
|
||||
|
||||
static time_t BAD_DATE = -1;
|
||||
|
||||
time_t tm2sec(const struct tm *t)
|
||||
{
|
||||
int year;
|
||||
time_t days, secs;
|
||||
const int dayoffset[12] =
|
||||
{ 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 };
|
||||
|
||||
year = t->tm_year;
|
||||
|
||||
if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138))) {
|
||||
return BAD_DATE;
|
||||
}
|
||||
|
||||
/* shift new year to 1st March in order to make leap year calc easy */
|
||||
|
||||
if (t->tm_mon < 2) {
|
||||
year--;
|
||||
}
|
||||
|
||||
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
|
||||
|
||||
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
|
||||
days += dayoffset[t->tm_mon] + t->tm_mday - 1;
|
||||
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
|
||||
|
||||
secs = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
|
||||
|
||||
if (secs < 0) {
|
||||
return BAD_DATE;
|
||||
} /* must have overflowed */
|
||||
else {
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (t->tm_zone) {
|
||||
secs -= t->tm_gmtoff;
|
||||
}
|
||||
#endif
|
||||
return secs;
|
||||
} /* must be a valid time */
|
||||
}
|
||||
|
||||
static const int SECSPERMIN = 60;
|
||||
static const int SECSPERHOUR = 60 * SECSPERMIN;
|
||||
static const int SECSPERDAY = 24 * SECSPERHOUR;
|
||||
static const int YEAR_BASE = 1900;
|
||||
static const int EPOCH_WDAY = 4;
|
||||
static const int DAYSPERWEEK = 7;
|
||||
static const int EPOCH_YEAR = 1970;
|
||||
|
||||
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
|
||||
|
||||
static const int year_lengths[2] = {
|
||||
365,
|
||||
366
|
||||
};
|
||||
|
||||
const unsigned short int mon_yday[2][13] = {
|
||||
/* Normal years. */
|
||||
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
|
||||
/* Leap years. */
|
||||
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
|
||||
};
|
||||
|
||||
void secs2wday(const struct timeval &tv, struct tm *res)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
|
||||
/* base decision about std/dst time on current time */
|
||||
lcltime = tv.tv_sec;
|
||||
|
||||
days = ((long) lcltime) / SECSPERDAY;
|
||||
rem = ((long) lcltime) % SECSPERDAY;
|
||||
while (rem < 0) {
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
}
|
||||
|
||||
struct tm *secs2tm(time_t *tim_p, struct tm *res)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
int y;
|
||||
int yleap;
|
||||
const unsigned short int *ip;
|
||||
|
||||
/* base decision about std/dst time on current time */
|
||||
lcltime = *tim_p;
|
||||
|
||||
days = ((long)lcltime) / SECSPERDAY;
|
||||
rem = ((long)lcltime) % SECSPERDAY;
|
||||
while (rem < 0)
|
||||
{
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
|
||||
/* compute hour, min, and sec */
|
||||
res->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
rem %= SECSPERHOUR;
|
||||
res->tm_min = (int) (rem / SECSPERMIN);
|
||||
res->tm_sec = (int) (rem % SECSPERMIN);
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
|
||||
/* compute year & day of year */
|
||||
y = EPOCH_YEAR;
|
||||
if (days >= 0)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
yleap = isleap(y);
|
||||
if (days < year_lengths[yleap])
|
||||
break;
|
||||
y++;
|
||||
days -= year_lengths[yleap];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
--y;
|
||||
yleap = isleap(y);
|
||||
days += year_lengths[yleap];
|
||||
} while (days < 0);
|
||||
}
|
||||
|
||||
res->tm_year = y - YEAR_BASE;
|
||||
res->tm_yday = days;
|
||||
ip = mon_yday[isleap(y)];
|
||||
for (y = 11; days < (long int) ip[y]; --y)
|
||||
continue;
|
||||
days -= ip[y];
|
||||
res->tm_mon = y;
|
||||
res->tm_mday = days + 1;
|
||||
|
||||
res->tm_isdst = 0;
|
||||
|
||||
return (res);
|
||||
}
|
@ -32,27 +32,96 @@
|
||||
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
|
||||
struct tm *secs2tm(time_t *tim_p, struct tm *res);
|
||||
/**
|
||||
* Convert the time stored in a 'tm' struct into epoch time.
|
||||
*
|
||||
* @param t The 'tm' structure to convert to epoch time.
|
||||
* @return The given time in seconds since the epoch.
|
||||
*/
|
||||
time_t tm2sec(const struct tm *t);
|
||||
void secs2wday(const struct timeval &tv, struct tm *res);
|
||||
|
||||
constexpr time_t MAX_TIME_T = 4000000000LL;
|
||||
|
||||
enum exttm_bits_t {
|
||||
ETB_YEAR_SET,
|
||||
ETB_MONTH_SET,
|
||||
ETB_DAY_SET,
|
||||
ETB_MACHINE_ORIENTED,
|
||||
ETB_EPOCH_TIME,
|
||||
};
|
||||
|
||||
enum exttm_flags_t {
|
||||
ETF_YEAR_SET = (1UL << ETB_YEAR_SET),
|
||||
ETF_MONTH_SET = (1UL << ETB_MONTH_SET),
|
||||
ETF_DAY_SET = (1UL << ETB_DAY_SET),
|
||||
ETF_MACHINE_ORIENTED = (1UL << ETB_MACHINE_ORIENTED),
|
||||
ETF_EPOCH_TIME = (1UL << ETB_EPOCH_TIME),
|
||||
};
|
||||
|
||||
struct exttm {
|
||||
struct tm et_tm;
|
||||
int32_t et_nsec;
|
||||
unsigned int et_flags;
|
||||
long et_gmtoff;
|
||||
|
||||
bool operator==(const exttm &other) const {
|
||||
return memcmp(this, &other, sizeof(exttm)) == 0;
|
||||
};
|
||||
|
||||
struct timeval to_timeval() const {
|
||||
struct timeval retval;
|
||||
|
||||
retval.tv_sec = tm2sec(&this->et_tm);
|
||||
retval.tv_usec = this->et_nsec * 1000;
|
||||
|
||||
return retval;
|
||||
};
|
||||
};
|
||||
|
||||
inline
|
||||
bool operator<(const struct timeval &left, time_t right) {
|
||||
return left.tv_sec < right;
|
||||
};
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator<(time_t left, const struct timeval &right) {
|
||||
return left < right.tv_sec;
|
||||
};
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator<(const struct timeval &left, const struct timeval &right) {
|
||||
return left.tv_sec < right.tv_sec ||
|
||||
((left.tv_sec == right.tv_sec) && (left.tv_usec < right.tv_usec));
|
||||
};
|
||||
}
|
||||
|
||||
inline
|
||||
bool operator!=(const struct timeval &left, const struct timeval &right) {
|
||||
return left.tv_sec != right.tv_sec ||
|
||||
left.tv_usec != right.tv_usec;
|
||||
};
|
||||
}
|
||||
|
||||
typedef int64_t mstime_t;
|
||||
|
||||
inline mstime_t getmstime() {
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
|
||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
|
||||
}
|
||||
|
||||
inline time_t day_num(time_t ti)
|
||||
{
|
||||
return ti / (24 * 60 * 60);
|
||||
}
|
||||
|
||||
inline time_t hour_num(time_t ti)
|
||||
{
|
||||
return ti / (60 * 60);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/math_util.hh"
|
||||
|
||||
template<typename T>
|
||||
struct big_array {
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include "lnav.hh"
|
||||
#include "log_format_loader.hh"
|
||||
#include "shlex.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "sql_util.hh"
|
||||
|
||||
#include "command_executor.hh"
|
||||
@ -827,3 +828,14 @@ void add_global_vars(exec_context &ec)
|
||||
ec.ec_global_vars[iter.first] = str;
|
||||
}
|
||||
}
|
||||
|
||||
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("{}:{}: error: ", source.first, source.second);
|
||||
}
|
||||
|
@ -63,15 +63,7 @@ struct exec_context {
|
||||
this->ec_output_stack.emplace_back(nonstd::nullopt);
|
||||
}
|
||||
|
||||
std::string get_error_prefix() {
|
||||
if (this->ec_source.size() <= 1) {
|
||||
return "error: ";
|
||||
}
|
||||
|
||||
std::pair<std::string, int> source = this->ec_source.top();
|
||||
|
||||
return fmt::format("{}:{}: error: ", source.first, source.second);
|
||||
}
|
||||
std::string get_error_prefix();
|
||||
|
||||
template<typename ...Args>
|
||||
Result<std::string, std::string> make_error(
|
||||
|
@ -66,7 +66,7 @@ public:
|
||||
|
||||
#include "auto_mem.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "base/time_util.hh"
|
||||
|
||||
class curl_request {
|
||||
public:
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "spookyhash/SpookyV2.h"
|
||||
#include "data_parser.hh"
|
||||
|
||||
using namespace std;
|
||||
|
@ -38,7 +38,6 @@
|
||||
#include <iterator>
|
||||
|
||||
#include "base/lnav_log.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "byte_array.hh"
|
||||
#include "data_scanner.hh"
|
||||
@ -220,7 +219,7 @@ public:
|
||||
|
||||
static FILE *TRACE_FILE;
|
||||
|
||||
typedef byte_array<2, uint64> schema_id_t;
|
||||
typedef byte_array<2, uint64_t> schema_id_t;
|
||||
|
||||
struct element;
|
||||
/* typedef std::list<element> element_list_t; */
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/date_time_scanner.hh"
|
||||
#include "base/time_util.hh"
|
||||
|
||||
#include "yajlpp/json_ptr.hh"
|
||||
|
@ -77,9 +77,9 @@ public:
|
||||
|
||||
void clear();
|
||||
|
||||
long column_name_to_index(const std::string &name) const;;
|
||||
long column_name_to_index(const std::string &name) const;
|
||||
|
||||
int row_for_time(struct timeval time_bucket);;
|
||||
int row_for_time(struct timeval time_bucket);
|
||||
|
||||
struct timeval time_for_row(int row) {
|
||||
if ((row < 0) || (((size_t) row) >= this->dls_time_column.size())) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "ansi_scrubber.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "relative_time.hh"
|
||||
@ -523,7 +524,7 @@ void field_overlay_source::build_meta_line(const listview_curses &lv,
|
||||
|
||||
const auto *tc = dynamic_cast<const textview_curses *>(&lv);
|
||||
if (tc) {
|
||||
const textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
const auto &hm = tc->get_highlights();
|
||||
auto hl_iter = hm.find({highlight_source_t::PREVIEW, "search"});
|
||||
|
||||
if (hl_iter != hm.end()) {
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include "base/future_util.hh"
|
||||
#include "logfile_fwd.hh"
|
||||
#include "archive_manager.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "file_format.hh"
|
||||
|
||||
struct scan_progress {
|
||||
std::list<archive_manager::extract_progress> sp_extractions;
|
||||
|
64
src/file_format.cc
Normal file
64
src/file_format.cc
Normal file
@ -0,0 +1,64 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file file_format.hh
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "base/intern_string.hh"
|
||||
#include "auto_fd.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "file_format.hh"
|
||||
#include "archive_manager.hh"
|
||||
|
||||
file_format_t detect_file_format(const ghc::filesystem::path &filename)
|
||||
{
|
||||
if (archive_manager::is_archive(filename)) {
|
||||
return file_format_t::FF_ARCHIVE;
|
||||
}
|
||||
|
||||
file_format_t retval = file_format_t::FF_UNKNOWN;
|
||||
auto_fd fd;
|
||||
|
||||
if ((fd = openp(filename, O_RDONLY)) != -1) {
|
||||
char buffer[32];
|
||||
ssize_t rc;
|
||||
|
||||
if ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
|
||||
static auto SQLITE3_HEADER = "SQLite format 3";
|
||||
auto header_frag = string_fragment(buffer, 0, rc);
|
||||
|
||||
if (header_frag.startswith(SQLITE3_HEADER)) {
|
||||
retval = file_format_t::FF_SQLITE_DB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
69
src/file_format.hh
Normal file
69
src/file_format.hh
Normal file
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file file_format.hh
|
||||
*/
|
||||
|
||||
#ifndef lnav_file_format_hh
|
||||
#define lnav_file_format_hh
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
enum class file_format_t {
|
||||
FF_UNKNOWN,
|
||||
FF_SQLITE_DB,
|
||||
FF_ARCHIVE,
|
||||
};
|
||||
|
||||
file_format_t detect_file_format(const ghc::filesystem::path& filename);
|
||||
|
||||
namespace fmt {
|
||||
template<>
|
||||
struct formatter<file_format_t> : formatter<string_view> {
|
||||
template<typename FormatContext>
|
||||
auto format(file_format_t ff, FormatContext &ctx)
|
||||
{
|
||||
string_view name = "unknown";
|
||||
switch (ff) {
|
||||
case file_format_t::FF_SQLITE_DB:
|
||||
name = "SQLite Database";
|
||||
break;
|
||||
case file_format_t::FF_ARCHIVE:
|
||||
name = "Archive";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return formatter<string_view>::format(name, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
@ -33,6 +33,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "base/lnav_log.hh"
|
||||
#include "logfile.hh"
|
||||
#include "file_vtab.hh"
|
||||
#include "session_data.hh"
|
||||
#include "vtab_module.hh"
|
||||
|
@ -53,7 +53,7 @@ public:
|
||||
logfile::const_iterator ll_end,
|
||||
shared_buffer_ref &sbr);
|
||||
|
||||
void logline_eof(const logfile &lf);;
|
||||
void logline_eof(const logfile &lf);
|
||||
|
||||
bool excluded(uint32_t filter_in_mask, uint32_t filter_out_mask,
|
||||
size_t offset) const {
|
||||
|
@ -385,7 +385,7 @@ void filter_sub_source::rl_change(readline_curses *rc)
|
||||
lnav_data.ld_filter_help_status_source.fss_error
|
||||
.set_value("error: %s", errptr);
|
||||
} else {
|
||||
textview_curses::highlight_map_t &hm = top_view->get_highlights();
|
||||
auto &hm = top_view->get_highlights();
|
||||
highlighter hl(code.release());
|
||||
int color;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
98
src/help_text.cc
Normal file
98
src/help_text.cc
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "help_text.hh"
|
||||
|
||||
|
||||
help_text &help_text::with_parameters(
|
||||
const std::initializer_list<help_text> ¶ms) noexcept
|
||||
{
|
||||
this->ht_parameters = params;
|
||||
for (auto ¶m : this->ht_parameters) {
|
||||
param.ht_context = help_context_t::HC_PARAMETER;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_parameter(const help_text &ht) noexcept
|
||||
{
|
||||
this->ht_parameters.emplace_back(ht);
|
||||
this->ht_parameters.back().ht_context = help_context_t::HC_PARAMETER;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_result(const help_text &ht) noexcept
|
||||
{
|
||||
this->ht_results.emplace_back(ht);
|
||||
this->ht_results.back().ht_context = help_context_t::HC_RESULT;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_examples(
|
||||
const std::initializer_list<help_example> &examples) noexcept
|
||||
{
|
||||
this->ht_example = examples;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_example(const help_example &example) noexcept
|
||||
{
|
||||
this->ht_example.emplace_back(example);
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_enum_values(
|
||||
const std::initializer_list<const char *> &enum_values) noexcept
|
||||
{
|
||||
this->ht_enum_values = enum_values;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &
|
||||
help_text::with_tags(const std::initializer_list<const char *> &tags) noexcept
|
||||
{
|
||||
this->ht_tags = tags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &help_text::with_opposites(
|
||||
const std::initializer_list<const char *> &opps) noexcept
|
||||
{
|
||||
this->ht_opposites = opps;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void help_text::index_tags()
|
||||
{
|
||||
for (const auto &tag: this->ht_tags) {
|
||||
TAGGED.insert(std::make_pair(tag, this));
|
||||
}
|
||||
}
|
@ -129,35 +129,15 @@ struct help_text {
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &with_parameters(const std::initializer_list<help_text> ¶ms) noexcept {
|
||||
this->ht_parameters = params;
|
||||
for (auto ¶m : this->ht_parameters) {
|
||||
param.ht_context = help_context_t::HC_PARAMETER;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
help_text &with_parameters(const std::initializer_list<help_text> ¶ms) noexcept;
|
||||
|
||||
help_text &with_parameter(const help_text &ht) noexcept {
|
||||
this->ht_parameters.emplace_back(ht);
|
||||
this->ht_parameters.back().ht_context = help_context_t::HC_PARAMETER;
|
||||
return *this;
|
||||
};
|
||||
help_text &with_parameter(const help_text &ht) noexcept;
|
||||
|
||||
help_text &with_result(const help_text &ht) noexcept {
|
||||
this->ht_results.emplace_back(ht);
|
||||
this->ht_results.back().ht_context = help_context_t::HC_RESULT;
|
||||
return *this;
|
||||
};
|
||||
help_text &with_result(const help_text &ht) noexcept;
|
||||
|
||||
help_text &with_examples(const std::initializer_list<help_example> &examples) noexcept {
|
||||
this->ht_example = examples;
|
||||
return *this;
|
||||
}
|
||||
help_text &with_examples(const std::initializer_list<help_example> &examples) noexcept;
|
||||
|
||||
help_text &with_example(const help_example &example) noexcept {
|
||||
this->ht_example.emplace_back(example);
|
||||
return *this;
|
||||
}
|
||||
help_text &with_example(const help_example &example) noexcept;
|
||||
|
||||
help_text &optional() noexcept {
|
||||
this->ht_nargs = help_nargs_t::HN_OPTIONAL;
|
||||
@ -179,26 +159,13 @@ struct help_text {
|
||||
return *this;
|
||||
}
|
||||
|
||||
help_text &with_enum_values(const std::initializer_list<const char*> &enum_values) noexcept {
|
||||
this->ht_enum_values = enum_values;
|
||||
return *this;
|
||||
};
|
||||
help_text &with_enum_values(const std::initializer_list<const char*> &enum_values) noexcept;
|
||||
|
||||
help_text &with_tags(const std::initializer_list<const char*> &tags) noexcept {
|
||||
this->ht_tags = tags;
|
||||
return *this;
|
||||
};
|
||||
help_text &with_tags(const std::initializer_list<const char*> &tags) noexcept;
|
||||
|
||||
help_text &with_opposites(const std::initializer_list<const char*> &opps) noexcept {
|
||||
this->ht_opposites = opps;
|
||||
return *this;
|
||||
};
|
||||
help_text &with_opposites(const std::initializer_list<const char*> &opps) noexcept;
|
||||
|
||||
void index_tags() {
|
||||
for (const auto &tag: this->ht_tags) {
|
||||
TAGGED.insert(std::make_pair(tag, this));
|
||||
}
|
||||
};
|
||||
void index_tags();
|
||||
|
||||
static std::multimap<std::string, help_text *> TAGGED;
|
||||
};
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/math_util.hh"
|
||||
#include "hist_source.hh"
|
||||
|
||||
using namespace std;
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "base/math_util.hh"
|
||||
#include "lnav.hh"
|
||||
#include "bookmarks.hh"
|
||||
#include "sql_util.hh"
|
||||
@ -44,6 +45,7 @@
|
||||
#include "hotkeys.hh"
|
||||
#include "base/opt_util.hh"
|
||||
#include "shlex.hh"
|
||||
#include "lnav_util.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -47,8 +47,8 @@
|
||||
#include "simdutf8check.h"
|
||||
#endif
|
||||
|
||||
#include "base/math_util.hh"
|
||||
#include "base/is_utf8.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "line_buffer.hh"
|
||||
#include "fmtlib/fmt/format.h"
|
||||
|
||||
@ -116,6 +116,14 @@ private:
|
||||
};
|
||||
/* XXX END */
|
||||
|
||||
static int32_t read_le32(const unsigned char *data)
|
||||
{
|
||||
return (
|
||||
(data[0] << 0) |
|
||||
(data[1] << 8) |
|
||||
(data[2] << 16) |
|
||||
(data[3] << 24));
|
||||
}
|
||||
|
||||
#define Z_BUFSIZE 65536U
|
||||
#define SYNCPOINT_SIZE (1024 * 1024)
|
||||
|
@ -43,6 +43,11 @@ using namespace std;
|
||||
|
||||
list_gutter_source listview_curses::DEFAULT_GUTTER_SOURCE;
|
||||
|
||||
listview_curses::listview_curses()
|
||||
: lv_scroll(noop_func{})
|
||||
{
|
||||
}
|
||||
|
||||
void listview_curses::reload_data()
|
||||
{
|
||||
if (this->lv_source == nullptr) {
|
||||
|
@ -117,6 +117,8 @@ class listview_curses
|
||||
public:
|
||||
using action = std::function<void(listview_curses*)>;
|
||||
|
||||
listview_curses();
|
||||
|
||||
void set_title(const std::string &title) {
|
||||
this->lv_title = title;
|
||||
};
|
||||
@ -581,7 +583,7 @@ protected:
|
||||
list_data_source *lv_source{nullptr}; /*< The data source delegate. */
|
||||
std::list<list_input_delegate *> lv_input_delegates;
|
||||
list_overlay_source *lv_overlay_source{nullptr};
|
||||
action lv_scroll{noop_func{}}; /*< The scroll action. */
|
||||
action lv_scroll; /*< The scroll action. */
|
||||
WINDOW * lv_window{nullptr}; /*< The window that contains this view. */
|
||||
unsigned int lv_x{0};
|
||||
unsigned int lv_y{0}; /*< The y offset of this view. */
|
||||
|
@ -2706,7 +2706,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
struct line_range lr = find_string_attr_range(
|
||||
rows[0].get_attrs(), &textview_curses::SA_ORIGINAL_LINE);
|
||||
rows[0].get_attrs(), &SA_ORIGINAL_LINE);
|
||||
if (write(STDOUT_FILENO, lr.substr(rows[0].get_string()),
|
||||
lr.sublen(rows[0].get_string())) == -1 ||
|
||||
write(STDOUT_FILENO, "\n", 1) == -1) {
|
||||
|
@ -588,8 +588,7 @@ static void json_write_row(yajl_gen handle, int row)
|
||||
static void write_line_to(FILE *outfile, const attr_line_t &al)
|
||||
{
|
||||
const auto& al_attrs = al.get_attrs();
|
||||
auto lr = find_string_attr_range(
|
||||
al_attrs, &textview_curses::SA_ORIGINAL_LINE);
|
||||
auto lr = find_string_attr_range(al_attrs, &SA_ORIGINAL_LINE);
|
||||
const auto& line_meta = find_string_attr(al_attrs, &logline::L_META);
|
||||
|
||||
if (lr.lr_start > 1) {
|
||||
@ -1141,7 +1140,7 @@ static Result<string, string> com_highlight(exec_context &ec, string cmdline, ve
|
||||
}
|
||||
else if (args.size() > 1) {
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
auto &hm = tc->get_highlights();
|
||||
const char *errptr;
|
||||
auto_mem<pcre> code;
|
||||
int eoff;
|
||||
@ -1203,11 +1202,10 @@ static Result<string, string> com_clear_highlight(exec_context &ec, string cmdli
|
||||
}
|
||||
else if (args.size() > 1 && args[1][0] != '$') {
|
||||
textview_curses *tc = *lnav_data.ld_view_stack.top();
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
textview_curses::highlight_map_t::iterator hm_iter;
|
||||
auto &hm = tc->get_highlights();
|
||||
|
||||
args[1] = remaining_args(cmdline, args);
|
||||
hm_iter = hm.find({highlight_source_t::INTERACTIVE, args[1]});
|
||||
auto hm_iter = hm.find({highlight_source_t::INTERACTIVE, args[1]});
|
||||
if (hm_iter == hm.end()) {
|
||||
return ec.make_error("highlight does not exist -- {}", args[1]);
|
||||
}
|
||||
@ -1291,7 +1289,7 @@ static Result<string, string> com_filter(exec_context &ec, string cmdline, vecto
|
||||
.set_value("Match preview for :filter-in only works if there are no other filters");
|
||||
retval = "";
|
||||
} else {
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
auto &hm = tc->get_highlights();
|
||||
highlighter hl(code.release());
|
||||
int color;
|
||||
|
||||
@ -1608,7 +1606,7 @@ static Result<string, string> com_create_search_table(exec_context &ec, string c
|
||||
|
||||
if (ec.ec_dry_run) {
|
||||
textview_curses *tc = &lnav_data.ld_views[LNV_LOG];
|
||||
textview_curses::highlight_map_t &hm = tc->get_highlights();
|
||||
auto &hm = tc->get_highlights();
|
||||
view_colors &vc = view_colors::singleton();
|
||||
highlighter hl(code.release());
|
||||
|
||||
|
399
src/lnav_util.cc
399
src/lnav_util.cc
@ -34,34 +34,16 @@
|
||||
#include "config.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "auto_fd.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "base/opt_util.hh"
|
||||
#include "base/result.h"
|
||||
#include "ansi_scrubber.hh"
|
||||
#include "view_curses.hh"
|
||||
#include "archive_manager.hh"
|
||||
#include "fmt/format.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool is_url(const char *fn)
|
||||
{
|
||||
static pcrepp url_re("^(file|https?|ftps?||scp|sftp):");
|
||||
|
||||
pcre_context_static<30> pc;
|
||||
pcre_input pi(fn);
|
||||
|
||||
return url_re.match(pc, pi);
|
||||
}
|
||||
|
||||
std::string time_ago(time_t last_time, bool convert_local)
|
||||
{
|
||||
time_t delta, current_time = time(nullptr);
|
||||
@ -177,358 +159,6 @@ bool change_to_parent_dir()
|
||||
return retval;
|
||||
}
|
||||
|
||||
void split_ws(const std::string &str, std::vector<std::string> &toks_out)
|
||||
{
|
||||
std::stringstream ss(str);
|
||||
std::string buf;
|
||||
|
||||
while (ss >> buf) {
|
||||
toks_out.push_back(buf);
|
||||
}
|
||||
}
|
||||
|
||||
file_format_t detect_file_format(const ghc::filesystem::path &filename)
|
||||
{
|
||||
if (archive_manager::is_archive(filename)) {
|
||||
return file_format_t::FF_ARCHIVE;
|
||||
}
|
||||
|
||||
file_format_t retval = file_format_t::FF_UNKNOWN;
|
||||
auto_fd fd;
|
||||
|
||||
if ((fd = openp(filename, O_RDONLY)) != -1) {
|
||||
char buffer[32];
|
||||
ssize_t rc;
|
||||
|
||||
if ((rc = read(fd, buffer, sizeof(buffer))) > 0) {
|
||||
static auto SQLITE3_HEADER = "SQLite format 3";
|
||||
auto header_frag = string_fragment(buffer, 0, rc);
|
||||
|
||||
if (header_frag.startswith(SQLITE3_HEADER)) {
|
||||
retval = file_format_t::FF_SQLITE_DB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static time_t BAD_DATE = -1;
|
||||
|
||||
time_t tm2sec(const struct tm *t)
|
||||
{
|
||||
int year;
|
||||
time_t days, secs;
|
||||
const int dayoffset[12] =
|
||||
{ 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275 };
|
||||
|
||||
year = t->tm_year;
|
||||
|
||||
if (year < 70 || ((sizeof(time_t) <= 4) && (year >= 138))) {
|
||||
return BAD_DATE;
|
||||
}
|
||||
|
||||
/* shift new year to 1st March in order to make leap year calc easy */
|
||||
|
||||
if (t->tm_mon < 2) {
|
||||
year--;
|
||||
}
|
||||
|
||||
/* Find number of days since 1st March 1900 (in the Gregorian calendar). */
|
||||
|
||||
days = year * 365 + year / 4 - year / 100 + (year / 100 + 3) / 4;
|
||||
days += dayoffset[t->tm_mon] + t->tm_mday - 1;
|
||||
days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
|
||||
|
||||
secs = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
|
||||
|
||||
if (secs < 0) {
|
||||
return BAD_DATE;
|
||||
} /* must have overflowed */
|
||||
else {
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (t->tm_zone) {
|
||||
secs -= t->tm_gmtoff;
|
||||
}
|
||||
#endif
|
||||
return secs;
|
||||
} /* must be a valid time */
|
||||
}
|
||||
|
||||
static const int SECSPERMIN = 60;
|
||||
static const int SECSPERHOUR = 60 * SECSPERMIN;
|
||||
static const int SECSPERDAY = 24 * SECSPERHOUR;
|
||||
static const int YEAR_BASE = 1900;
|
||||
static const int EPOCH_WDAY = 4;
|
||||
static const int DAYSPERWEEK = 7;
|
||||
static const int EPOCH_YEAR = 1970;
|
||||
|
||||
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
|
||||
|
||||
static const int year_lengths[2] = {
|
||||
365,
|
||||
366
|
||||
};
|
||||
|
||||
const unsigned short int mon_yday[2][13] = {
|
||||
/* Normal years. */
|
||||
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
|
||||
/* Leap years. */
|
||||
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
|
||||
};
|
||||
|
||||
static void secs2wday(const struct timeval &tv, struct tm *res)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
|
||||
/* base decision about std/dst time on current time */
|
||||
lcltime = tv.tv_sec;
|
||||
|
||||
days = ((long) lcltime) / SECSPERDAY;
|
||||
rem = ((long) lcltime) % SECSPERDAY;
|
||||
while (rem < 0) {
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
}
|
||||
|
||||
struct tm *secs2tm(time_t *tim_p, struct tm *res)
|
||||
{
|
||||
long days, rem;
|
||||
time_t lcltime;
|
||||
int y;
|
||||
int yleap;
|
||||
const unsigned short int *ip;
|
||||
|
||||
/* base decision about std/dst time on current time */
|
||||
lcltime = *tim_p;
|
||||
|
||||
days = ((long)lcltime) / SECSPERDAY;
|
||||
rem = ((long)lcltime) % SECSPERDAY;
|
||||
while (rem < 0)
|
||||
{
|
||||
rem += SECSPERDAY;
|
||||
--days;
|
||||
}
|
||||
|
||||
/* compute hour, min, and sec */
|
||||
res->tm_hour = (int) (rem / SECSPERHOUR);
|
||||
rem %= SECSPERHOUR;
|
||||
res->tm_min = (int) (rem / SECSPERMIN);
|
||||
res->tm_sec = (int) (rem % SECSPERMIN);
|
||||
|
||||
/* compute day of week */
|
||||
if ((res->tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
|
||||
res->tm_wday += DAYSPERWEEK;
|
||||
|
||||
/* compute year & day of year */
|
||||
y = EPOCH_YEAR;
|
||||
if (days >= 0)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
yleap = isleap(y);
|
||||
if (days < year_lengths[yleap])
|
||||
break;
|
||||
y++;
|
||||
days -= year_lengths[yleap];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
--y;
|
||||
yleap = isleap(y);
|
||||
days += year_lengths[yleap];
|
||||
} while (days < 0);
|
||||
}
|
||||
|
||||
res->tm_year = y - YEAR_BASE;
|
||||
res->tm_yday = days;
|
||||
ip = mon_yday[isleap(y)];
|
||||
for (y = 11; days < (long int) ip[y]; --y)
|
||||
continue;
|
||||
days -= ip[y];
|
||||
res->tm_mon = y;
|
||||
res->tm_mday = days + 1;
|
||||
|
||||
res->tm_isdst = 0;
|
||||
|
||||
return (res);
|
||||
}
|
||||
|
||||
bool next_format(const char * const fmt[], int &index, int &locked_index)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
if (locked_index == -1) {
|
||||
index += 1;
|
||||
if (fmt[index] == nullptr) {
|
||||
retval = false;
|
||||
}
|
||||
}
|
||||
else if (index == locked_index) {
|
||||
retval = false;
|
||||
}
|
||||
else {
|
||||
index = locked_index;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
const char *date_time_scanner::scan(const char *time_dest,
|
||||
size_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct exttm *tm_out,
|
||||
struct timeval &tv_out,
|
||||
bool convert_local)
|
||||
{
|
||||
int curr_time_fmt = -1;
|
||||
bool found = false;
|
||||
const char *retval = nullptr;
|
||||
|
||||
if (!time_fmt) {
|
||||
time_fmt = PTIMEC_FORMAT_STR;
|
||||
}
|
||||
|
||||
while (next_format(time_fmt,
|
||||
curr_time_fmt,
|
||||
this->dts_fmt_lock)) {
|
||||
*tm_out = this->dts_base_tm;
|
||||
tm_out->et_flags = 0;
|
||||
if (time_len > 1 &&
|
||||
time_dest[0] == '+' &&
|
||||
isdigit(time_dest[1])) {
|
||||
char time_cp[time_len + 1];
|
||||
int gmt_int, off;
|
||||
|
||||
retval = nullptr;
|
||||
memcpy(time_cp, time_dest, time_len);
|
||||
time_cp[time_len] = '\0';
|
||||
if (sscanf(time_cp, "+%d%n", &gmt_int, &off) == 1) {
|
||||
time_t gmt = gmt_int;
|
||||
|
||||
if (convert_local && this->dts_local_time) {
|
||||
localtime_r(&gmt, &tm_out->et_tm);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
#endif
|
||||
tm_out->et_tm.tm_isdst = 0;
|
||||
gmt = tm2sec(&tm_out->et_tm);
|
||||
}
|
||||
tv_out.tv_sec = gmt;
|
||||
tv_out.tv_usec = 0;
|
||||
tm_out->et_flags = ETF_DAY_SET|ETF_MONTH_SET|ETF_YEAR_SET|ETF_MACHINE_ORIENTED|ETF_EPOCH_TIME;
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = off;
|
||||
retval = time_dest + off;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (time_fmt == PTIMEC_FORMAT_STR) {
|
||||
ptime_func func = PTIMEC_FORMATS[curr_time_fmt].pf_func;
|
||||
off_t off = 0;
|
||||
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (!this->dts_keep_base_tz) {
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (func(tm_out, time_dest, off, time_len)) {
|
||||
retval = &time_dest[off];
|
||||
|
||||
if (tm_out->et_tm.tm_year < 70) {
|
||||
tm_out->et_tm.tm_year = 80;
|
||||
}
|
||||
if (convert_local &&
|
||||
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
||||
time_t gmt = tm2sec(&tm_out->et_tm);
|
||||
|
||||
this->to_localtime(gmt, *tm_out);
|
||||
}
|
||||
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
secs2wday(tv_out, &tm_out->et_tm);
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = retval - time_dest;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
off_t off = 0;
|
||||
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
if (!this->dts_keep_base_tz) {
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
}
|
||||
#endif
|
||||
if (ptime_fmt(time_fmt[curr_time_fmt], tm_out, time_dest, off, time_len) &&
|
||||
(time_dest[off] == '.' || time_dest[off] == ',' || off == (off_t)time_len)) {
|
||||
retval = &time_dest[off];
|
||||
if (tm_out->et_tm.tm_year < 70) {
|
||||
tm_out->et_tm.tm_year = 80;
|
||||
}
|
||||
if (convert_local &&
|
||||
(this->dts_local_time || tm_out->et_flags & ETF_EPOCH_TIME)) {
|
||||
time_t gmt = tm2sec(&tm_out->et_tm);
|
||||
|
||||
this->to_localtime(gmt, *tm_out);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out->et_tm.tm_zone = nullptr;
|
||||
#endif
|
||||
tm_out->et_tm.tm_isdst = 0;
|
||||
}
|
||||
|
||||
tv_out.tv_sec = tm2sec(&tm_out->et_tm);
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
secs2wday(tv_out, &tm_out->et_tm);
|
||||
|
||||
this->dts_fmt_lock = curr_time_fmt;
|
||||
this->dts_fmt_len = retval - time_dest;
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
retval = nullptr;
|
||||
}
|
||||
|
||||
if (retval != nullptr) {
|
||||
/* Try to pull out the milli/micro-second value. */
|
||||
if (retval[0] == '.' || retval[0] == ',') {
|
||||
off_t off = (retval - time_dest) + 1;
|
||||
|
||||
if (ptime_f(tm_out, time_dest, off, time_len)) {
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
this->dts_fmt_len += 7;
|
||||
retval += 7;
|
||||
}
|
||||
else if (ptime_L(tm_out, time_dest, off, time_len)) {
|
||||
tv_out.tv_usec = tm_out->et_nsec / 1000;
|
||||
this->dts_fmt_len += 4;
|
||||
retval += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t strtonum(T &num_out, const char *string, size_t len)
|
||||
{
|
||||
@ -592,35 +222,6 @@ bool read_file(const ghc::filesystem::path &filename, string &out)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t abbreviate_str(char *str, size_t len, size_t max_len)
|
||||
{
|
||||
size_t last_start = 1;
|
||||
|
||||
if (len < max_len) {
|
||||
return len;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < len; index++) {
|
||||
switch (str[index]) {
|
||||
case '.':
|
||||
case '-':
|
||||
case '/':
|
||||
case ':':
|
||||
memmove(&str[last_start], &str[index], len - index);
|
||||
len -= (index - last_start);
|
||||
index = last_start + 1;
|
||||
last_start = index + 1;
|
||||
|
||||
if (len < max_len) {
|
||||
return len;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
Result<std::pair<ghc::filesystem::path, int>, std::string>
|
||||
open_temp_file(const ghc::filesystem::path &pattern)
|
||||
{
|
||||
|
221
src/lnav_util.hh
221
src/lnav_util.hh
@ -56,67 +56,10 @@
|
||||
#include "fmt/format.h"
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
#undef rounddown
|
||||
|
||||
/**
|
||||
* Round down a number based on a given granularity.
|
||||
*
|
||||
* @param
|
||||
* @param step The granularity.
|
||||
*/
|
||||
template<typename Size, typename Step>
|
||||
inline int rounddown(Size size, Step step)
|
||||
{
|
||||
return size - (size % step);
|
||||
}
|
||||
|
||||
inline int rounddown_offset(size_t size, int step, int offset)
|
||||
{
|
||||
return size - ((size - offset) % step);
|
||||
}
|
||||
|
||||
inline size_t roundup_size(size_t size, int step)
|
||||
{
|
||||
size_t retval = size + step;
|
||||
|
||||
retval -= (retval % step);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
inline int32_t read_le32(const unsigned char *data)
|
||||
{
|
||||
return (
|
||||
(data[0] << 0) |
|
||||
(data[1] << 8) |
|
||||
(data[2] << 16) |
|
||||
(data[3] << 24));
|
||||
}
|
||||
|
||||
inline time_t day_num(time_t ti)
|
||||
{
|
||||
return ti / (24 * 60 * 60);
|
||||
}
|
||||
|
||||
inline time_t hour_num(time_t ti)
|
||||
{
|
||||
return ti / (60 * 60);
|
||||
}
|
||||
|
||||
std::string time_ago(time_t last_time, bool convert_local = false);
|
||||
|
||||
std::string precise_time_ago(const struct timeval &tv, bool convert_local = false);
|
||||
|
||||
typedef int64_t mstime_t;
|
||||
|
||||
inline mstime_t getmstime() {
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
return tv.tv_sec * 1000ULL + tv.tv_usec / 1000ULL;
|
||||
}
|
||||
|
||||
#if SIZEOF_OFF_T == 8
|
||||
#define FORMAT_OFF_T "%qd"
|
||||
#elif SIZEOF_OFF_T == 4
|
||||
@ -185,38 +128,6 @@ object_field_t<UnaryFunction, Member> object_field(UnaryFunction &func,
|
||||
|
||||
bool change_to_parent_dir();
|
||||
|
||||
void split_ws(const std::string &str, std::vector<std::string> &toks_out);
|
||||
|
||||
enum class file_format_t {
|
||||
FF_UNKNOWN,
|
||||
FF_SQLITE_DB,
|
||||
FF_ARCHIVE,
|
||||
};
|
||||
|
||||
file_format_t detect_file_format(const ghc::filesystem::path& filename);
|
||||
|
||||
namespace fmt {
|
||||
template<>
|
||||
struct formatter<file_format_t> : formatter<string_view> {
|
||||
template<typename FormatContext>
|
||||
auto format(file_format_t ff, FormatContext &ctx)
|
||||
{
|
||||
string_view name = "unknown";
|
||||
switch (ff) {
|
||||
case file_format_t::FF_SQLITE_DB:
|
||||
name = "SQLite Database";
|
||||
break;
|
||||
case file_format_t::FF_ARCHIVE:
|
||||
name = "Archive";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return formatter<string_view>::format(name, ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool next_format(const char * const fmt[], int &index, int &locked_index);
|
||||
|
||||
namespace std {
|
||||
@ -231,20 +142,10 @@ inline bool is_glob(const char *fn)
|
||||
strchr(fn, '[') != nullptr);
|
||||
};
|
||||
|
||||
bool is_url(const char *fn);
|
||||
|
||||
std::string build_path(const std::vector<ghc::filesystem::path> &paths);
|
||||
|
||||
bool read_file(const ghc::filesystem::path &path, std::string &out);
|
||||
|
||||
/**
|
||||
* Convert the time stored in a 'tm' struct into epoch time.
|
||||
*
|
||||
* @param t The 'tm' structure to convert to epoch time.
|
||||
* @return The given time in seconds since the epoch.
|
||||
*/
|
||||
time_t tm2sec(const struct tm *t);
|
||||
|
||||
inline
|
||||
time_t convert_log_time_to_local(time_t value) {
|
||||
struct tm tm;
|
||||
@ -257,126 +158,6 @@ time_t convert_log_time_to_local(time_t value) {
|
||||
return tm2sec(&tm);
|
||||
}
|
||||
|
||||
struct tm *secs2tm(time_t *tim_p, struct tm *res);
|
||||
|
||||
extern const char *std_time_fmt[];
|
||||
|
||||
struct date_time_scanner {
|
||||
date_time_scanner() : dts_keep_base_tz(false),
|
||||
dts_local_time(false),
|
||||
dts_local_offset_cache(0),
|
||||
dts_local_offset_valid(0),
|
||||
dts_local_offset_expiry(0) {
|
||||
this->clear();
|
||||
};
|
||||
|
||||
void clear() {
|
||||
this->dts_base_time = 0;
|
||||
memset(&this->dts_base_tm, 0, sizeof(this->dts_base_tm));
|
||||
this->dts_fmt_lock = -1;
|
||||
this->dts_fmt_len = -1;
|
||||
};
|
||||
|
||||
void unlock() {
|
||||
this->dts_fmt_lock = -1;
|
||||
this->dts_fmt_len = -1;
|
||||
}
|
||||
|
||||
void set_base_time(time_t base_time) {
|
||||
this->dts_base_time = base_time;
|
||||
localtime_r(&base_time, &this->dts_base_tm.et_tm);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a timestamp to local time.
|
||||
*
|
||||
* Calling localtime_r is slow since it wants to lookup the timezone on
|
||||
* every call, so we cache the result and only call it again if the
|
||||
* requested time falls outside of a fifteen minute range.
|
||||
*/
|
||||
void to_localtime(time_t t, struct exttm &tm_out) {
|
||||
if (t < (24 * 60 * 60)) {
|
||||
// Don't convert and risk going past the epoch.
|
||||
return;
|
||||
}
|
||||
|
||||
if (t < this->dts_local_offset_valid ||
|
||||
t >= this->dts_local_offset_expiry) {
|
||||
time_t new_gmt;
|
||||
|
||||
localtime_r(&t, &tm_out.et_tm);
|
||||
#ifdef HAVE_STRUCT_TM_TM_ZONE
|
||||
tm_out.et_tm.tm_zone = NULL;
|
||||
#endif
|
||||
tm_out.et_tm.tm_isdst = 0;
|
||||
|
||||
new_gmt = tm2sec(&tm_out.et_tm);
|
||||
this->dts_local_offset_cache = t - new_gmt;
|
||||
this->dts_local_offset_valid = t;
|
||||
this->dts_local_offset_expiry = t + (EXPIRE_TIME - 1);
|
||||
this->dts_local_offset_expiry -=
|
||||
this->dts_local_offset_expiry % EXPIRE_TIME;
|
||||
}
|
||||
else {
|
||||
time_t adjust_gmt = t - this->dts_local_offset_cache;
|
||||
gmtime_r(&adjust_gmt, &tm_out.et_tm);
|
||||
}
|
||||
};
|
||||
|
||||
bool dts_keep_base_tz;
|
||||
bool dts_local_time;
|
||||
time_t dts_base_time;
|
||||
struct exttm dts_base_tm;
|
||||
int dts_fmt_lock;
|
||||
int dts_fmt_len;
|
||||
time_t dts_local_offset_cache;
|
||||
time_t dts_local_offset_valid;
|
||||
time_t dts_local_offset_expiry;
|
||||
|
||||
static const int EXPIRE_TIME = 15 * 60;
|
||||
|
||||
const char *scan(const char *time_src,
|
||||
size_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct exttm *tm_out,
|
||||
struct timeval &tv_out,
|
||||
bool convert_local = true);
|
||||
|
||||
size_t ftime(char *dst, size_t len, const struct exttm &tm) {
|
||||
off_t off = 0;
|
||||
|
||||
PTIMEC_FORMATS[this->dts_fmt_lock].pf_ffunc(dst, off, len, tm);
|
||||
|
||||
return (size_t) off;
|
||||
};
|
||||
|
||||
bool convert_to_timeval(const char *time_src,
|
||||
ssize_t time_len,
|
||||
const char * const time_fmt[],
|
||||
struct timeval &tv_out) {
|
||||
struct exttm tm;
|
||||
|
||||
if (time_len == -1) {
|
||||
time_len = strlen(time_src);
|
||||
}
|
||||
if (this->scan(time_src, time_len, time_fmt, &tm, tv_out) != NULL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
bool convert_to_timeval(const std::string &time_src,
|
||||
struct timeval &tv_out) {
|
||||
struct exttm tm;
|
||||
|
||||
if (this->scan(time_src.c_str(), time_src.size(),
|
||||
NULL, &tm, tv_out) != NULL) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
size_t strtonum(T &num_out, const char *data, size_t len);
|
||||
|
||||
@ -427,8 +208,6 @@ inline void rusageadd(const struct rusage &left, const struct rusage &right, str
|
||||
diff_out.ru_nivcsw = left.ru_nivcsw + right.ru_nivcsw;
|
||||
}
|
||||
|
||||
size_t abbreviate_str(char *str, size_t len, size_t max_len);
|
||||
|
||||
inline int statp(const ghc::filesystem::path &path, struct stat *buf) {
|
||||
return stat(path.c_str(), buf);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "lnav.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "log_actions.hh"
|
||||
|
||||
using namespace std;
|
||||
@ -138,7 +139,7 @@ static string execute_action(log_data_helper &ldh,
|
||||
action.ad_cmdline[0].c_str());
|
||||
lnav_data.ld_active_files.fc_file_names[desc]
|
||||
.with_fd(pp->get_fd());
|
||||
lnav_data.ld_files_to_front.push_back({ desc, 0 });
|
||||
lnav_data.ld_files_to_front.emplace_back( desc, 0 );
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -103,7 +103,7 @@ public:
|
||||
this->ldh_file->read_full_message(ll, this->ldh_msg);
|
||||
format->annotate(this->ldh_line_index, this->ldh_msg, sa, this->ldh_line_values, false);
|
||||
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
body = find_string_attr_range(sa, &SA_BODY);
|
||||
if (body.lr_start == -1) {
|
||||
body.lr_start = this->ldh_msg.length();
|
||||
body.lr_end = this->ldh_msg.length();
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
this->ldt_parent_column_count = cols.size();
|
||||
lf->read_full_message(lf->begin() + cl_copy, line);
|
||||
format->annotate(cl_copy, line, sa, line_values, false);
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
body = find_string_attr_range(sa, &SA_BODY);
|
||||
if (body.lr_end == -1) {
|
||||
this->ldt_schema_id.clear();
|
||||
return;
|
||||
@ -166,7 +166,7 @@ public:
|
||||
sa,
|
||||
line_values,
|
||||
false);
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
body = find_string_attr_range(sa, &SA_BODY);
|
||||
if (body.lr_end == -1) {
|
||||
return false;
|
||||
}
|
||||
|
@ -38,11 +38,12 @@
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
#include "yajlpp/yajlpp_def.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "log_format.hh"
|
||||
#include "log_format_ext.hh"
|
||||
#include "log_vtab_impl.hh"
|
||||
#include "ptimec.hh"
|
||||
#include "log_search_table.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "lnav_util.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -894,7 +895,7 @@ void external_log_format::annotate(uint64_t line_number, shared_buffer_ref &line
|
||||
// A continued line still needs a body.
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = line.length();
|
||||
sa.emplace_back(lr, &textview_curses::SA_BODY);
|
||||
sa.emplace_back(lr, &SA_BODY);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -932,7 +933,7 @@ void external_log_format::annotate(uint64_t line_number, shared_buffer_ref &line
|
||||
lr.lr_start = line.length();
|
||||
lr.lr_end = line.length();
|
||||
}
|
||||
sa.emplace_back(lr, &textview_curses::SA_BODY);
|
||||
sa.emplace_back(lr, &SA_BODY);
|
||||
|
||||
for (size_t lpc = 0; lpc < pat.p_value_by_index.size(); lpc++) {
|
||||
const indexed_value_def &ivd = pat.p_value_by_index[lpc];
|
||||
@ -1273,7 +1274,7 @@ void external_log_format::get_subline(const logline &ll, shared_buffer_ref &sbr,
|
||||
}
|
||||
else if (lv_iter->lv_name == this->elf_body_field) {
|
||||
this->jlf_line_attrs.emplace_back(
|
||||
lr, &textview_curses::SA_BODY);
|
||||
lr, &SA_BODY);
|
||||
}
|
||||
else if (lv_iter->lv_name == this->elf_opid_field) {
|
||||
this->jlf_line_attrs.emplace_back(
|
||||
@ -2053,7 +2054,7 @@ public:
|
||||
|
||||
this->vi_attrs.clear();
|
||||
format->annotate(cl, line, this->vi_attrs, values, false);
|
||||
this->elt_container_body = find_string_attr_range(this->vi_attrs, &textview_curses::SA_BODY);
|
||||
this->elt_container_body = find_string_attr_range(this->vi_attrs, &SA_BODY);
|
||||
if (!this->elt_container_body.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
@ -2118,6 +2119,32 @@ log_vtab_impl *external_log_format::get_vtab_impl() const
|
||||
return new external_log_table(*this);
|
||||
}
|
||||
|
||||
std::unique_ptr<log_format> external_log_format::specialized(int fmt_lock)
|
||||
{
|
||||
auto retval = std::make_unique<external_log_format>(*this);
|
||||
|
||||
retval->lf_specialized = true;
|
||||
this->lf_pattern_locks.clear();
|
||||
if (fmt_lock != -1) {
|
||||
retval->lf_pattern_locks.emplace_back(0, fmt_lock);
|
||||
}
|
||||
|
||||
if (this->elf_type == ELF_TYPE_JSON) {
|
||||
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(this->elf_name.to_string());
|
||||
this->jlf_yajl_handle.reset(yajl_alloc(
|
||||
&this->jlf_parse_context->ypc_callbacks,
|
||||
nullptr,
|
||||
this->jlf_parse_context.get()));
|
||||
yajl_config(this->jlf_yajl_handle.in(), yajl_dont_validate_strings, 1);
|
||||
this->jlf_cached_line.reserve(16 * 1024);
|
||||
}
|
||||
|
||||
this->lf_value_stats.clear();
|
||||
this->lf_value_stats.resize(this->elf_numeric_value_defs.size());
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int log_format::pattern_index_for_line(uint64_t line_number) const
|
||||
{
|
||||
auto iter = lower_bound(this->lf_pattern_locks.cbegin(),
|
||||
@ -2135,6 +2162,12 @@ int log_format::pattern_index_for_line(uint64_t line_number) const
|
||||
return iter->pfl_pat_index;
|
||||
}
|
||||
|
||||
std::string log_format::get_pattern_name(uint64_t line_number) const
|
||||
{
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
return fmt::format("builtin ({})", pat_index);
|
||||
}
|
||||
|
||||
log_format::pattern_for_lines::pattern_for_lines(
|
||||
uint32_t pfl_line, uint32_t pfl_pat_index) :
|
||||
pfl_line(pfl_line), pfl_pat_index(pfl_pat_index)
|
||||
|
@ -53,9 +53,8 @@
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "base/date_time_scanner.hh"
|
||||
#include "byte_array.hh"
|
||||
#include "view_curses.hh"
|
||||
#include "base/intern_string.hh"
|
||||
#include "shared_buffer.hh"
|
||||
#include "highlighter.hh"
|
||||
@ -310,14 +309,7 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
log_format() : lf_mod_index(0),
|
||||
lf_timestamp_field(intern_string::lookup("timestamp", -1)),
|
||||
lf_timestamp_flags(0),
|
||||
lf_is_self_describing(false),
|
||||
lf_time_ordered(true) {
|
||||
};
|
||||
|
||||
virtual ~log_format() { };
|
||||
virtual ~log_format() = default;
|
||||
|
||||
virtual void clear()
|
||||
{
|
||||
@ -420,12 +412,7 @@ public:
|
||||
void check_for_new_year(std::vector<logline> &dst, exttm log_tv,
|
||||
timeval timeval1);
|
||||
|
||||
virtual std::string get_pattern_name(uint64_t line_number) const {
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
char name[32];
|
||||
snprintf(name, sizeof(name), "builtin (%d)", pat_index);
|
||||
return name;
|
||||
};
|
||||
virtual std::string get_pattern_name(uint64_t line_number) const;
|
||||
|
||||
virtual std::string get_pattern_regex(uint64_t line_number) const {
|
||||
return "";
|
||||
@ -448,17 +435,17 @@ public:
|
||||
|
||||
int pattern_index_for_line(uint64_t line_number) const;
|
||||
|
||||
uint8_t lf_mod_index;
|
||||
uint8_t lf_mod_index{0};
|
||||
date_time_scanner lf_date_time;
|
||||
std::vector<pattern_for_lines> lf_pattern_locks;
|
||||
intern_string_t lf_timestamp_field;
|
||||
intern_string_t lf_timestamp_field{intern_string::lookup("timestamp", -1)};
|
||||
std::vector<const char *> lf_timestamp_format;
|
||||
unsigned int lf_timestamp_flags;
|
||||
unsigned int lf_timestamp_flags{0};
|
||||
std::map<std::string, action_def> lf_action_defs;
|
||||
std::vector<logline_value_stats> lf_value_stats;
|
||||
std::vector<highlighter> lf_highlighters;
|
||||
bool lf_is_self_describing;
|
||||
bool lf_time_ordered;
|
||||
bool lf_is_self_describing{false};
|
||||
bool lf_time_ordered{true};
|
||||
bool lf_specialized{false};
|
||||
protected:
|
||||
static std::vector<log_format *> lf_root_formats;
|
||||
@ -487,430 +474,4 @@ protected:
|
||||
...);
|
||||
};
|
||||
|
||||
class module_format;
|
||||
|
||||
class external_log_format : public log_format {
|
||||
|
||||
public:
|
||||
struct sample {
|
||||
sample() : s_level(LEVEL_UNKNOWN) {};
|
||||
|
||||
std::string s_line;
|
||||
log_level_t s_level;
|
||||
};
|
||||
|
||||
struct value_def {
|
||||
intern_string_t vd_name;
|
||||
logline_value::kind_t vd_kind{logline_value::VALUE_UNKNOWN};
|
||||
std::string vd_collate;
|
||||
bool vd_identifier{false};
|
||||
bool vd_foreign_key{false};
|
||||
intern_string_t vd_unit_field;
|
||||
std::map<const intern_string_t, scaling_factor> vd_unit_scaling;
|
||||
int vd_column{-1};
|
||||
ssize_t vd_values_index{-1};
|
||||
bool vd_hidden{false};
|
||||
bool vd_user_hidden{false};
|
||||
bool vd_internal{false};
|
||||
std::vector<std::string> vd_action_list;
|
||||
std::string vd_rewriter;
|
||||
std::string vd_description;
|
||||
};
|
||||
|
||||
struct indexed_value_def {
|
||||
indexed_value_def(int index = -1,
|
||||
int unit_index = -1,
|
||||
std::shared_ptr<value_def> vd = nullptr)
|
||||
: ivd_index(index),
|
||||
ivd_unit_field_index(unit_index),
|
||||
ivd_value_def(std::move(vd)) {
|
||||
}
|
||||
|
||||
int ivd_index;
|
||||
int ivd_unit_field_index;
|
||||
std::shared_ptr<value_def> ivd_value_def;
|
||||
|
||||
bool operator<(const indexed_value_def &rhs) const {
|
||||
return this->ivd_index < rhs.ivd_index;
|
||||
}
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
std::string p_config_path;
|
||||
std::string p_string;
|
||||
pcrepp *p_pcre{nullptr};
|
||||
std::vector<indexed_value_def> p_value_by_index;
|
||||
std::vector<int> p_numeric_value_indexes;
|
||||
int p_timestamp_field_index{-1};
|
||||
int p_level_field_index{-1};
|
||||
int p_module_field_index{-1};
|
||||
int p_opid_field_index{-1};
|
||||
int p_body_field_index{-1};
|
||||
int p_timestamp_end{-1};
|
||||
bool p_module_format{false};
|
||||
};
|
||||
|
||||
struct level_pattern {
|
||||
std::string lp_regex;
|
||||
pcrepp *lp_pcre{nullptr};
|
||||
};
|
||||
|
||||
external_log_format(const intern_string_t name)
|
||||
: elf_file_pattern(".*"),
|
||||
elf_filename_pcre(NULL),
|
||||
elf_column_count(0),
|
||||
elf_timestamp_divisor(1.0),
|
||||
elf_level_field(intern_string::lookup("level", -1)),
|
||||
elf_body_field(intern_string::lookup("body", -1)),
|
||||
elf_multiline(true),
|
||||
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(yajl_free),
|
||||
elf_name(name) {
|
||||
this->jlf_line_offsets.reserve(128);
|
||||
};
|
||||
|
||||
const intern_string_t get_name() const {
|
||||
return this->elf_name;
|
||||
};
|
||||
|
||||
bool match_name(const std::string &filename) {
|
||||
pcre_context_static<10> pc;
|
||||
pcre_input pi(filename);
|
||||
|
||||
return this->elf_filename_pcre->match(pc, pi);
|
||||
};
|
||||
|
||||
scan_result_t scan(logfile &lf,
|
||||
std::vector<logline> &dst,
|
||||
const line_info &offset,
|
||||
shared_buffer_ref &sbr);
|
||||
|
||||
bool scan_for_partial(shared_buffer_ref &sbr, size_t &len_out);
|
||||
|
||||
void annotate(uint64_t line_number, shared_buffer_ref &line, string_attrs_t &sa,
|
||||
std::vector<logline_value> &values, bool annotate_module = true) const;
|
||||
|
||||
void rewrite(exec_context &ec,
|
||||
shared_buffer_ref &line,
|
||||
string_attrs_t &sa,
|
||||
std::string &value_out);
|
||||
|
||||
void build(std::vector<std::string> &errors);
|
||||
|
||||
void register_vtabs(log_vtab_manager *vtab_manager,
|
||||
std::vector<std::string> &errors);
|
||||
|
||||
bool match_samples(const std::vector<sample> &samples) const;
|
||||
|
||||
bool hide_field(const intern_string_t field_name, bool val) {
|
||||
auto vd_iter = this->elf_value_defs.find(field_name);
|
||||
|
||||
if (vd_iter == this->elf_value_defs.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vd_iter->second->vd_user_hidden = val;
|
||||
return true;
|
||||
};
|
||||
|
||||
std::unique_ptr<log_format> specialized(int fmt_lock) {
|
||||
auto retval = std::make_unique<external_log_format>(*this);
|
||||
|
||||
retval->lf_specialized = true;
|
||||
this->lf_pattern_locks.clear();
|
||||
if (fmt_lock != -1) {
|
||||
retval->lf_pattern_locks.emplace_back(0, fmt_lock);
|
||||
}
|
||||
|
||||
if (this->elf_type == ELF_TYPE_JSON) {
|
||||
this->jlf_parse_context = std::make_shared<yajlpp_parse_context>(this->elf_name.to_string());
|
||||
this->jlf_yajl_handle.reset(yajl_alloc(
|
||||
&this->jlf_parse_context->ypc_callbacks,
|
||||
nullptr,
|
||||
this->jlf_parse_context.get()));
|
||||
yajl_config(this->jlf_yajl_handle.in(), yajl_dont_validate_strings, 1);
|
||||
this->jlf_cached_line.reserve(16 * 1024);
|
||||
}
|
||||
|
||||
this->lf_value_stats.clear();
|
||||
this->lf_value_stats.resize(this->elf_numeric_value_defs.size());
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
const logline_value_stats *stats_for_value(const intern_string_t &name) const {
|
||||
const logline_value_stats *retval = nullptr;
|
||||
|
||||
for (size_t lpc = 0; lpc < this->elf_numeric_value_defs.size(); lpc++) {
|
||||
value_def &vd = *this->elf_numeric_value_defs[lpc];
|
||||
|
||||
if (vd.vd_name == name) {
|
||||
retval = &this->lf_value_stats[lpc];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
void get_subline(const logline &ll, shared_buffer_ref &sbr, bool full_message);
|
||||
|
||||
log_vtab_impl *get_vtab_impl() const;
|
||||
|
||||
const std::vector<std::string> *get_actions(const logline_value &lv) const {
|
||||
const std::vector<std::string> *retval = nullptr;
|
||||
|
||||
const auto iter = this->elf_value_defs.find(lv.lv_name);
|
||||
if (iter != this->elf_value_defs.end()) {
|
||||
retval = &iter->second->vd_action_list;
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::set<std::string> get_source_path() const {
|
||||
return this->elf_source_path;
|
||||
};
|
||||
|
||||
enum json_log_field {
|
||||
JLF_CONSTANT,
|
||||
JLF_VARIABLE
|
||||
};
|
||||
|
||||
struct json_format_element {
|
||||
enum class align_t {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
};
|
||||
|
||||
enum class overflow_t {
|
||||
ABBREV,
|
||||
TRUNCATE,
|
||||
DOTDOT,
|
||||
};
|
||||
|
||||
enum class transform_t {
|
||||
NONE,
|
||||
UPPERCASE,
|
||||
LOWERCASE,
|
||||
CAPITALIZE,
|
||||
};
|
||||
|
||||
json_format_element()
|
||||
: jfe_type(JLF_CONSTANT), jfe_default_value("-"), jfe_min_width(0),
|
||||
jfe_max_width(LLONG_MAX), jfe_align(align_t::LEFT),
|
||||
jfe_overflow(overflow_t::ABBREV),
|
||||
jfe_text_transform(transform_t::NONE)
|
||||
{ };
|
||||
|
||||
json_log_field jfe_type;
|
||||
intern_string_t jfe_value;
|
||||
std::string jfe_default_value;
|
||||
long long jfe_min_width;
|
||||
long long jfe_max_width;
|
||||
align_t jfe_align;
|
||||
overflow_t jfe_overflow;
|
||||
transform_t jfe_text_transform;
|
||||
std::string jfe_ts_format;
|
||||
};
|
||||
|
||||
struct json_field_cmp {
|
||||
json_field_cmp(json_log_field type,
|
||||
const intern_string_t name)
|
||||
: jfc_type(type), jfc_field_name(name) {
|
||||
};
|
||||
|
||||
bool operator()(const json_format_element &jfe) const {
|
||||
return (this->jfc_type == jfe.jfe_type &&
|
||||
this->jfc_field_name == jfe.jfe_value);
|
||||
};
|
||||
|
||||
json_log_field jfc_type;
|
||||
const intern_string_t jfc_field_name;
|
||||
};
|
||||
|
||||
struct highlighter_def {
|
||||
highlighter_def() : hd_underline(false), hd_blink(false) {
|
||||
}
|
||||
|
||||
std::string hd_pattern;
|
||||
std::string hd_color;
|
||||
std::string hd_background_color;
|
||||
bool hd_underline;
|
||||
bool hd_blink;
|
||||
};
|
||||
|
||||
long value_line_count(const intern_string_t ist,
|
||||
bool top_level,
|
||||
const unsigned char *str = nullptr,
|
||||
ssize_t len = -1) const {
|
||||
const auto iter = this->elf_value_defs.find(ist);
|
||||
long line_count = (str != NULL) ? std::count(&str[0], &str[len], '\n') + 1 : 1;
|
||||
|
||||
if (iter == this->elf_value_defs.end()) {
|
||||
return (this->jlf_hide_extra || !top_level) ? 0 : line_count;
|
||||
}
|
||||
|
||||
if (iter->second->vd_hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (std::find_if(this->jlf_line_format.begin(),
|
||||
this->jlf_line_format.end(),
|
||||
json_field_cmp(JLF_VARIABLE, ist)) !=
|
||||
this->jlf_line_format.end()) {
|
||||
return line_count - 1;
|
||||
}
|
||||
|
||||
return line_count;
|
||||
};
|
||||
|
||||
bool has_value_def(const intern_string_t ist) const {
|
||||
const auto iter = this->elf_value_defs.find(ist);
|
||||
|
||||
return iter != this->elf_value_defs.end();
|
||||
};
|
||||
|
||||
std::string get_pattern_name(uint64_t line_number) const {
|
||||
if (this->elf_type != ELF_TYPE_TEXT) {
|
||||
return "structured";
|
||||
}
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
return this->elf_pattern_order[pat_index]->p_config_path;
|
||||
}
|
||||
|
||||
std::string get_pattern_regex(uint64_t line_number) const {
|
||||
if (this->elf_type != ELF_TYPE_TEXT) {
|
||||
return "";
|
||||
}
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
return this->elf_pattern_order[pat_index]->p_string;
|
||||
}
|
||||
|
||||
log_level_t convert_level(const pcre_input &pi, pcre_context::capture_t *level_cap) const {
|
||||
log_level_t retval = LEVEL_INFO;
|
||||
|
||||
if (level_cap != nullptr && level_cap->is_valid()) {
|
||||
pcre_context_static<128> pc_level;
|
||||
pcre_input pi_level(pi.get_substr_start(level_cap),
|
||||
0,
|
||||
level_cap->length());
|
||||
|
||||
if (this->elf_level_patterns.empty()) {
|
||||
retval = string2level(pi_level.get_string(), level_cap->length());
|
||||
} else {
|
||||
for (const auto &elf_level_pattern : this->elf_level_patterns) {
|
||||
if (elf_level_pattern.second.lp_pcre->match(pc_level, pi_level)) {
|
||||
retval = elf_level_pattern.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
typedef std::map<intern_string_t, module_format> mod_map_t;
|
||||
static mod_map_t MODULE_FORMATS;
|
||||
static std::vector<external_log_format *> GRAPH_ORDERED_FORMATS;
|
||||
|
||||
std::set<std::string> elf_source_path;
|
||||
std::list<intern_string_t> elf_collision;
|
||||
std::string elf_file_pattern;
|
||||
pcrepp *elf_filename_pcre;
|
||||
std::map<std::string, std::shared_ptr<pattern>> elf_patterns;
|
||||
std::vector<std::shared_ptr<pattern>> elf_pattern_order;
|
||||
std::vector<sample> elf_samples;
|
||||
std::unordered_map<const intern_string_t, std::shared_ptr<value_def>>
|
||||
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;
|
||||
intern_string_t elf_level_field;
|
||||
intern_string_t elf_body_field;
|
||||
intern_string_t elf_module_id_field;
|
||||
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_multiline;
|
||||
bool elf_container;
|
||||
bool elf_has_module_format;
|
||||
bool elf_builtin_format;
|
||||
std::vector<std::pair<intern_string_t, std::string> > elf_search_tables;
|
||||
std::map<const intern_string_t, highlighter_def> elf_highlighter_patterns;
|
||||
|
||||
enum elf_type_t {
|
||||
ELF_TYPE_TEXT,
|
||||
ELF_TYPE_JSON,
|
||||
ELF_TYPE_CSV,
|
||||
};
|
||||
|
||||
elf_type_t elf_type;
|
||||
|
||||
void json_append_to_cache(const char *value, ssize_t len) {
|
||||
size_t old_size = this->jlf_cached_line.size();
|
||||
if (len == -1) {
|
||||
len = strlen(value);
|
||||
}
|
||||
this->jlf_cached_line.resize(old_size + len);
|
||||
memcpy(&(this->jlf_cached_line[old_size]), value, len);
|
||||
};
|
||||
|
||||
void json_append_to_cache(ssize_t len) {
|
||||
size_t old_size = this->jlf_cached_line.size();
|
||||
this->jlf_cached_line.resize(old_size + len);
|
||||
memset(&this->jlf_cached_line[old_size], ' ', len);
|
||||
};
|
||||
|
||||
void json_append(const json_format_element &jfe, const char *value, ssize_t len) {
|
||||
if (len == -1) {
|
||||
len = strlen(value);
|
||||
}
|
||||
if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
|
||||
if (len < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||
}
|
||||
}
|
||||
this->json_append_to_cache(value, len);
|
||||
if (jfe.jfe_align == json_format_element::align_t::LEFT) {
|
||||
if (len < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool jlf_hide_extra;
|
||||
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;
|
||||
bool jlf_cached_full{false};
|
||||
std::vector<off_t> jlf_line_offsets;
|
||||
shared_buffer jlf_share_manager;
|
||||
std::vector<char> jlf_cached_line;
|
||||
string_attrs_t jlf_line_attrs;
|
||||
std::shared_ptr<yajlpp_parse_context> jlf_parse_context;
|
||||
auto_mem<yajl_handle_t> jlf_yajl_handle;
|
||||
private:
|
||||
const intern_string_t elf_name;
|
||||
|
||||
static uint8_t module_scan(const pcre_input &pi,
|
||||
pcre_context::capture_t *body_cap,
|
||||
const intern_string_t &mod_name);
|
||||
};
|
||||
|
||||
class module_format {
|
||||
|
||||
public:
|
||||
external_log_format *mf_mod_format{nullptr};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
440
src/log_format_ext.hh
Normal file
440
src/log_format_ext.hh
Normal file
@ -0,0 +1,440 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file log_format_ext.hh
|
||||
*/
|
||||
|
||||
#ifndef lnav_log_format_ext_hh
|
||||
#define lnav_log_format_ext_hh
|
||||
|
||||
#include "log_format.hh"
|
||||
|
||||
class module_format;
|
||||
|
||||
class external_log_format : public log_format {
|
||||
|
||||
public:
|
||||
struct sample {
|
||||
sample() : s_level(LEVEL_UNKNOWN) {};
|
||||
|
||||
std::string s_line;
|
||||
log_level_t s_level;
|
||||
};
|
||||
|
||||
struct value_def {
|
||||
intern_string_t vd_name;
|
||||
logline_value::kind_t vd_kind{logline_value::VALUE_UNKNOWN};
|
||||
std::string vd_collate;
|
||||
bool vd_identifier{false};
|
||||
bool vd_foreign_key{false};
|
||||
intern_string_t vd_unit_field;
|
||||
std::map<const intern_string_t, scaling_factor> vd_unit_scaling;
|
||||
int vd_column{-1};
|
||||
ssize_t vd_values_index{-1};
|
||||
bool vd_hidden{false};
|
||||
bool vd_user_hidden{false};
|
||||
bool vd_internal{false};
|
||||
std::vector<std::string> vd_action_list;
|
||||
std::string vd_rewriter;
|
||||
std::string vd_description;
|
||||
};
|
||||
|
||||
struct indexed_value_def {
|
||||
indexed_value_def(int index = -1,
|
||||
int unit_index = -1,
|
||||
std::shared_ptr<value_def> vd = nullptr)
|
||||
: ivd_index(index),
|
||||
ivd_unit_field_index(unit_index),
|
||||
ivd_value_def(std::move(vd)) {
|
||||
}
|
||||
|
||||
int ivd_index;
|
||||
int ivd_unit_field_index;
|
||||
std::shared_ptr<value_def> ivd_value_def;
|
||||
|
||||
bool operator<(const indexed_value_def &rhs) const {
|
||||
return this->ivd_index < rhs.ivd_index;
|
||||
}
|
||||
};
|
||||
|
||||
struct pattern {
|
||||
std::string p_config_path;
|
||||
std::string p_string;
|
||||
pcrepp *p_pcre{nullptr};
|
||||
std::vector<indexed_value_def> p_value_by_index;
|
||||
std::vector<int> p_numeric_value_indexes;
|
||||
int p_timestamp_field_index{-1};
|
||||
int p_level_field_index{-1};
|
||||
int p_module_field_index{-1};
|
||||
int p_opid_field_index{-1};
|
||||
int p_body_field_index{-1};
|
||||
int p_timestamp_end{-1};
|
||||
bool p_module_format{false};
|
||||
};
|
||||
|
||||
struct level_pattern {
|
||||
std::string lp_regex;
|
||||
pcrepp *lp_pcre{nullptr};
|
||||
};
|
||||
|
||||
external_log_format(const intern_string_t name)
|
||||
: elf_file_pattern(".*"),
|
||||
elf_filename_pcre(NULL),
|
||||
elf_column_count(0),
|
||||
elf_timestamp_divisor(1.0),
|
||||
elf_level_field(intern_string::lookup("level", -1)),
|
||||
elf_body_field(intern_string::lookup("body", -1)),
|
||||
elf_multiline(true),
|
||||
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(yajl_free),
|
||||
elf_name(name) {
|
||||
this->jlf_line_offsets.reserve(128);
|
||||
};
|
||||
|
||||
const intern_string_t get_name() const {
|
||||
return this->elf_name;
|
||||
};
|
||||
|
||||
bool match_name(const std::string &filename) {
|
||||
pcre_context_static<10> pc;
|
||||
pcre_input pi(filename);
|
||||
|
||||
return this->elf_filename_pcre->match(pc, pi);
|
||||
};
|
||||
|
||||
scan_result_t scan(logfile &lf,
|
||||
std::vector<logline> &dst,
|
||||
const line_info &offset,
|
||||
shared_buffer_ref &sbr);
|
||||
|
||||
bool scan_for_partial(shared_buffer_ref &sbr, size_t &len_out);
|
||||
|
||||
void annotate(uint64_t line_number, shared_buffer_ref &line, string_attrs_t &sa,
|
||||
std::vector<logline_value> &values, bool annotate_module = true) const;
|
||||
|
||||
void rewrite(exec_context &ec,
|
||||
shared_buffer_ref &line,
|
||||
string_attrs_t &sa,
|
||||
std::string &value_out);
|
||||
|
||||
void build(std::vector<std::string> &errors);
|
||||
|
||||
void register_vtabs(log_vtab_manager *vtab_manager,
|
||||
std::vector<std::string> &errors);
|
||||
|
||||
bool match_samples(const std::vector<sample> &samples) const;
|
||||
|
||||
bool hide_field(const intern_string_t field_name, bool val) {
|
||||
auto vd_iter = this->elf_value_defs.find(field_name);
|
||||
|
||||
if (vd_iter == this->elf_value_defs.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vd_iter->second->vd_user_hidden = val;
|
||||
return true;
|
||||
};
|
||||
|
||||
std::unique_ptr<log_format> specialized(int fmt_lock);
|
||||
|
||||
const logline_value_stats *stats_for_value(const intern_string_t &name) const {
|
||||
const logline_value_stats *retval = nullptr;
|
||||
|
||||
for (size_t lpc = 0; lpc < this->elf_numeric_value_defs.size(); lpc++) {
|
||||
value_def &vd = *this->elf_numeric_value_defs[lpc];
|
||||
|
||||
if (vd.vd_name == name) {
|
||||
retval = &this->lf_value_stats[lpc];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
void get_subline(const logline &ll, shared_buffer_ref &sbr, bool full_message);
|
||||
|
||||
log_vtab_impl *get_vtab_impl() const;
|
||||
|
||||
const std::vector<std::string> *get_actions(const logline_value &lv) const {
|
||||
const std::vector<std::string> *retval = nullptr;
|
||||
|
||||
const auto iter = this->elf_value_defs.find(lv.lv_name);
|
||||
if (iter != this->elf_value_defs.end()) {
|
||||
retval = &iter->second->vd_action_list;
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
std::set<std::string> get_source_path() const {
|
||||
return this->elf_source_path;
|
||||
};
|
||||
|
||||
enum json_log_field {
|
||||
JLF_CONSTANT,
|
||||
JLF_VARIABLE
|
||||
};
|
||||
|
||||
struct json_format_element {
|
||||
enum class align_t {
|
||||
LEFT,
|
||||
RIGHT,
|
||||
};
|
||||
|
||||
enum class overflow_t {
|
||||
ABBREV,
|
||||
TRUNCATE,
|
||||
DOTDOT,
|
||||
};
|
||||
|
||||
enum class transform_t {
|
||||
NONE,
|
||||
UPPERCASE,
|
||||
LOWERCASE,
|
||||
CAPITALIZE,
|
||||
};
|
||||
|
||||
json_format_element()
|
||||
: jfe_type(JLF_CONSTANT), jfe_default_value("-"), jfe_min_width(0),
|
||||
jfe_max_width(LLONG_MAX), jfe_align(align_t::LEFT),
|
||||
jfe_overflow(overflow_t::ABBREV),
|
||||
jfe_text_transform(transform_t::NONE)
|
||||
{ };
|
||||
|
||||
json_log_field jfe_type;
|
||||
intern_string_t jfe_value;
|
||||
std::string jfe_default_value;
|
||||
long long jfe_min_width;
|
||||
long long jfe_max_width;
|
||||
align_t jfe_align;
|
||||
overflow_t jfe_overflow;
|
||||
transform_t jfe_text_transform;
|
||||
std::string jfe_ts_format;
|
||||
};
|
||||
|
||||
struct json_field_cmp {
|
||||
json_field_cmp(json_log_field type,
|
||||
const intern_string_t name)
|
||||
: jfc_type(type), jfc_field_name(name) {
|
||||
};
|
||||
|
||||
bool operator()(const json_format_element &jfe) const {
|
||||
return (this->jfc_type == jfe.jfe_type &&
|
||||
this->jfc_field_name == jfe.jfe_value);
|
||||
};
|
||||
|
||||
json_log_field jfc_type;
|
||||
const intern_string_t jfc_field_name;
|
||||
};
|
||||
|
||||
struct highlighter_def {
|
||||
highlighter_def() : hd_underline(false), hd_blink(false) {
|
||||
}
|
||||
|
||||
std::string hd_pattern;
|
||||
std::string hd_color;
|
||||
std::string hd_background_color;
|
||||
bool hd_underline;
|
||||
bool hd_blink;
|
||||
};
|
||||
|
||||
long value_line_count(const intern_string_t ist,
|
||||
bool top_level,
|
||||
const unsigned char *str = nullptr,
|
||||
ssize_t len = -1) const {
|
||||
const auto iter = this->elf_value_defs.find(ist);
|
||||
long line_count = (str != NULL) ? std::count(&str[0], &str[len], '\n') + 1 : 1;
|
||||
|
||||
if (iter == this->elf_value_defs.end()) {
|
||||
return (this->jlf_hide_extra || !top_level) ? 0 : line_count;
|
||||
}
|
||||
|
||||
if (iter->second->vd_hidden) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (std::find_if(this->jlf_line_format.begin(),
|
||||
this->jlf_line_format.end(),
|
||||
json_field_cmp(JLF_VARIABLE, ist)) !=
|
||||
this->jlf_line_format.end()) {
|
||||
return line_count - 1;
|
||||
}
|
||||
|
||||
return line_count;
|
||||
};
|
||||
|
||||
bool has_value_def(const intern_string_t ist) const {
|
||||
const auto iter = this->elf_value_defs.find(ist);
|
||||
|
||||
return iter != this->elf_value_defs.end();
|
||||
};
|
||||
|
||||
std::string get_pattern_name(uint64_t line_number) const {
|
||||
if (this->elf_type != ELF_TYPE_TEXT) {
|
||||
return "structured";
|
||||
}
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
return this->elf_pattern_order[pat_index]->p_config_path;
|
||||
}
|
||||
|
||||
std::string get_pattern_regex(uint64_t line_number) const {
|
||||
if (this->elf_type != ELF_TYPE_TEXT) {
|
||||
return "";
|
||||
}
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
return this->elf_pattern_order[pat_index]->p_string;
|
||||
}
|
||||
|
||||
log_level_t convert_level(const pcre_input &pi, pcre_context::capture_t *level_cap) const {
|
||||
log_level_t retval = LEVEL_INFO;
|
||||
|
||||
if (level_cap != nullptr && level_cap->is_valid()) {
|
||||
pcre_context_static<128> pc_level;
|
||||
pcre_input pi_level(pi.get_substr_start(level_cap),
|
||||
0,
|
||||
level_cap->length());
|
||||
|
||||
if (this->elf_level_patterns.empty()) {
|
||||
retval = string2level(pi_level.get_string(), level_cap->length());
|
||||
} else {
|
||||
for (const auto &elf_level_pattern : this->elf_level_patterns) {
|
||||
if (elf_level_pattern.second.lp_pcre->match(pc_level, pi_level)) {
|
||||
retval = elf_level_pattern.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
typedef std::map<intern_string_t, module_format> mod_map_t;
|
||||
static mod_map_t MODULE_FORMATS;
|
||||
static std::vector<external_log_format *> GRAPH_ORDERED_FORMATS;
|
||||
|
||||
std::set<std::string> elf_source_path;
|
||||
std::list<intern_string_t> elf_collision;
|
||||
std::string elf_file_pattern;
|
||||
pcrepp *elf_filename_pcre;
|
||||
std::map<std::string, std::shared_ptr<pattern>> elf_patterns;
|
||||
std::vector<std::shared_ptr<pattern>> elf_pattern_order;
|
||||
std::vector<sample> elf_samples;
|
||||
std::unordered_map<const intern_string_t, std::shared_ptr<value_def>>
|
||||
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;
|
||||
intern_string_t elf_level_field;
|
||||
intern_string_t elf_body_field;
|
||||
intern_string_t elf_module_id_field;
|
||||
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_multiline;
|
||||
bool elf_container;
|
||||
bool elf_has_module_format;
|
||||
bool elf_builtin_format;
|
||||
std::vector<std::pair<intern_string_t, std::string> > elf_search_tables;
|
||||
std::map<const intern_string_t, highlighter_def> elf_highlighter_patterns;
|
||||
|
||||
enum elf_type_t {
|
||||
ELF_TYPE_TEXT,
|
||||
ELF_TYPE_JSON,
|
||||
ELF_TYPE_CSV,
|
||||
};
|
||||
|
||||
elf_type_t elf_type;
|
||||
|
||||
void json_append_to_cache(const char *value, ssize_t len) {
|
||||
size_t old_size = this->jlf_cached_line.size();
|
||||
if (len == -1) {
|
||||
len = strlen(value);
|
||||
}
|
||||
this->jlf_cached_line.resize(old_size + len);
|
||||
memcpy(&(this->jlf_cached_line[old_size]), value, len);
|
||||
};
|
||||
|
||||
void json_append_to_cache(ssize_t len) {
|
||||
size_t old_size = this->jlf_cached_line.size();
|
||||
this->jlf_cached_line.resize(old_size + len);
|
||||
memset(&this->jlf_cached_line[old_size], ' ', len);
|
||||
};
|
||||
|
||||
void json_append(const json_format_element &jfe, const char *value, ssize_t len) {
|
||||
if (len == -1) {
|
||||
len = strlen(value);
|
||||
}
|
||||
if (jfe.jfe_align == json_format_element::align_t::RIGHT) {
|
||||
if (len < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||
}
|
||||
}
|
||||
this->json_append_to_cache(value, len);
|
||||
if (jfe.jfe_align == json_format_element::align_t::LEFT) {
|
||||
if (len < jfe.jfe_min_width) {
|
||||
this->json_append_to_cache(jfe.jfe_min_width - len);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool jlf_hide_extra;
|
||||
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;
|
||||
bool jlf_cached_full{false};
|
||||
std::vector<off_t> jlf_line_offsets;
|
||||
shared_buffer jlf_share_manager;
|
||||
std::vector<char> jlf_cached_line;
|
||||
string_attrs_t jlf_line_attrs;
|
||||
std::shared_ptr<yajlpp_parse_context> jlf_parse_context;
|
||||
auto_mem<yajl_handle_t> jlf_yajl_handle;
|
||||
private:
|
||||
const intern_string_t elf_name;
|
||||
|
||||
static uint8_t module_scan(const pcre_input &pi,
|
||||
pcre_context::capture_t *body_cap,
|
||||
const intern_string_t &mod_name);
|
||||
};
|
||||
|
||||
class module_format {
|
||||
|
||||
public:
|
||||
external_log_format *mf_mod_format{nullptr};
|
||||
};
|
||||
|
||||
#endif
|
@ -207,7 +207,7 @@ class generic_log_format : public log_format {
|
||||
|
||||
lr.lr_start = prefix_len;
|
||||
lr.lr_end = line.length();
|
||||
sa.emplace_back(lr, &textview_curses::SA_BODY);
|
||||
sa.emplace_back(lr, &SA_BODY);
|
||||
};
|
||||
|
||||
unique_ptr<log_format> specialized(int fmt_lock)
|
||||
|
@ -46,9 +46,10 @@
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
#include "yajlpp/yajlpp_def.hh"
|
||||
#include "lnav_config.hh"
|
||||
#include "log_format.hh"
|
||||
#include "log_format_ext.hh"
|
||||
#include "auto_fd.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "builtin-scripts.h"
|
||||
#include "builtin-sh-scripts.h"
|
||||
#include "default-formats.h"
|
||||
|
@ -550,7 +550,7 @@ static int vt_column(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int col)
|
||||
struct line_range body_range;
|
||||
|
||||
body_range = find_string_attr_range(
|
||||
vt->vi->vi_attrs, &textview_curses::SA_BODY);
|
||||
vt->vi->vi_attrs, &SA_BODY);
|
||||
if (!body_range.is_valid()) {
|
||||
sqlite3_result_null(ctx);
|
||||
}
|
||||
|
@ -36,9 +36,10 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "textview_curses.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
|
||||
class textview_curses;
|
||||
|
||||
enum {
|
||||
VT_COL_LINE_NUMBER,
|
||||
VT_COL_PARTITION,
|
||||
|
@ -210,7 +210,7 @@ void logfile_sub_source::text_value_for_line(textview_curses &tc,
|
||||
if (this->lss_token_line->is_continued()) {
|
||||
this->lss_token_attrs.emplace_back(
|
||||
line_range{0, (int) this->lss_token_value.length()},
|
||||
&textview_curses::SA_BODY);
|
||||
&SA_BODY);
|
||||
} else {
|
||||
format->annotate(line, sbr, this->lss_token_attrs,
|
||||
this->lss_token_values,
|
||||
@ -365,7 +365,7 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
|
||||
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = this->lss_token_value.length();
|
||||
value_out.emplace_back(lr, &textview_curses::SA_ORIGINAL_LINE);
|
||||
value_out.emplace_back(lr, &SA_ORIGINAL_LINE);
|
||||
|
||||
lr.lr_start = time_offset_end;
|
||||
lr.lr_end = -1;
|
||||
@ -381,7 +381,7 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
|
||||
|
||||
if (line_value.lv_hidden) {
|
||||
value_out.emplace_back(
|
||||
line_value.lv_origin, &textview_curses::SA_HIDDEN);
|
||||
line_value.lv_origin, &SA_HIDDEN);
|
||||
}
|
||||
|
||||
if (!line_value.lv_identifier || !line_value.lv_origin.is_valid()) {
|
||||
@ -491,7 +491,7 @@ void logfile_sub_source::text_attrs_for_line(textview_curses &lv,
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = -1;
|
||||
value_out.emplace_back(lr, &logline::L_FILE, this->lss_token_file.get());
|
||||
value_out.emplace_back(lr, &textview_curses::SA_FORMAT,
|
||||
value_out.emplace_back(lr, &SA_FORMAT,
|
||||
this->lss_token_file->get_format()->get_name());
|
||||
|
||||
{
|
||||
|
@ -43,46 +43,7 @@
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
struct tm *secs2tm(time_t *tim_p, struct tm *res);
|
||||
time_t tm2sec(const struct tm *t);
|
||||
|
||||
constexpr time_t MAX_TIME_T = 4000000000LL;
|
||||
|
||||
enum exttm_bits_t {
|
||||
ETB_YEAR_SET,
|
||||
ETB_MONTH_SET,
|
||||
ETB_DAY_SET,
|
||||
ETB_MACHINE_ORIENTED,
|
||||
ETB_EPOCH_TIME,
|
||||
};
|
||||
|
||||
enum exttm_flags_t {
|
||||
ETF_YEAR_SET = (1UL << ETB_YEAR_SET),
|
||||
ETF_MONTH_SET = (1UL << ETB_MONTH_SET),
|
||||
ETF_DAY_SET = (1UL << ETB_DAY_SET),
|
||||
ETF_MACHINE_ORIENTED = (1UL << ETB_MACHINE_ORIENTED),
|
||||
ETF_EPOCH_TIME = (1UL << ETB_EPOCH_TIME),
|
||||
};
|
||||
|
||||
struct exttm {
|
||||
struct tm et_tm;
|
||||
int32_t et_nsec;
|
||||
unsigned int et_flags;
|
||||
long et_gmtoff;
|
||||
|
||||
bool operator==(const exttm &other) const {
|
||||
return memcmp(this, &other, sizeof(exttm)) == 0;
|
||||
};
|
||||
|
||||
struct timeval to_timeval() const {
|
||||
struct timeval retval;
|
||||
|
||||
retval.tv_sec = tm2sec(&this->et_tm);
|
||||
retval.tv_usec = this->et_nsec * 1000;
|
||||
|
||||
return retval;
|
||||
};
|
||||
};
|
||||
#include "base/time_util.hh"
|
||||
|
||||
#define PTIME_CONSUME(amount, block) \
|
||||
if ((off_inout + amount) > len) { \
|
||||
|
@ -458,11 +458,14 @@ readline_context::readline_context(const std::string &name,
|
||||
}
|
||||
|
||||
readline_curses::readline_curses()
|
||||
: rc_active_context(-1),
|
||||
rc_child(-1),
|
||||
rc_value_expiration(0),
|
||||
rc_matches_remaining(0),
|
||||
rc_max_match_length(0)
|
||||
: rc_change(noop_func{}),
|
||||
rc_perform(noop_func{}),
|
||||
rc_alt_perform(noop_func{}),
|
||||
rc_timeout(noop_func{}),
|
||||
rc_abort(noop_func{}),
|
||||
rc_display_match(noop_func{}),
|
||||
rc_display_next(noop_func{}),
|
||||
rc_blur(noop_func{})
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -402,29 +402,29 @@ private:
|
||||
|
||||
friend class readline_context;
|
||||
|
||||
int rc_active_context;
|
||||
pid_t rc_child;
|
||||
int rc_active_context{-1};
|
||||
pid_t rc_child{-1};
|
||||
auto_fd rc_pty[2];
|
||||
auto_fd rc_command_pipe[2];
|
||||
std::map<int, readline_context *> rc_contexts;
|
||||
std::string rc_value;
|
||||
std::string rc_line_buffer;
|
||||
time_t rc_value_expiration;
|
||||
time_t rc_value_expiration{0};
|
||||
std::string rc_alt_value;
|
||||
int rc_match_start{0};
|
||||
int rc_matches_remaining;
|
||||
int rc_max_match_length;
|
||||
int rc_matches_remaining{0};
|
||||
int rc_max_match_length{0};
|
||||
int rc_match_index{0};
|
||||
std::vector<std::string> rc_matches;
|
||||
bool rc_is_alt_focus{false};
|
||||
|
||||
action rc_change{noop_func{}};
|
||||
action rc_perform{noop_func{}};
|
||||
action rc_alt_perform{noop_func{}};
|
||||
action rc_timeout{noop_func{}};
|
||||
action rc_abort{noop_func{}};
|
||||
action rc_display_match{noop_func{}};
|
||||
action rc_display_next{noop_func{}};
|
||||
action rc_blur{noop_func{}};
|
||||
action rc_change;
|
||||
action rc_perform;
|
||||
action rc_alt_perform;
|
||||
action rc_timeout;
|
||||
action rc_abort;
|
||||
action rc_display_match;
|
||||
action rc_display_next;
|
||||
action rc_blur;
|
||||
};
|
||||
#endif
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "lnav_config.hh"
|
||||
#include "session_data.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "log_format_ext.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@ -1390,17 +1391,13 @@ static void save_session_with_id(const std::string session_id)
|
||||
}
|
||||
}
|
||||
|
||||
textview_curses::highlight_map_t &hmap =
|
||||
lnav_data.ld_views[lpc].get_highlights();
|
||||
textview_curses::highlight_map_t::iterator hl_iter;
|
||||
auto &hmap = lnav_data.ld_views[lpc].get_highlights();
|
||||
|
||||
for (hl_iter = hmap.begin();
|
||||
hl_iter != hmap.end();
|
||||
++hl_iter) {
|
||||
if (hl_iter->first.first != highlight_source_t::INTERACTIVE) {
|
||||
for (auto & hl : hmap) {
|
||||
if (hl.first.first != highlight_source_t::INTERACTIVE) {
|
||||
continue;
|
||||
}
|
||||
cmd_array.gen("highlight " + hl_iter->first.second);
|
||||
cmd_array.gen("highlight " + hl.first.second);
|
||||
}
|
||||
|
||||
if (lpc == LNV_LOG) {
|
||||
|
393
src/spectro_source.cc
Normal file
393
src/spectro_source.cc
Normal file
@ -0,0 +1,393 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @file spectro_source.cc
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "base/math_util.hh"
|
||||
#include "spectro_source.hh"
|
||||
|
||||
bool spectrogram_source::list_input_handle_key(listview_curses &lv, int ch)
|
||||
{
|
||||
switch (ch) {
|
||||
case 'm': {
|
||||
if (this->ss_cursor_top < 0 ||
|
||||
(size_t) this->ss_cursor_top >= this->text_line_count() ||
|
||||
this->ss_cursor_column == -1 ||
|
||||
this->ss_value_source == nullptr) {
|
||||
alerter::singleton().chime();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
struct timeval begin_time = this->time_for_row(this->ss_cursor_top);
|
||||
struct timeval end_time = begin_time;
|
||||
|
||||
end_time.tv_sec += this->ss_granularity;
|
||||
double range_min, range_max, column_size;
|
||||
|
||||
column_size = (sb.sb_max_value_out - sb.sb_min_value_out) /
|
||||
(double) (width - 1);
|
||||
range_min = sb.sb_min_value_out + this->ss_cursor_column * column_size;
|
||||
range_max = range_min + column_size + column_size * 0.01;
|
||||
this->ss_value_source->spectro_mark((textview_curses &) lv,
|
||||
begin_time.tv_sec, end_time.tv_sec,
|
||||
range_min, range_max);
|
||||
this->invalidate();
|
||||
lv.reload_data();
|
||||
return true;
|
||||
}
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT: {
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
string_attrs_t sa;
|
||||
|
||||
this->ss_cursor_top = lv.get_top();
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
this->text_attrs_for_line((textview_curses &) lv, this->ss_cursor_top, sa);
|
||||
|
||||
if (sa.empty()) {
|
||||
this->ss_cursor_column = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
string_attrs_t::iterator current;
|
||||
|
||||
struct line_range lr(this->ss_cursor_column, this->ss_cursor_column + 1);
|
||||
|
||||
current = find_string_attr(sa, lr);
|
||||
|
||||
if (current != sa.end()) {
|
||||
if (ch == KEY_LEFT) {
|
||||
if (current == sa.begin()) {
|
||||
current = sa.end();
|
||||
}
|
||||
else {
|
||||
--current;
|
||||
}
|
||||
}
|
||||
else {
|
||||
++current;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == sa.end()) {
|
||||
if (ch == KEY_LEFT) {
|
||||
current = sa.end();
|
||||
--current;
|
||||
}
|
||||
else {
|
||||
current = sa.begin();
|
||||
}
|
||||
}
|
||||
this->ss_cursor_column = current->sa_range.lr_start;
|
||||
|
||||
lv.reload_data();
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
spectrogram_source::list_value_for_overlay(const listview_curses &lv, int y,
|
||||
int bottom, vis_line_t row,
|
||||
attr_line_t &value_out)
|
||||
{
|
||||
if (y != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string &line = value_out.get_string();
|
||||
char buf[128];
|
||||
vis_line_t height;
|
||||
unsigned long width;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
this->cache_bounds();
|
||||
|
||||
if (this->ss_cached_line_count == 0) {
|
||||
value_out.with_ansi_string(
|
||||
ANSI_ROLE("error: no log data"),
|
||||
view_colors::VCR_ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
|
||||
line = buf;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
ANSI_ROLE(" ") " 1-%'d "
|
||||
ANSI_ROLE(" ") " %'d-%'d "
|
||||
ANSI_ROLE(" ") " %'d+",
|
||||
view_colors::VCR_LOW_THRESHOLD,
|
||||
st.st_green_threshold - 1,
|
||||
view_colors::VCR_MED_THRESHOLD,
|
||||
st.st_green_threshold,
|
||||
st.st_yellow_threshold - 1,
|
||||
view_colors::VCR_HIGH_THRESHOLD,
|
||||
st.st_yellow_threshold);
|
||||
line.append(width / 2 - strlen(buf) / 3 - line.length(), ' ');
|
||||
line.append(buf);
|
||||
scrub_ansi_string(line, value_out.get_attrs());
|
||||
|
||||
snprintf(buf, sizeof(buf), "Max: %'.10lg", sb.sb_max_value_out);
|
||||
line.append(width - strlen(buf) - line.length() - 2, ' ');
|
||||
line.append(buf);
|
||||
|
||||
value_out.with_attr(string_attr(
|
||||
line_range(0, -1),
|
||||
&view_curses::VC_STYLE,
|
||||
A_UNDERLINE));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t spectrogram_source::text_line_count()
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->cache_bounds();
|
||||
|
||||
return this->ss_cached_line_count;
|
||||
}
|
||||
|
||||
size_t spectrogram_source::text_line_width(textview_curses &tc)
|
||||
{
|
||||
if (tc.get_window() == nullptr) {
|
||||
return 80;
|
||||
}
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
tc.get_dimensions(height, width);
|
||||
return width;
|
||||
}
|
||||
|
||||
struct timeval spectrogram_source::time_for_row(int row)
|
||||
{
|
||||
struct timeval retval { 0, 0 };
|
||||
|
||||
this->cache_bounds();
|
||||
retval.tv_sec =
|
||||
rounddown(this->ss_cached_bounds.sb_begin_time, this->ss_granularity) +
|
||||
row * this->ss_granularity;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int spectrogram_source::row_for_time(struct timeval time_bucket)
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t diff;
|
||||
int retval;
|
||||
|
||||
this->cache_bounds();
|
||||
if (time_bucket.tv_sec < this->ss_cached_bounds.sb_begin_time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff = time_bucket.tv_sec - this->ss_cached_bounds.sb_begin_time;
|
||||
retval = diff / this->ss_granularity;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void spectrogram_source::text_value_for_line(textview_curses &tc, int row,
|
||||
std::string &value_out,
|
||||
text_sub_source::line_flags_t flags)
|
||||
{
|
||||
spectrogram_row &s_row = this->load_row(tc, row);
|
||||
|
||||
struct timeval row_time;
|
||||
char tm_buffer[128];
|
||||
struct tm tm;
|
||||
|
||||
row_time = this->time_for_row(row);
|
||||
|
||||
gmtime_r(&row_time.tv_sec, &tm);
|
||||
strftime(tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S", &tm);
|
||||
|
||||
value_out = tm_buffer;
|
||||
value_out.resize(s_row.sr_width, ' ');
|
||||
|
||||
for (size_t lpc = 0; lpc <= s_row.sr_width; lpc++) {
|
||||
if (s_row.sr_values[lpc].rb_marks) {
|
||||
value_out[lpc] = 'x';
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ss_cursor_top == row && this->ss_cursor_column != -1) {
|
||||
if (value_out[this->ss_cursor_column] == 'x') {
|
||||
value_out[this->ss_cursor_column] = '*';
|
||||
}
|
||||
else {
|
||||
value_out[this->ss_cursor_column] = '+';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void spectrogram_source::text_attrs_for_line(textview_curses &tc, int row,
|
||||
string_attrs_t &value_out)
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
view_colors &vc = view_colors::singleton();
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
spectrogram_row &s_row = this->load_row(tc, row);
|
||||
|
||||
for (int lpc = 0; lpc <= (int) s_row.sr_width; lpc++) {
|
||||
int col_value = s_row.sr_values[lpc].rb_counter;
|
||||
|
||||
if (col_value == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int color;
|
||||
|
||||
if (col_value < st.st_green_threshold) {
|
||||
color = COLOR_GREEN;
|
||||
}
|
||||
else if (col_value < st.st_yellow_threshold) {
|
||||
color = COLOR_YELLOW;
|
||||
}
|
||||
else {
|
||||
color = COLOR_RED;
|
||||
}
|
||||
value_out.emplace_back(
|
||||
line_range(lpc, lpc + 1),
|
||||
&view_curses::VC_STYLE,
|
||||
vc.ansi_color_pair(COLOR_BLACK, color)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void spectrogram_source::cache_bounds()
|
||||
{
|
||||
if (this->ss_value_source == nullptr) {
|
||||
this->ss_cached_bounds.sb_count = 0;
|
||||
this->ss_cached_bounds.sb_begin_time = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
spectrogram_bounds sb;
|
||||
|
||||
this->ss_value_source->spectro_bounds(sb);
|
||||
|
||||
if (sb.sb_count == this->ss_cached_bounds.sb_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->ss_cached_bounds = sb;
|
||||
|
||||
if (sb.sb_count == 0) {
|
||||
this->ss_cached_line_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
time_t grain_begin_time = rounddown(sb.sb_begin_time, this->ss_granularity);
|
||||
time_t grain_end_time = roundup_size(sb.sb_end_time, this->ss_granularity);
|
||||
|
||||
time_t diff = std::max((time_t) 1, grain_end_time - grain_begin_time);
|
||||
this->ss_cached_line_count =
|
||||
(diff + this->ss_granularity - 1) / this->ss_granularity;
|
||||
|
||||
int64_t samples_per_row = sb.sb_count / this->ss_cached_line_count;
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
|
||||
st.st_yellow_threshold = samples_per_row / 2;
|
||||
st.st_green_threshold = st.st_yellow_threshold / 2;
|
||||
|
||||
if (st.st_green_threshold <= 1) {
|
||||
st.st_green_threshold = 2;
|
||||
}
|
||||
if (st.st_yellow_threshold <= st.st_green_threshold) {
|
||||
st.st_yellow_threshold = st.st_green_threshold + 1;
|
||||
}
|
||||
}
|
||||
|
||||
spectrogram_row &spectrogram_source::load_row(textview_curses &tc, int row)
|
||||
{
|
||||
this->cache_bounds();
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
tc.get_dimensions(height, width);
|
||||
width -= 2;
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
spectrogram_request sr(sb);
|
||||
time_t row_time;
|
||||
|
||||
sr.sr_width = width;
|
||||
row_time = rounddown(sb.sb_begin_time, this->ss_granularity) +
|
||||
row * this->ss_granularity;
|
||||
sr.sr_begin_time = row_time;
|
||||
sr.sr_end_time = row_time + this->ss_granularity;
|
||||
|
||||
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out) /
|
||||
(double) (width - 1);
|
||||
|
||||
spectrogram_row &s_row = this->ss_row_cache[row_time];
|
||||
|
||||
if (s_row.sr_values == nullptr ||
|
||||
s_row.sr_width != width ||
|
||||
s_row.sr_column_size != sr.sr_column_size) {
|
||||
s_row.sr_width = width;
|
||||
s_row.sr_column_size = sr.sr_column_size;
|
||||
delete[] s_row.sr_values;
|
||||
s_row.sr_values = new spectrogram_row::row_bucket[width + 1];
|
||||
this->ss_value_source->spectro_row(sr, s_row);
|
||||
}
|
||||
|
||||
return s_row;
|
||||
}
|
@ -41,44 +41,31 @@
|
||||
#include "textview_curses.hh"
|
||||
|
||||
struct spectrogram_bounds {
|
||||
spectrogram_bounds()
|
||||
: sb_begin_time(0),
|
||||
sb_end_time(0),
|
||||
sb_min_value_out(0.0),
|
||||
sb_max_value_out(0.0),
|
||||
sb_count(0) {
|
||||
|
||||
};
|
||||
|
||||
time_t sb_begin_time;
|
||||
time_t sb_end_time;
|
||||
double sb_min_value_out;
|
||||
double sb_max_value_out;
|
||||
int64_t sb_count;
|
||||
time_t sb_begin_time{0};
|
||||
time_t sb_end_time{0};
|
||||
double sb_min_value_out{0.0};
|
||||
double sb_max_value_out{0.0};
|
||||
int64_t sb_count{0};
|
||||
};
|
||||
|
||||
struct spectrogram_thresholds {
|
||||
int st_green_threshold;
|
||||
int st_yellow_threshold;
|
||||
int st_green_threshold{0};
|
||||
int st_yellow_threshold{0};
|
||||
};
|
||||
|
||||
struct spectrogram_request {
|
||||
spectrogram_request(spectrogram_bounds &sb)
|
||||
: sr_bounds(sb), sr_width(0), sr_column_size(0) {
|
||||
explicit spectrogram_request(spectrogram_bounds &sb)
|
||||
: sr_bounds(sb) {
|
||||
};
|
||||
|
||||
spectrogram_bounds &sr_bounds;
|
||||
unsigned long sr_width;
|
||||
time_t sr_begin_time;
|
||||
time_t sr_end_time;
|
||||
double sr_column_size;
|
||||
unsigned long sr_width{0};
|
||||
time_t sr_begin_time{0};
|
||||
time_t sr_end_time{0};
|
||||
double sr_column_size{0};
|
||||
};
|
||||
|
||||
struct spectrogram_row {
|
||||
spectrogram_row() : sr_values(NULL), sr_width(0) {
|
||||
|
||||
};
|
||||
|
||||
~spectrogram_row() {
|
||||
delete[] this->sr_values;
|
||||
}
|
||||
@ -90,9 +77,9 @@ struct spectrogram_row {
|
||||
int rb_marks;
|
||||
};
|
||||
|
||||
row_bucket *sr_values;
|
||||
unsigned long sr_width;
|
||||
double sr_column_size;
|
||||
row_bucket *sr_values{nullptr};
|
||||
unsigned long sr_width{0};
|
||||
double sr_column_size{0.0};
|
||||
|
||||
void add_value(spectrogram_request &sr, double value, bool marked) {
|
||||
long index = lrint((value - sr.sr_bounds.sb_min_value_out) / sr.sr_column_size);
|
||||
@ -106,7 +93,7 @@ struct spectrogram_row {
|
||||
|
||||
class spectrogram_value_source {
|
||||
public:
|
||||
virtual ~spectrogram_value_source() { };
|
||||
virtual ~spectrogram_value_source() = default;
|
||||
|
||||
virtual void spectro_bounds(spectrogram_bounds &sb_out) = 0;
|
||||
|
||||
@ -124,382 +111,52 @@ class spectrogram_source
|
||||
public list_overlay_source,
|
||||
public list_input_delegate {
|
||||
public:
|
||||
|
||||
spectrogram_source()
|
||||
: ss_granularity(60),
|
||||
ss_value_source(NULL),
|
||||
ss_cursor_column(-1) {
|
||||
|
||||
};
|
||||
|
||||
void invalidate() {
|
||||
this->ss_cached_bounds.sb_count = 0;
|
||||
this->ss_row_cache.clear();
|
||||
this->ss_cursor_column = -1;
|
||||
};
|
||||
|
||||
bool list_input_handle_key(listview_curses &lv, int ch) {
|
||||
switch (ch) {
|
||||
case 'm': {
|
||||
if (this->ss_cursor_top < 0 ||
|
||||
(size_t) this->ss_cursor_top >= this->text_line_count() ||
|
||||
this->ss_cursor_column == -1 ||
|
||||
this->ss_value_source == NULL) {
|
||||
alerter::singleton().chime();
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
struct timeval begin_time = this->time_for_row(this->ss_cursor_top);
|
||||
struct timeval end_time = begin_time;
|
||||
|
||||
end_time.tv_sec += this->ss_granularity;
|
||||
double range_min, range_max, column_size;
|
||||
|
||||
column_size = (sb.sb_max_value_out - sb.sb_min_value_out) /
|
||||
(double) (width - 1);
|
||||
range_min = sb.sb_min_value_out + this->ss_cursor_column * column_size;
|
||||
range_max = range_min + column_size + column_size * 0.01;
|
||||
this->ss_value_source->spectro_mark((textview_curses &) lv,
|
||||
begin_time.tv_sec, end_time.tv_sec,
|
||||
range_min, range_max);
|
||||
this->invalidate();
|
||||
lv.reload_data();
|
||||
return true;
|
||||
}
|
||||
case KEY_LEFT:
|
||||
case KEY_RIGHT: {
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
string_attrs_t sa;
|
||||
|
||||
this->ss_cursor_top = lv.get_top();
|
||||
lv.get_dimensions(height, width);
|
||||
|
||||
this->text_attrs_for_line((textview_curses &) lv, this->ss_cursor_top, sa);
|
||||
|
||||
if (sa.empty()) {
|
||||
this->ss_cursor_column = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
string_attrs_t::iterator current;
|
||||
|
||||
struct line_range lr(this->ss_cursor_column, this->ss_cursor_column + 1);
|
||||
|
||||
current = find_string_attr(sa, lr);
|
||||
|
||||
if (current != sa.end()) {
|
||||
if (ch == KEY_LEFT) {
|
||||
if (current == sa.begin()) {
|
||||
current = sa.end();
|
||||
}
|
||||
else {
|
||||
--current;
|
||||
}
|
||||
}
|
||||
else {
|
||||
++current;
|
||||
}
|
||||
}
|
||||
|
||||
if (current == sa.end()) {
|
||||
if (ch == KEY_LEFT) {
|
||||
current = sa.end();
|
||||
--current;
|
||||
}
|
||||
else {
|
||||
current = sa.begin();
|
||||
}
|
||||
}
|
||||
this->ss_cursor_column = current->sa_range.lr_start;
|
||||
|
||||
lv.reload_data();
|
||||
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
bool list_input_handle_key(listview_curses &lv, int ch) override;
|
||||
|
||||
bool list_value_for_overlay(const listview_curses &lv,
|
||||
int y, int bottom,
|
||||
vis_line_t row,
|
||||
attr_line_t &value_out) {
|
||||
if (y != 0) {
|
||||
return false;
|
||||
}
|
||||
attr_line_t &value_out) override;
|
||||
|
||||
std::string &line = value_out.get_string();
|
||||
char buf[128];
|
||||
vis_line_t height;
|
||||
unsigned long width;
|
||||
size_t text_line_count() override;
|
||||
|
||||
lv.get_dimensions(height, width);
|
||||
size_t text_line_width(textview_curses &tc) override;
|
||||
|
||||
this->cache_bounds();
|
||||
|
||||
if (this->ss_cached_line_count == 0) {
|
||||
value_out.with_ansi_string(
|
||||
ANSI_ROLE("error: no log data"),
|
||||
view_colors::VCR_ERROR);
|
||||
return true;
|
||||
}
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
|
||||
snprintf(buf, sizeof(buf), "Min: %'.10lg", sb.sb_min_value_out);
|
||||
line = buf;
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
ANSI_ROLE(" ") " 1-%'d "
|
||||
ANSI_ROLE(" ") " %'d-%'d "
|
||||
ANSI_ROLE(" ") " %'d+",
|
||||
view_colors::VCR_LOW_THRESHOLD,
|
||||
st.st_green_threshold - 1,
|
||||
view_colors::VCR_MED_THRESHOLD,
|
||||
st.st_green_threshold,
|
||||
st.st_yellow_threshold - 1,
|
||||
view_colors::VCR_HIGH_THRESHOLD,
|
||||
st.st_yellow_threshold);
|
||||
line.append(width / 2 - strlen(buf) / 3 - line.length(), ' ');
|
||||
line.append(buf);
|
||||
scrub_ansi_string(line, value_out.get_attrs());
|
||||
|
||||
snprintf(buf, sizeof(buf), "Max: %'.10lg", sb.sb_max_value_out);
|
||||
line.append(width - strlen(buf) - line.length() - 2, ' ');
|
||||
line.append(buf);
|
||||
|
||||
value_out.with_attr(string_attr(
|
||||
line_range(0, -1),
|
||||
&view_curses::VC_STYLE,
|
||||
A_UNDERLINE));
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
size_t text_line_count() {
|
||||
if (this->ss_value_source == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
this->cache_bounds();
|
||||
|
||||
return this->ss_cached_line_count;
|
||||
};
|
||||
|
||||
size_t text_line_width(textview_curses &tc) {
|
||||
if (tc.get_window() == NULL) {
|
||||
return 80;
|
||||
}
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
tc.get_dimensions(height, width);
|
||||
return width;
|
||||
};
|
||||
|
||||
size_t text_size_for_line(textview_curses &tc, int row, line_flags_t flags) {
|
||||
size_t text_size_for_line(textview_curses &tc, int row, line_flags_t flags) override {
|
||||
return 0;
|
||||
};
|
||||
|
||||
struct timeval time_for_row(int row) {
|
||||
struct timeval retval { 0, 0 };
|
||||
struct timeval time_for_row(int row) override;
|
||||
|
||||
this->cache_bounds();
|
||||
retval.tv_sec =
|
||||
rounddown(this->ss_cached_bounds.sb_begin_time, this->ss_granularity) +
|
||||
row * this->ss_granularity;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
int row_for_time(struct timeval time_bucket) {
|
||||
if (this->ss_value_source == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
time_t diff;
|
||||
int retval;
|
||||
|
||||
this->cache_bounds();
|
||||
if (time_bucket.tv_sec < this->ss_cached_bounds.sb_begin_time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff = time_bucket.tv_sec - this->ss_cached_bounds.sb_begin_time;
|
||||
retval = diff / this->ss_granularity;
|
||||
|
||||
return retval;
|
||||
}
|
||||
int row_for_time(struct timeval time_bucket) override;
|
||||
|
||||
void text_value_for_line(textview_curses &tc,
|
||||
int row,
|
||||
std::string &value_out,
|
||||
line_flags_t flags) {
|
||||
spectrogram_row &s_row = this->load_row(tc, row);
|
||||
|
||||
struct timeval row_time;
|
||||
char tm_buffer[128];
|
||||
struct tm tm;
|
||||
|
||||
row_time = this->time_for_row(row);
|
||||
|
||||
gmtime_r(&row_time.tv_sec, &tm);
|
||||
strftime(tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S", &tm);
|
||||
|
||||
value_out = tm_buffer;
|
||||
value_out.resize(s_row.sr_width, ' ');
|
||||
|
||||
for (size_t lpc = 0; lpc <= s_row.sr_width; lpc++) {
|
||||
if (s_row.sr_values[lpc].rb_marks) {
|
||||
value_out[lpc] = 'x';
|
||||
}
|
||||
}
|
||||
|
||||
if (this->ss_cursor_top == row && this->ss_cursor_column != -1) {
|
||||
if (value_out[this->ss_cursor_column] == 'x') {
|
||||
value_out[this->ss_cursor_column] = '*';
|
||||
}
|
||||
else {
|
||||
value_out[this->ss_cursor_column] = '+';
|
||||
}
|
||||
}
|
||||
};
|
||||
line_flags_t flags) override;
|
||||
|
||||
void text_attrs_for_line(textview_curses &tc,
|
||||
int row,
|
||||
string_attrs_t &value_out) {
|
||||
if (this->ss_value_source == NULL) {
|
||||
return;
|
||||
}
|
||||
string_attrs_t &value_out) override;
|
||||
|
||||
view_colors &vc = view_colors::singleton();
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
spectrogram_row &s_row = this->load_row(tc, row);
|
||||
void cache_bounds();
|
||||
|
||||
for (int lpc = 0; lpc <= (int) s_row.sr_width; lpc++) {
|
||||
int col_value = s_row.sr_values[lpc].rb_counter;
|
||||
spectrogram_row &load_row(textview_curses &tc, int row);
|
||||
|
||||
if (col_value == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int color;
|
||||
|
||||
if (col_value < st.st_green_threshold) {
|
||||
color = COLOR_GREEN;
|
||||
}
|
||||
else if (col_value < st.st_yellow_threshold) {
|
||||
color = COLOR_YELLOW;
|
||||
}
|
||||
else {
|
||||
color = COLOR_RED;
|
||||
}
|
||||
value_out.emplace_back(
|
||||
line_range(lpc, lpc + 1),
|
||||
&view_curses::VC_STYLE,
|
||||
vc.ansi_color_pair(COLOR_BLACK, color)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
void cache_bounds() {
|
||||
if (this->ss_value_source == NULL) {
|
||||
this->ss_cached_bounds.sb_count = 0;
|
||||
this->ss_cached_bounds.sb_begin_time = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
spectrogram_bounds sb;
|
||||
|
||||
this->ss_value_source->spectro_bounds(sb);
|
||||
|
||||
if (sb.sb_count == this->ss_cached_bounds.sb_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->ss_cached_bounds = sb;
|
||||
|
||||
if (sb.sb_count == 0) {
|
||||
this->ss_cached_line_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
time_t grain_begin_time = rounddown(sb.sb_begin_time, this->ss_granularity);
|
||||
time_t grain_end_time = roundup_size(sb.sb_end_time, this->ss_granularity);
|
||||
|
||||
time_t diff = std::max((time_t) 1, grain_end_time - grain_begin_time);
|
||||
this->ss_cached_line_count =
|
||||
(diff + this->ss_granularity - 1) / this->ss_granularity;
|
||||
|
||||
int64_t samples_per_row = sb.sb_count / this->ss_cached_line_count;
|
||||
spectrogram_thresholds &st = this->ss_cached_thresholds;
|
||||
|
||||
st.st_yellow_threshold = samples_per_row / 2;
|
||||
st.st_green_threshold = st.st_yellow_threshold / 2;
|
||||
|
||||
if (st.st_green_threshold <= 1) {
|
||||
st.st_green_threshold = 2;
|
||||
}
|
||||
if (st.st_yellow_threshold <= st.st_green_threshold) {
|
||||
st.st_yellow_threshold = st.st_green_threshold + 1;
|
||||
}
|
||||
};
|
||||
|
||||
spectrogram_row &load_row(textview_curses &tc, int row) {
|
||||
this->cache_bounds();
|
||||
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
||||
tc.get_dimensions(height, width);
|
||||
width -= 2;
|
||||
|
||||
spectrogram_bounds &sb = this->ss_cached_bounds;
|
||||
spectrogram_request sr(sb);
|
||||
time_t row_time;
|
||||
|
||||
sr.sr_width = width;
|
||||
row_time = rounddown(sb.sb_begin_time, this->ss_granularity) +
|
||||
row * this->ss_granularity;
|
||||
sr.sr_begin_time = row_time;
|
||||
sr.sr_end_time = row_time + this->ss_granularity;
|
||||
|
||||
sr.sr_column_size = (sb.sb_max_value_out - sb.sb_min_value_out) /
|
||||
(double) (width - 1);
|
||||
|
||||
spectrogram_row &s_row = this->ss_row_cache[row_time];
|
||||
|
||||
if (s_row.sr_values == nullptr ||
|
||||
s_row.sr_width != width ||
|
||||
s_row.sr_column_size != sr.sr_column_size) {
|
||||
s_row.sr_width = width;
|
||||
s_row.sr_column_size = sr.sr_column_size;
|
||||
delete[] s_row.sr_values;
|
||||
s_row.sr_values = new spectrogram_row::row_bucket[width + 1];
|
||||
this->ss_value_source->spectro_row(sr, s_row);
|
||||
}
|
||||
|
||||
return s_row;
|
||||
}
|
||||
|
||||
int ss_granularity;
|
||||
spectrogram_value_source *ss_value_source;
|
||||
int ss_granularity{60};
|
||||
spectrogram_value_source *ss_value_source{nullptr};
|
||||
spectrogram_bounds ss_cached_bounds;
|
||||
spectrogram_thresholds ss_cached_thresholds;
|
||||
size_t ss_cached_line_count;
|
||||
size_t ss_cached_line_count{0};
|
||||
std::map<time_t, spectrogram_row> ss_row_cache;
|
||||
vis_line_t ss_cursor_top;
|
||||
int ss_cursor_column;
|
||||
int ss_cursor_column{-1};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -42,8 +42,8 @@
|
||||
#include "sql_util.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/time_util.hh"
|
||||
#include "pcrepp/pcrepp.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "sqlite-extension-func.hh"
|
||||
|
||||
using namespace std;
|
||||
|
@ -34,7 +34,6 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include "sqlite3.h"
|
||||
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "elem_to_json.hh"
|
||||
#include "vtab_module.hh"
|
||||
#include "vtab_module_json.hh"
|
||||
#include "spookyhash/SpookyV2.h"
|
||||
|
||||
#include "optional.hpp"
|
||||
#include "mapbox/variant.hpp"
|
||||
|
38
src/string_attr_type.cc
Normal file
38
src/string_attr_type.cc
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "string_attr_type.hh"
|
||||
|
||||
string_attr_type SA_ORIGINAL_LINE("original_line");
|
||||
string_attr_type SA_BODY("body");
|
||||
string_attr_type SA_HIDDEN("hidden");
|
||||
string_attr_type SA_FORMAT("format");
|
||||
string_attr_type SA_REMOVED("removed");
|
49
src/string_attr_type.hh
Normal file
49
src/string_attr_type.hh
Normal file
@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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
|
||||
|
||||
class string_attr_type {
|
||||
public:
|
||||
explicit string_attr_type(const char *name = nullptr) noexcept
|
||||
: sat_name(name) {
|
||||
};
|
||||
|
||||
const char *sat_name;
|
||||
};
|
||||
typedef string_attr_type *string_attr_type_t;
|
||||
|
||||
extern string_attr_type SA_ORIGINAL_LINE;
|
||||
extern string_attr_type SA_BODY;
|
||||
extern string_attr_type SA_HIDDEN;
|
||||
extern string_attr_type SA_FORMAT;
|
||||
extern string_attr_type SA_REMOVED;
|
||||
|
||||
#endif
|
@ -55,7 +55,7 @@ static pcre *xpcre_compile(const char *pattern, int options = 0)
|
||||
return retval;
|
||||
}
|
||||
|
||||
void setup_highlights(textview_curses::highlight_map_t &hm)
|
||||
void setup_highlights(highlight_map_t &hm)
|
||||
{
|
||||
hm[{highlight_source_t::INTERNAL, "python"}] = highlighter(xpcre_compile(
|
||||
"(?:"
|
||||
|
@ -30,8 +30,8 @@
|
||||
#ifndef textfile_highlighters_hh
|
||||
#define textfile_highlighters_hh
|
||||
|
||||
#include "textview_curses.hh"
|
||||
#include "textview_curses_fwd.hh"
|
||||
|
||||
void setup_highlights(textview_curses::highlight_map_t &hm);
|
||||
void setup_highlights(highlight_map_t &hm);
|
||||
|
||||
#endif
|
||||
|
@ -114,13 +114,8 @@ bookmark_type_t textview_curses::BM_USER("user");
|
||||
bookmark_type_t textview_curses::BM_SEARCH("search");
|
||||
bookmark_type_t textview_curses::BM_META("meta");
|
||||
|
||||
string_attr_type textview_curses::SA_ORIGINAL_LINE("original_line");
|
||||
string_attr_type textview_curses::SA_BODY("body");
|
||||
string_attr_type textview_curses::SA_HIDDEN("hidden");
|
||||
string_attr_type textview_curses::SA_FORMAT("format");
|
||||
string_attr_type textview_curses::SA_REMOVED("removed");
|
||||
|
||||
textview_curses::textview_curses()
|
||||
: tc_search_action(noop_func{})
|
||||
{
|
||||
this->set_data_source(this);
|
||||
}
|
||||
@ -556,7 +551,7 @@ void textview_curses::execute_search(const std::string ®ex_orig)
|
||||
|
||||
hl.with_role(view_colors::VCR_SEARCH);
|
||||
|
||||
textview_curses::highlight_map_t &hm = this->get_highlights();
|
||||
highlight_map_t &hm = this->get_highlights();
|
||||
hm[{highlight_source_t::PREVIEW, "search"}] = hl;
|
||||
|
||||
unique_ptr<grep_proc<vis_line_t>> gp = make_unique<grep_proc<vis_line_t>>(code, *this);
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "text_format.hh"
|
||||
#include "logfile.hh"
|
||||
#include "highlighter.hh"
|
||||
#include "textview_curses_fwd.hh"
|
||||
|
||||
class logline;
|
||||
class textview_curses;
|
||||
@ -558,7 +559,7 @@ private:
|
||||
|
||||
class text_delegate {
|
||||
public:
|
||||
virtual ~text_delegate() { };
|
||||
virtual ~text_delegate() = default;
|
||||
|
||||
virtual void text_overlay(textview_curses &tc) { };
|
||||
|
||||
@ -567,14 +568,6 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
enum class highlight_source_t {
|
||||
INTERNAL,
|
||||
THEME,
|
||||
PREVIEW,
|
||||
CONFIGURATION,
|
||||
INTERACTIVE,
|
||||
};
|
||||
|
||||
/**
|
||||
* The textview_curses class adds user bookmarks and searching to the standard
|
||||
* list view interface.
|
||||
@ -593,12 +586,6 @@ public:
|
||||
static bookmark_type_t BM_SEARCH;
|
||||
static bookmark_type_t BM_META;
|
||||
|
||||
static string_attr_type SA_ORIGINAL_LINE;
|
||||
static string_attr_type SA_BODY;
|
||||
static string_attr_type SA_HIDDEN;
|
||||
static string_attr_type SA_FORMAT;
|
||||
static string_attr_type SA_REMOVED;
|
||||
|
||||
textview_curses();
|
||||
|
||||
void reload_config(error_reporter &reporter);
|
||||
@ -837,9 +824,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
using highlight_map_t =
|
||||
std::map<std::pair<highlight_source_t, std::string>, highlighter>;
|
||||
|
||||
highlight_map_t &get_highlights() { return this->tc_highlights; };
|
||||
|
||||
const highlight_map_t &get_highlights() const { return this->tc_highlights; };
|
||||
@ -944,7 +928,7 @@ protected:
|
||||
grep_highlighter(std::unique_ptr<grep_proc<vis_line_t>> &gp,
|
||||
highlight_source_t source,
|
||||
std::string hl_name,
|
||||
textview_curses::highlight_map_t &hl_map)
|
||||
highlight_map_t &hl_map)
|
||||
: gh_grep_proc(std::move(gp)),
|
||||
gh_hl_source(source),
|
||||
gh_hl_name(std::move(hl_name)),
|
||||
@ -962,7 +946,7 @@ protected:
|
||||
std::unique_ptr<grep_proc<vis_line_t>> gh_grep_proc;
|
||||
highlight_source_t gh_hl_source;
|
||||
std::string gh_hl_name;
|
||||
textview_curses::highlight_map_t &gh_hl_map;
|
||||
highlight_map_t &gh_hl_map;
|
||||
};
|
||||
|
||||
text_sub_source *tc_sub_source{nullptr};
|
||||
@ -974,7 +958,7 @@ protected:
|
||||
struct timeval tc_follow_deadline{0, 0};
|
||||
vis_line_t tc_follow_top{-1_vl};
|
||||
std::function<bool()> tc_follow_func;
|
||||
action tc_search_action{noop_func{}};
|
||||
action tc_search_action;
|
||||
|
||||
highlight_map_t tc_highlights;
|
||||
|
||||
|
48
src/textview_curses_fwd.hh
Normal file
48
src/textview_curses_fwd.hh
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* 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_textview_curses_fwd_hh
|
||||
#define lnav_textview_curses_fwd_hh
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "highlighter.hh"
|
||||
|
||||
enum class highlight_source_t {
|
||||
INTERNAL,
|
||||
THEME,
|
||||
PREVIEW,
|
||||
CONFIGURATION,
|
||||
INTERACTIVE,
|
||||
};
|
||||
|
||||
using highlight_map_t = std::map<std::pair<highlight_source_t, std::string>, highlighter>;
|
||||
|
||||
#endif
|
@ -37,7 +37,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/date_time_scanner.hh"
|
||||
#include "sql_util.hh"
|
||||
#include "relative_time.hh"
|
||||
|
||||
|
127
src/unique_path.cc
Normal file
127
src/unique_path.cc
Normal file
@ -0,0 +1,127 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "unique_path.hh"
|
||||
|
||||
void unique_path_generator::add_source(
|
||||
const std::shared_ptr<unique_path_source> &path_source)
|
||||
{
|
||||
ghc::filesystem::path path = path_source->get_path();
|
||||
|
||||
path_source->set_unique_path(path.filename());
|
||||
path_source->set_path_prefix(path.parent_path());
|
||||
this->upg_unique_paths[path.filename()].push_back(path_source);
|
||||
}
|
||||
|
||||
void unique_path_generator::generate()
|
||||
{
|
||||
int loop_count = 0;
|
||||
|
||||
while (!this->upg_unique_paths.empty()) {
|
||||
std::vector<std::shared_ptr<unique_path_source>> collisions;
|
||||
|
||||
for (auto pair : this->upg_unique_paths) {
|
||||
if (pair.second.size() == 1) {
|
||||
if (loop_count > 0) {
|
||||
std::shared_ptr<unique_path_source> src = pair.second[0];
|
||||
|
||||
src->set_unique_path("[" + src->get_unique_path());
|
||||
}
|
||||
|
||||
this->upg_max_len = std::max(
|
||||
this->upg_max_len,
|
||||
pair.second[0]->get_unique_path().size());
|
||||
} else {
|
||||
bool all_common = true;
|
||||
|
||||
do {
|
||||
std::string common;
|
||||
|
||||
for (auto &src : pair.second) {
|
||||
auto &path = src->get_path_prefix();
|
||||
|
||||
if (common.empty()) {
|
||||
common = path.filename();
|
||||
if (common.empty()) {
|
||||
all_common = false;
|
||||
}
|
||||
} else if (common != path.filename()) {
|
||||
all_common = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_common) {
|
||||
for (auto &src : pair.second) {
|
||||
auto &path = src->get_path_prefix();
|
||||
auto par = path.parent_path();
|
||||
|
||||
if (path.empty() || path == par) {
|
||||
all_common = false;
|
||||
} else {
|
||||
src->set_path_prefix(path.parent_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (all_common);
|
||||
|
||||
collisions.insert(collisions.end(),
|
||||
pair.second.begin(),
|
||||
pair.second.end());
|
||||
}
|
||||
}
|
||||
|
||||
this->upg_unique_paths.clear();
|
||||
|
||||
for (auto &src : collisions) {
|
||||
const auto unique_path = src->get_unique_path();
|
||||
auto &prefix = src->get_path_prefix();
|
||||
|
||||
if (loop_count == 0) {
|
||||
src->set_unique_path(prefix.filename().string() + "]/" + unique_path);
|
||||
} else {
|
||||
src->set_unique_path(prefix.filename().string() + "/" + unique_path);
|
||||
}
|
||||
|
||||
ghc::filesystem::path parent = prefix.parent_path();
|
||||
|
||||
src->set_path_prefix(parent);
|
||||
|
||||
if (parent.empty() || parent == prefix) {
|
||||
src->set_unique_path("[" + src->get_unique_path());
|
||||
} else {
|
||||
this->upg_unique_paths[src->get_unique_path()].push_back(
|
||||
src);
|
||||
}
|
||||
}
|
||||
|
||||
loop_count += 1;
|
||||
}
|
||||
}
|
@ -67,104 +67,12 @@ private:
|
||||
|
||||
class unique_path_generator {
|
||||
public:
|
||||
unique_path_generator() : upg_max_len(0) {
|
||||
void add_source(const std::shared_ptr<unique_path_source>& path_source);
|
||||
|
||||
};
|
||||
|
||||
void add_source(const std::shared_ptr<unique_path_source>& path_source) {
|
||||
ghc::filesystem::path path = path_source->get_path();
|
||||
|
||||
path_source->set_unique_path(path.filename());
|
||||
path_source->set_path_prefix(path.parent_path());
|
||||
this->upg_unique_paths[path.filename()].push_back(path_source);
|
||||
};
|
||||
|
||||
void generate() {
|
||||
int loop_count = 0;
|
||||
|
||||
while (!this->upg_unique_paths.empty()) {
|
||||
std::vector<std::shared_ptr<unique_path_source>> collisions;
|
||||
|
||||
for (auto pair : this->upg_unique_paths) {
|
||||
if (pair.second.size() == 1) {
|
||||
if (loop_count > 0) {
|
||||
std::shared_ptr<unique_path_source> src = pair.second[0];
|
||||
|
||||
src->set_unique_path("[" + src->get_unique_path());
|
||||
}
|
||||
|
||||
this->upg_max_len = std::max(
|
||||
this->upg_max_len,
|
||||
pair.second[0]->get_unique_path().size());
|
||||
} else {
|
||||
bool all_common = true;
|
||||
|
||||
do {
|
||||
std::string common;
|
||||
|
||||
for (auto &src : pair.second) {
|
||||
auto &path = src->get_path_prefix();
|
||||
|
||||
if (common.empty()) {
|
||||
common = path.filename();
|
||||
if (common.empty()) {
|
||||
all_common = false;
|
||||
}
|
||||
} else if (common != path.filename()) {
|
||||
all_common = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_common) {
|
||||
for (auto &src : pair.second) {
|
||||
auto &path = src->get_path_prefix();
|
||||
auto par = path.parent_path();
|
||||
|
||||
if (path.empty() || path == par) {
|
||||
all_common = false;
|
||||
} else {
|
||||
src->set_path_prefix(path.parent_path());
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (all_common);
|
||||
|
||||
collisions.insert(collisions.end(),
|
||||
pair.second.begin(),
|
||||
pair.second.end());
|
||||
}
|
||||
}
|
||||
|
||||
this->upg_unique_paths.clear();
|
||||
|
||||
for (auto &src : collisions) {
|
||||
const auto unique_path = src->get_unique_path();
|
||||
auto &prefix = src->get_path_prefix();
|
||||
|
||||
if (loop_count == 0) {
|
||||
src->set_unique_path(prefix.filename().string() + "]/" + unique_path);
|
||||
} else {
|
||||
src->set_unique_path(prefix.filename().string() + "/" + unique_path);
|
||||
}
|
||||
|
||||
ghc::filesystem::path parent = prefix.parent_path();
|
||||
|
||||
src->set_path_prefix(parent);
|
||||
|
||||
if (parent.empty() || parent == prefix) {
|
||||
src->set_unique_path("[" + src->get_unique_path());
|
||||
} else {
|
||||
this->upg_unique_paths[src->get_unique_path()].push_back(
|
||||
src);
|
||||
}
|
||||
}
|
||||
|
||||
loop_count += 1;
|
||||
}
|
||||
}
|
||||
void generate();
|
||||
|
||||
std::map<std::string, std::vector<std::shared_ptr<unique_path_source>>> upg_unique_paths;
|
||||
size_t upg_max_len;
|
||||
size_t upg_max_len{0};
|
||||
};
|
||||
|
||||
#endif //LNAV_UNIQUE_PATH_HH
|
||||
|
@ -103,7 +103,7 @@ static void open_pretty_view()
|
||||
lss.text_attrs_for_line(*log_tc, vl, al.get_attrs());
|
||||
|
||||
line_range orig_lr = find_string_attr_range(
|
||||
al.get_attrs(), &textview_curses::SA_ORIGINAL_LINE);
|
||||
al.get_attrs(), &SA_ORIGINAL_LINE);
|
||||
attr_line_t orig_al = al.subline(orig_lr.lr_start, orig_lr.length());
|
||||
attr_line_t prefix_al = al.subline(0, orig_lr.lr_start);
|
||||
|
||||
|
@ -33,7 +33,9 @@
|
||||
#define lnav_view_helpers_hh
|
||||
|
||||
#include "help_text.hh"
|
||||
#include "textview_curses.hh"
|
||||
#include "attr_line.hh"
|
||||
|
||||
class textview_curses;
|
||||
|
||||
/** The different views available. */
|
||||
typedef enum {
|
||||
|
@ -39,7 +39,6 @@
|
||||
#include "optional.hpp"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "auto_mem.hh"
|
||||
#include "mapbox/variant.hpp"
|
||||
#include "fmt/format.h"
|
||||
|
@ -170,7 +170,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (format.get() != NULL) {
|
||||
format->annotate(0, sbr, sa, ll_values);
|
||||
body = find_string_attr_range(sa, &textview_curses::SA_BODY);
|
||||
body = find_string_attr_range(sa, &SA_BODY);
|
||||
}
|
||||
|
||||
data_parser::TRACE_FILE = fopen("scanned.dpt", "w");
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/string_util.hh"
|
||||
|
||||
static struct test_data {
|
||||
const char *str;
|
||||
@ -43,7 +43,7 @@ static struct test_data {
|
||||
{ "com.example.foo.bar", "c.e.foo.bar", 15 },
|
||||
{ "no dots in here", "no dots in here", 5 },
|
||||
|
||||
{ NULL }
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include <assert.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include "lnav_util.hh"
|
||||
#include "base/date_time_scanner.hh"
|
||||
#include "../src/lnav_util.hh"
|
||||
|
||||
static const char *GOOD_TIMES[] = {
|
||||
|
Loading…
Reference in New Issue
Block a user