[build] some more compiler profiling

Defect Number:
    Reviewed By:
   Testing Done:
This commit is contained in:
Timothy Stack 2020-11-19 21:36:51 -08:00
parent 6fe2f552d5
commit 8e629b166a
75 changed files with 2303 additions and 1751 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View 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;
}

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

View File

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

View File

@ -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
View 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);
}

View File

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

View File

@ -34,7 +34,7 @@
#include <sys/mman.h>
#include "lnav_util.hh"
#include "base/math_util.hh"
template<typename T>
struct big_array {

View File

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

View File

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

View File

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

View File

@ -29,6 +29,7 @@
#include "config.h"
#include "spookyhash/SpookyV2.h"
#include "data_parser.hh"
using namespace std;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View 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> &params) noexcept
{
this->ht_parameters = params;
for (auto &param : 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));
}
}

View File

@ -129,35 +129,15 @@ struct help_text {
return *this;
}
help_text &with_parameters(const std::initializer_list<help_text> &params) noexcept {
this->ht_parameters = params;
for (auto &param : this->ht_parameters) {
param.ht_context = help_context_t::HC_PARAMETER;
}
return *this;
}
help_text &with_parameters(const std::initializer_list<help_text> &params) 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;
};

View File

@ -29,7 +29,7 @@
#include "config.h"
#include "lnav_util.hh"
#include "base/math_util.hh"
#include "hist_source.hh"
using namespace std;

View File

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

View File

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

View File

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

View File

@ -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. */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,7 +34,6 @@
#include <stdint.h>
#include <string>
#include <exception>
#include "sqlite3.h"

View File

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

View File

@ -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(
"(?:"

View File

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

View File

@ -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 &regex_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);

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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[])

View File

@ -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[] = {